Ayende @ Rahien

Refunds available at head office

WCF Async without proxies

I don't like generated proxies for web services, they are generally ugly and not fun to work with. However, up until recently I believed that I had to deal with them if I wanted to use the async operations for web services. As it turn out, I was wrong.

We can actually define an WCF service interface like this:

[ServiceContract]
public interface IAsyncBus
{
	[OperationContract(AsyncPattern = true)]
	IAsyncResult BeginProcess(IMessage[] request, AsyncCallback callback, object asyncState);

	IMessage[] EndProcess(IAsyncResult result);
}

Now you can work with it using:

IAsyncBus bus = new ChannelFactory<IAsyncBus>().CreateChannel();
ar = bus.BeginProcess(...);
//do work
bus.EndProcess(ar);

The problem with that is that on the server side, I also have to do things in an async manner. This is sometimes appropriate, but it tends to be a major PITA for a lot of things.

As it turn out, we can solve the issue with aliasing. In the interface dll, we can define:

[ServiceContract]
public interface IBus
{
	[OperationContract]
	IMessage[] Process(IMessage[] request);
}

[ServiceContract(Name="IBus")]
public interface IAsyncBus
{
	[OperationContract(AsyncPattern = true)]
	IAsyncResult BeginProcess(IMessage[] request, AsyncCallback callback, object asyncState);
	IMessage[] EndProcess(IAsyncResult result);
}

Now, you can create an instance of IAsyncBus to communicate with IBus directory. On the server side, we implement IBus, and handle the message in a synchronous manner. Easy, simple, and doesn't require any proxies :-)

WCF, Federated Security and Custom Authentication Token, oh my!

So today I spiked some code to see how hard it was to get federated security to work using WCF. I have never had to do something like that before, but I am familiar with the concepts, at least.

Armed with the WCF federation sample, I set out to build my own.

I failed.

I cannot believe how complex this was. I stopped after I realized that my app.config file passed 200 lines, solely for WCF configuration, I got a lot of unhelpful exceptions from WCF and generally felt that I am wasting my time.

Then I turned to building a custom authentication token, that would have served my need well enough. I started to follow the steps in the sample, but by the fifth class that I had to implement, I had enough. I started counting the number of classes that are required to build a custom authentication token. 11(!) classes!

Sorry, at this point, I back away slowly. This is way too complex for what I need. I solved the problem using a message header, which was a sweet & simple solution to the problem.

I consider myself fairly adept at handling complexity, but oh my god, just trying to touch this piece was scary on many levels.

WCF & Microsoft CRM don't really like each other

Last week I spent about half a day trying to get WCF to reliabely work against Microsoft CRM web services.

I had plenty of trouble just getting the authentication to work, which I blame solely on my lack of experience in configuring WCF. The problem is that it is literally four lines of code in ASMX, and configuration + code in WCF, which you have to get just right. A big issue there was that authentication failure in the CRM services (ASMX, basically), will return an HTML page explaining the problem, and WCF was willing to give me just the first 1024 bytes of it.

Resorting to black magic hackery to get the real error message is nasty, I am not sure what to blame here, but I would say that WCF is to blame here for not showing the full error message even in the trace logs.

My current problem with this is that the response from the CRM looks something like this:

<RetrieveResult xmlns="http://schemas.microsoft.com/crm/2006/WebServices" xsi:type="account">

       <owningbusinessunit>{50C57583-5F04-DC11-AF65-000C29955492}</owningbusinessunit>

       <ownerid name="Oren Eini" dsc="0" type="systemuser">{48D20A98-5F04-DC11-AF65-000C29955492}</ownerid>

       <name>oren eini2</name>

       <accountid>{8A1BBC3C-C44B-DC11-B6EF-000C292DA987}</accountid>

</RetrieveResult>

But WCF will return to me an object with all the fields set to null. The problem is that the result message has a lot more properties, and therefor, tags in it. The CRM web service appernatly pick and choose what it will send, and doesn't bother to send empty tags for null proeprties. In the above message, the account entity has ~175 properties, so that is somewhat understandable. The problem is that there is no easy way to make WCF understand that.

After running svcutil over the wsdl, I got a C# file that was 2MB (!!) in size, so manual intervention is out, and there seems to be no easy way to do globally. Playing with the options of svcutil also shows that WCF really doesn't like that WSDL (in fact, it refused to generate DataContracts, instead relying on XML Serialization).

Again, I am not really a WCF expert, but from my experiments, that may be possible, but it is complex, non-DRY, and not worth the trouble. I am using ASMX services now, which just works.

Using NHibernate Session Per Request with WCF Windsor Integration

Okay, this is a quickie, but here is how you can do it, you need to register a UnitOfWorkEndPointBehavior implementation on the container, and make sure that your WCF services are used through the WindsorServiceHost. That is all. What is the UnitOfWorkEndPointBehavior, you ask, here it is:

public class UnitOfworkEndPointBehavior : IEndpointBehavior
{
	public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
	{
		foreach (DispatchOperation operation in endpointDispatcher.DispatchRuntime.Operations)
		{
			operation.CallContextInitializers.Add(new UnitOfWorkCallContextInitializer());
		}
	}
}

public class UnitOfWorkCallContextInitializer : ICallContextInitializer
{
	public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
	{
		ISession session = factory.OpenSession();
		OperationContext.Items["NHibernate_Session"] = session;
		return session;
	}

	public void AfterInvoke(object correlationState)
	{
		((IDisposable)correlationState).Dispose();
	}
}

public static class UnitOfWork
{
	public static ISession Current { get { return (ISession )OperationContext.Items["NHibernate_Session"]; }
}

WCF Windsor Integration

Last night I commited WCF Integration to the Castle trunk. This is based in part on the work described here by Orand.

The main idea here is that WCF should get the service instances from Windsor, which gives us all the usual advantages of IoC plus Windsor's interceptors capabilities. It isn't a lot of code, but it makes it much easier to work with WCF services.

Using it on a web application:

  • Call this in the beginning of the application (Global.Application_Start):
    WindsorServiceHostFactory.RegisterContainer(container);
  • Create a MyService.svc file and put this in it:
  • <%@ ServiceHost Service="component name in windsor" 
    Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory, Castle.Facilities.WcfIntegration" %> 
    
  • Have fun

Using it from a non-web application:

WindsorContainer windsorContainer = new WindsorContainer();

windsorContainer.AddComponent("operations", typeof (IOperations), typeof (Operations));
Uri uri = new Uri("net.tcp://localhost/WCF.Facility");
WindsorServiceHost host = new WindsorServiceHost(windsorContainer, typeof (Operations),
                                                 uri);
host.Description.Endpoints.Add(
	new ServiceEndpoint(ContractDescription.GetContract(typeof (IOperations)),
	                    new NetTcpBinding(),
	                    new EndpointAddress(uri)));
host.Open();

Obviously you can cut down on this code significantly using the standard WCF configuration.

Another interesting thing is the ability to apply cross cutting concerns using IServiceBehavior / IEndPointBehavior. Any components that are registered with those interfaces on the container will be applied to all the services that are created this way. You can apply individual behaviors using the standard WCF means.

What is this not?

It is not here to replace WCF configuarion, and it deals specifically just with creating/disposing of services using Windsor. There are already good tools to configure WCF, and this approach takes full advantage of them.

The code has both tests and a sample site, have fun.