I have a WCF client proxy that I use to call an async method like so:
var prox = new DoSomethingServiceClient("BasicHttpBinding_IDoSomethingService");
prox.DoSomethingCompleted += new EventHandler<AsyncCompletedEventArgs>(svcref_DoSomethingCompleted);
prox.DoSomethingAsync(0, Guid.NewGuid());
When do I close/abort the proxy?
Do I do it in the first line in svcref_DoSomethingCompleted
using the sender object?
I see here in MS documentation that they close the proxy within the main thread, not in the callback. Is that good practice? Seems wrong to me:
http://msdn.microsoft.com/en-us/library/ms730059(v=vs.110).aspx
I also found this, using task-based async which suggests that you close the proxy in the delegate callback, which is what I'd kind of expect:
http://fun-with-blackhawk.blogspot.com/2010/03/wcf-exception-handling-in-asynchronous.html
This is where WCF gets tricky. When the server invokes a callback method, it needs to complete that call before the client proxy is closed. This means a full round trip to the client and back. The Microsoft example requests input from the user on the client side (via Console.ReadLine()) to trigger the close. If you want to use an async callback to trigger a close, you'll need to get creative. For example, you could queue the close on a new thread, and then put a delay on the execution. This would allow the callback to complete its trip back to the server and then subsequently close the channel.
Add something like this in your callback:
var currentChannel = OperationContext.Current.Channel;
ThreadPool.QueueUserWorkItem(o => CloseChannel(currentChannel));
and then this for CloseChannel:
private static void CloseChannel<T>(T channel)
{
var clientChannel = (IClientChannel)channel;
var success = false;
try
{
//sleep before close so the main thread has a chance to catch up
Thread.Sleep(10000);
clientChannel.Close();
success = true;
}
catch (CommunicationException ce)
{
clientChannel.Abort();
}
catch (TimeoutException te)
{
clientChannel.Abort();
}
finally
{
if (!success)
clientChannel.Abort();
}
}