The previous post of this series was all about the internals of a binding syntax for Ninject that enables binding to Castle dynamic proxies. At the end I suggested using that syntax to create dynamic WCF client proxies.
kernel.Bind<IEchoService>() .ToDynamicProxy(p => p .AsWcfProxy() .UsingInterceptor<LogInterceptor>()) .InSingletonScope(); // ... var proxy = kernel.Get<IEchoService>(); Console.WriteLine(proxy.Echo("Hey there"));
The previous code creates a dynamic proxy with a logging interceptor that ends up invoking a WCF service whose contract is represented by the IEchoService interface. This may look a bit esoteric, but the principles are quite simple:
- It is assumed that there is a single client endpoint configuration for IEchoService on Web.config. This is reasonable for most scenarios but the code could be enhanced so that the endpoint configuration or configuration name would be a parameter of the AsWcfProxy method. But let’s keep it simple.
- The proxy can be used without worrying about disposal and the same instance can be used multiple times. This means that the dynamic proxy is actually working as an “implicit factory”, which can be more convenient to use.
- I’m avoiding using service references generated by svcutil. This means that we don’t have a generated “EchoServiceClient” class to be the actual target of the dynamic proxy. We’ll go with ChannelFactory instead, generating proxies on-the-fly. The code could be easily extended to also support existing WCF proxies (I’ll point that out on the appropriate time or maybe I’ll include it with the code at the end of the series).
So, first things first. We don’t have a target for the dynamic proxy, so we’ll create one when needed. That leaves us with one option: using a dynamic proxy without target. The “target” will be a WCF proxy created through ChannelFactory. But when should this proxy be created? Since I don’t want to be worried about disposal when using the dynamic proxy, a possible approach is to create a new WCF proxy before each service request and dispose it afterwards. This behavior is a perfect match for an interceptor!
class WcfProxyWithDisposalInterceptor<TInterface> : IInterceptor { // Per-type cache of channel factories. private static ChannelFactory<TInterface> ServiceFactory = new ChannelFactory<TInterface>("*"); void IInterceptor.Intercept(IInvocation invocation) { using (var channel = (IDisposable)ServiceFactory.CreateChannel()) { invocation.ReturnValue = invocation.Method.Invoke(channel, invocation.Arguments); } } }
The code is slightly abbreviated for clarity, but the essence is there. We have a ChannelFactory for a given service contract that is used to create a proxy on which the actual operation is invoked. Since invocation.Proceed() isn’t invoked, the interceptor will return to the caller with the return value obtained from the WCF proxy. With this interceptor in place, defining the AsWcfProxy method presented on the beginning of this post is straightforward. Of course we’ll take advantage of the binding syntax that was previously developed!
public static IBindingToProxyWithInterceptorSyntax<T> AsWcfProxy<T>(this IBindingToProxySyntax<T> binding) { return binding.WithoutTarget<WcfProxyWithDisposalInterceptor<T>>(); }
As described before, we configure a dynamic proxy without target, whose final interceptor is the one just defined, that in turn forwards the call to the WCF service. Over the result of this method one can configure additional interceptors, since the syntax is exactly the same from the previous post. If you just want to take advantage of the “auto disposal” you can go further and define an extension method over Ninject’s syntax so that you can write:
kernel.Bind<IEchoService>().ToWcfProxy();
At this point I’ve met the initial goal for this series of posts but… I’ve also been using Ninject’s WCF extensions to have WCF services activated using dependency injection and I figured that we could push it a little further and also enable interceptors on the service side. Yes, I know about WCF’s message inspectors, but dynamic proxies and interceptors will be at an higher level on the “service call stack”. In addition, interceptors can easily rely on dependency injection to have access to more application specific stuff, which can be harder using behaviors and message inspectors.
So.. the next (and last) post of the series will be about tweaking Ninject’s WCF extensions and using dynamic proxies on service activation.