Sunday, November 1, 2009

Hosting WCF Service in Windows forms application and Sendtimeout exception

I was entrusted with a development of a POC(proof of concept) for an occasionally connected service. The client was a smart client application and the service was written in WCF. The idea was to have a online service when the machine was connected to a network and to use a self hosted service when not connected to the network. The sync between online and offline data would be take care by ado.net sync services.

The idea was to host the service in the shell form. If you know or heard about CAB SCSF then you are sure to know about shell form. Right so i have my offline service implementation and the contracts were in an assembly to enable re use between offline and online services.

This is what i did to host my service

public Form1()
{
InitializeComponent();
ServiceHost host = new ServiceHost(typeof(Service1));
host.Open();

}
I hosted my service in the form's constructor. I called my service operation in the form_load event

ChannelFactory factory = new ChannelFactory(new WSHttpBinding(), new EndpointAddress("http://localhost:8731/Design_Time_Addresses/WindowsFormsHost/Service1/"));
IService1 proxy = factory.CreateChannel();
proxy.DoWork();

This is the exception i got

Photobucket

Seems quite wierd. After hosting it in the current winform i tried accesing it from another winform solution and i was able to access the service. Ok so, i was hosting the service in proc which failed. So my service is fine.

I was able to nail the problem after a long tiresome googlejob!! The more I looked the more it became painfully obvious that I had a threading deadlock issue. The UseSynchronizationContext from the ServiceBehavior attribute in WCF is used to determine which thread your service will execute on. Basically System.Threading.SynchronizationContext.Current is read and cached so that when a request comes to the service, the host can marshal the request onto the thread that the host was created on using the cached SynchronizationContext.

As i mentioned earlier the timeout exception was due to a deadlock. The reason for this is that the default value of the UseSynchronizationContext is true and so when you create the ServiceHost on the UI thread of the winform application, then the current synchronization context is a DispatcherSynchronizationContext which holds a reference to a System.Windows.Threading.Dispatcher object which then holds a reference to the current thread. The DispatcherSynchronizationContext will then be used when a request comes in to marshal requests onto the UI thread. But if you are calling the service from the UI thread then you have a deadlock when it tries to do this!!

Phew!!! There are two ways to fix this issue.

1.)First one is to declare [CallbackBehavior(UseSynchronizationContext = false)] on the service class. Set that to false on your service and the service will create it's own SynchronizationContext and your client will no longer block when hosting the service in process.

2.)Host the servie before form constructor is invoked.

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServiceHost host = new ServiceHost(typeof(Service1));
host.Open();

Application.Run(new Form1());
}

No comments:

Post a Comment