So here's the situation. You're wrapping a webservice in library used for some clients. You want to call the web service methods asynchronously using MethodNameAsync.
When you plug out the network cable to test for offline scenarios, ta-da, you get this cryptic error called "TargetInvocationException".
This code gives me TargetInvocationException error:
void _ws_loginCompleted(object sender, ws.loginCompletedEventArgs e)
{
if (IsWsResponseOk(e.Result))
{
// Save session key used for upcoming Web Service Requests
_sessionKey = e.Result.message;
}
IResponse response = ConvertWsResponse(e.Result);
LoginCompleted(this, response);
}
Since I'm a productive programmer, at first I asked uncled Google about this error. He showed me this post in MSDN forum that leads to this blog about using BackgroundWorker to do synchronous web calls instead.
Dude, that's not an elegant solution, I say.
So, I read up on MSDN about this creepy TargetInvocationException, and found out that if there's an error in the asynchronous call, e.Result is basically not valid.
So here's my (elegant) solution to the above TargetInvocationException error, I've put in BOLD the methods that you need to place to handle the error:
void _ws_loginCompleted(object sender, ws.loginCompletedEventArgs e)
{
ws.ws_response wsResponse = IsAsyncCallOk(e) ? e.Result : BuildWsErrorMsg(e);
if (IsWsResponseOk(wsResponse))
{
// Save session key used for upcoming Web Service Requests
_sessionKey = wsResponse.message;
}
IResponse response = ConvertWsResponse(wsResponse);
LoginCompleted(this, response);
}
private bool IsAsyncCallOk(System.ComponentModel.AsyncCompletedEventArgs args)
{
return (args.Error != null) ? false : true;
}
private ws.ws_response BuildWsErrorMsg(System.ComponentModel.AsyncCompletedEventArgs e)
{
ws.ws_response wsResponse = new ws.ws_response();
wsResponse.status_code = -1;
wsResponse.message = GetInnerExceptionError(e);
return wsResponse;
}
private static string GetInnerExceptionError(System.ComponentModel.AsyncCompletedEventArgs e)
{
if (e.Error.Message != null)
{
return e.Error.Message;
}
// Find the InnerException that threw this error
Exception ex = e.Error.InnerException;
while (ex == null)
{
ex = ex.InnerException;
}
return ex.Message;
}
Now, my client code doesn't need any changes or catching some fancy Asynchronous Exceptions...
static void TestDmsLoginLogout()
{
IServer server = Config.IServerFactory("http://127.0.0.1");
ICredential credential = Config.ICredentialFactory("admin", "admin");
IDms dms = Config.IDmsFactory(server);
dms.LoginCompleted += new LoginCompletedEventHandler(dms_LoginCompleted);
dms.LoginAsync(credential);
System.Threading.Thread.Sleep(10000);
dms.LogoutCompleted += new LogoutCompletedEventHandler(dms_LogoutCompleted);
dms.LogoutAsync();
}
static void dms_LogoutCompleted(object sender, IResponse result)
{
Console.WriteLine("\nDMS Logout completed. Result={0}, ErrorMessage={1}",
result.Success.ToString(), result.ErrorMessage);
}
static void dms_LoginCompleted(object sender, IResponse result)
{
Console.WriteLine("\nDMS Login completed. Result={0}, ErrorMessage={1}",
result.Success.ToString(), result.ErrorMessage);
}
And the Console Output for the above test code is:
DMS Login completed. Result=False, ErrorMessage=No connection could be made beca
use the target machine actively refused it 127.0.0.1:80
DMS Logout completed. Result=False, ErrorMessage=No connection could be made bec
ause the target machine actively refused it 127.0.0.1:80
Hope that helps for those in familiar situations.