BeginInvoke Bugs


A delegate is a special type that can be bound at execution time to a method invocation. Normally you’d think of method invocations as being synchronous, but delegates can be executed either synchronously in the obvious way or asynchronously by introducing an extra thread of execution. An asynchronous delegate invocation uses the standard BeginInvoke and EndInvoke pattern, with the option to provide a callback for when the work is complete. You would expect that the asynchronous delegate pattern would be an easy way to make asynchronous calls to a web service. You would be wrong.

[ServiceContract]
public interface IService
{
[OperationContract]
string Echo(string text);
}

public class Service : IService
{
public string Echo(string text)
{
Thread.Sleep(5000);
return text;
}
}

public delegate string EchoDelegate(string text);

public class BeginInvokeDelegate
{
static void Callback(IAsyncResult result)
{
Console.WriteLine("In callback.");
}

static void Main(string[] args)
{
string uri = "http://localhost:8000/";
ServiceHost service = new ServiceHost(typeof(Service));
service.AddServiceEndpoint(typeof(IService), new BasicHttpBinding(), uri);
service.Open();

ChannelFactory<IService> factory = new ChannelFactory<IService>(new BasicHttpBinding(), new EndpointAddress(uri));
IService proxy = factory.CreateChannel();

EchoDelegate d = new EchoDelegate(proxy.Echo);
IAsyncResult result = d.BeginInvoke("foo", new AsyncCallback(Callback), null);
Console.WriteLine("Returned.");
Console.WriteLine(d.EndInvoke(result));

factory.Close();
service.Close();
Console.ReadLine();
}
}

The code above is using a ChannelFactory against a synchronous version of the Echo interface. The client and service are decoupled so it would have been legal to generate a client proxy that expressed the Echo interface using an asynchronous representation regardless of how Echo is implemented on the server. Instead, the code attempts to make the service call asynchronous by wrapping it in a delegate and using BeginInvoke. If you run this code, then you’ll see that the call to BeginInvoke does not complete until after the Echo service call has returned. This defeats the purpose of using the asynchronous pattern. The problem is that BeginInvoke knows about and only works with specific types of proxy objects, which do not include the proxy objects generated by ChannelFactory. The right way to make an asynchronous service call is to generate a proxy that has an asynchronous representation of the service method.

Next time: Logging Binary Messages

Comments (3)

  1. Why does message security stop working when the transport isn’t HTTP? There are two ways that messages

  2. Peter says:

    And how would you create such a proxy? Let’s assume that we do not want to change a thing to the server interface. Then a proxy will also have to use BeginInvoke which blocks?

  3. You would generate an asynchronous proxy by running svcutil with the /async switch.  Having an asynchronous proxy is different than trying to BeginInvoke a synchronous proxy.

Skip to main content