I’m currently updating a client application that makes use of a WCF web service from synchronous to asynchronous calls. The main server and the client are on the same local network, but it is too unreliable for the application to hang while it waits for a response.
The application makes use of 4 identical endpoints across 2 servers (so if an instance has crashed or a server is offline, there should still be something to call).
The client has a layer responsible for making calls to the web service. My initial synchronous design was for the the active endpoint to be called and if an exception was thrown we would then move to the next endpoint and recursively call the same method. This would be done until all endpoints are exhausted.
I’ve now made the modifications to make this async but there is one issue. The parameters are lost once we are in the callback. So when it it time to call the Begin method again recursively, the parameters are not accessible to be passed in again.
What would be the best way to pass parameters from the Begin method to the callback method? Are they stored anywhere in the client object? Can it be done through the event or should I store them at the class level?
public delegate void GetUserInfoCompletedEventHandler(UserInfo e);
public static event GetUserInfoCompletedEventHandler GetUserInfoCompleted;
public delegate void GetUserInfoFaultedEventHandler(string errorMessage);
public static event GetUserInfoFaultedEventHandler GetUserInfoFaulted;
public static void BeginGetUserInfo(string fobID)
{
MyClient client = new MyClient(availableEndpoints[activeEndpointIndex].Name);
client.GetUserInfoCompleted += new EventHandler<GetUserInfoCompletedEventArgs>(client_GetUserInfoCompleted);
client.GetUserInfoAsync(fobID);
}
static void client_GetUserInfoCompleted(object sender, GetUserInfoCompletedEventArgs e)
{
// Get the instance of the client
MyClient client = (MyClient)sender;
if (null == e.Error)
{
// Close the client instance if there was no error
try { client.Close(); }
catch { }
if ((null != GetUserInfoCompleted) && (null != e.Result))
{
// Report as successful and raise the event
ServiceActionSuccessful();
GetUserInfoCompleted(e.Result);
}
}
else
{
// Abort the client as there was an error
try { client.Abort(); }
catch { }
if (e.Error is FaultException<WebServiceError>)
{
FaultException<WebServiceError> fault = (FaultException<WebServiceError>)e.Error;
if (null != GetUserInfoFaulted)
{
// A fault occurred in the web service
GetUserInfoFaulted(fault.Detail.ErrorMessage);
}
}
else
{
// Assume this was problem in connection so test if there any more endpoints to attempt
bool isNextEndpointAvaialble = ServiceActionFailure();
if (isNextEndpointAvaialble)
{
// If there are more endpoints to try, call the method to run again
BeginGetUserInfo(); // Need parameters here
}
else
{
if (null != GetUserInfoFaulted)
{
// No more endpoints to try
GetUserInfoFaulted(Errors.GetUserFriendlyMessage(e.Error));
}
}
}
}
}
If
MyClientis a generated class, there should be a second function calledThe content of the
userStateargument is passed dirctly to theGetUserInfoCompletedEventArgs.UserStateproperty in the eventargs received byclient_GetUserInfoCompleted.So you could do something like this:
Another alternative is to use a lambda for handling the event: