Mixin’ up Ninject, Castle Dynamic Proxy and WCF – Part II

On the previous post of this series I introduced an extension to Ninject’s binding syntax  that allows you to define bindings to Castle dynamic proxies by writing something like:

kernel.Bind<IService>().ToDynamicProxy(p => p
         .WithTarget<ServiceImpl>()
         .UsingInterceptor<ConsoleLogInterceptor>());

Before going into the details of the syntax it’s worthy pointing out the base ideas:

  • Both proxies with and without target should be supported;
  • Proxies without target need at least one interceptor and this interceptor must not make the invocation proceed;
  • Proxies with target also need at least one interceptor (why using a dynamic proxy with target and no interceptors?) but there’s no special semantic like in the previous case;
  • An arbitrary number of interceptors can be added to the dynamic proxy
  • For proxies with target, target types should be resolved using Ninject;
  • Interceptors should also be resolved using Ninject;
  • Targets and interceptors may also be specified by instance or using a Func<IContext, T>, to keep the syntax flexible and similar to Ninject’s

The ToDynamicProxy method is an extension method to Ninject’s IBindingToSyntax and receives a lambda that defines the dynamic proxy configuration. Since the two options of proxies are with or without target, the following type is used for the lambda parameter:

    public interface IBindingToProxySyntax<T>
    {
	... WithTarget<TTarget>() where TTarget : T;
	... WithTarget<TTarget>(TTarget target) where TTarget : T;
	... WithTarget(Func<IContext, T> targetFactory);

	... WithoutTarget<TInterceptor>() where TInterceptor : IInterceptor;
	... WithoutTarget(IInterceptor interceptor);
	... WithoutTarget(Func<IContext, IInterceptor> interceptorFactory);
    }

I’ll get to the return types shortly. WithoutTarget receives the “final” interceptor (the one that must not proceed) by type, instance or factory. On the other hand the WithTarget method receives the proxy target. Note that the type argument on the interface above matches the one on ToDynamicProxy, which in turn matches the one on Ninject’s syntax.

IBindingWhenInNamedWithOrOnSyntax<T> ToDynamicProxy<T>(
            this IBindingToSyntax<T> binding,
            Func<IBindingToProxySyntax<T>, ...> dynamicProxyConfig)

The Func could be void but this would allow incorrect configurations. The approach in place is to use a return type that can only be obtained when defining correct configurations. You write exactly the same code but you get “compile time correctness”. But what exactly is a correct configuration for the dynamic proxy? If the WithoutTarget method above is used, then we’re good to go because the needed interceptor is configured. But when using WithTarget, we still need an interceptor. It is not specified directly on this method because the generic version would need both TTarget and TInterceptor type arguments to be specified. And that’s not so neat. So, WithTarget and WithoutTarget have different return types:

public interface IBindingToProxyWithInterceptorSyntaxBase<T>
{
	IBindingToProxyWithInterceptorSyntax<T> UsingInterceptor<TInterceptor>() where TInterceptor : IInterceptor;
	IBindingToProxyWithInterceptorSyntax<T> UsingInterceptor(IInterceptor interceptor);
	IBindingToProxyWithInterceptorSyntax<T> UsingInterceptor(Func<IContext, IInterceptor> interceptorFactory);
}

// Returned by WithTarget
public interface IBindingToProxyWithTargetAndWithInterceptorSyntax<T> : IBindingToProxyWithInterceptorSyntaxBase<T> { }
// Returned by WithoutTarget
public interface IBindingToProxyWithInterceptorSyntax<T> : IBindingToProxyWithInterceptorSyntaxBase<T> { }

The trick is the definition of the proxy configuration Func (now complete):

Func<IBindingToProxySyntax<T>, IBindingToProxyWithInterceptorSyntax<T>> dynamicProxyConfig

To get the appropriate return type – and thus the correct configuration – one needs to either:

  • Invoke WithoutTarget and optionally invoke UsingInterceptor to add more interceptors; OR
  • Invoke WithTarget and then invoke UsingInterceptor at least once.

The whole work being done behind the scenes is based on a simple principle: since we want Ninject to resolve targets and interceptors we keep track of them using factories that will have access to the Ninject kernel. Therefore the implementation of the syntax interfaces presented above is backed on a set of factories for the interceptors and a factory for the dynamic proxy itself:

private List<Func<IContext, IInterceptor>> _interceptorFactories;
private Func<IContext, IInterceptor[], T> _proxyFactory;

Note that IContext is an Ninject interface and provides access to the kernel in use on the current activation request. The implementation then has a method to actually create the dynamic proxy:

public T CreateProxy(IContext ctx)
{
	var interceptors = _interceptorFactories
		.Select(f => f(ctx))
		.ToArray();
	return _proxyFactory(ctx, interceptors);
}

The actual interceptor factories depend on which UsingInterceptor overloads were used. For instance, the generic overload registers a factory that requests the interceptor to the kernel. The proxy factory, in turn, depends on whether WithTarget or WithoutTarget was used. For instance:

public ... WithTarget<TTarget>() where TTarget : T
{
	_proxyFactory = (ctx, interceptors) => 
               ProxyGenerator.CreateInterfaceProxyWithTarget<T>(
                    ctx.Kernel.Get<TTarget>(),
                    interceptors);
	return this;
}

After this, implementing the ToDynamicProxy – which actually does the binding – comes down to defining an Ninject binding using the CreateProxy method above.

public static ... ToDynamicProxy<T>(
       this IBindingToSyntax<T> binding,
       Func<IBindingToProxySyntax<T>, IBindingToProxyWithInterceptorSyntax<T>> dynamicProxyConfig)
{
        var proxyBinding = new BindingToProxySyntax<T>();
	dynamicProxyConfig(proxyBinding);
	return binding.ToMethod(proxyBinding.CreateProxy);
}

Pretty cool right? But this series of posts is also about WCF.. so what about creating dynamic WCF proxies?

kernel.Bind<IEchoService>()
	.ToDynamicProxy(p => p
             .AsWcfProxy()
             .UsingInterceptor<LogInterceptor>())
        .InSingletonScope();
// ...
var proxy = kernel.Get<IEchoService>();
Console.WriteLine(proxy.Echo("Hey there"));

Stay tuned for the next post!

Note: I’ll make the source code available at the end of the series

Advertisements

One thought on “Mixin’ up Ninject, Castle Dynamic Proxy and WCF – Part II

  1. Pingback: Mixin’ up Ninject, Castle Dynamic Proxy and WCF – Part III | Luís Gonçalves

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s