Ninject and WCF

Ninject is very simple and in the same time powerful IoC container. I used it in few project and had very little or none problems.

Most of the time getting instances from Ninject kernel requires simple line with binding expression, or even this is sometime unnecessary if type is self bind able (i.e. if it is concrete class with parameterless constructor).

Little harder is getting Ninject to work with WCF. You cannot just bind interfaces types because proxies which implements them are created through .NET mechanism. Luckily WCF system is very flexible and mostly can be changed/extended with custom functionality.

How we can do that? Best solution is to add new behavior for our WCF services. Behavior is a class that implements IServiceBehavior interface. ApplyDispatchBehavior method accessible through that interface allow our code to change instance provider of our service. Instance provider on the other hand is object with IInstanceProvider interface implementation and GetInstance method. This method is defined in following way:

object GetInstance(InstanceContext instanceContext);
object GetInstance(InstanceContext instanceContext, Message message);

Inside one of them we can create instance of our service from Ninject container.

Let us start from the top, with behavior class. It can be applied to service from attribute.

public class NinjectBehaviorAttribute : Attribute, IServiceBehavior
{
	public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
				Collection endpoints, BindingParameterCollection bindingParameters)
	{
	}

	public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
	{
		Type serviceType = serviceDescription.ServiceType;
		IInstanceProvider instanceProvider = new NinjectInstanceProvider(NinjectServiceLocator.Kernel, serviceType);

		foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
		{
			foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
			{
				DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
				dispatchRuntime.InstanceProvider = instanceProvider;
			}
		}
	}

	public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
	{
	}
}

All interesting things happens inside of ApplyDispatchBehavior method. First is created NinjectInstanceProvider class, to which is passed instance of Ninject kernel, and our desired service type information. Instance provider is defined as following:

public class NinjectInstanceProvider : IInstanceProvider
{
	private Type serviceType;
	private IKernel kernel;

	public NinjectInstanceProvider(IKernel kernel, Type serviceType)
	{
		this.kernel = kernel;
		this.serviceType = serviceType;
	}

	public object GetInstance(InstanceContext instanceContext)
	{
		return this.GetInstance(instanceContext, null);
	}

	public object GetInstance(InstanceContext instanceContext, Message message)
	{
		return kernel.Get(this.serviceType);
	}

	public void ReleaseInstance(InstanceContext instanceContext, object instance)
	{
	}
}

Inside second overload of GetInstance method is created actual service instance, through Ninject kernel. Ninject kernel is acquired from simple implementation of service locator. It’s just static class with public read only property with Ninject kernel.

public static class NinjectServiceLocator
{
	public static IKernel Kernel { get; private set; }

	public static void SetServiceLocator(IKernel kernel)
	{
		Kernel = kernel;
	}
}

Instance of kernel is injected into property with SetServiceLocator method after initialization, preferably inside NinjectWebCommon class, which is created in App_Start directory after adding Ninject to project from NuGet.

private static IKernel CreateKernel()
{
	var kernel = new StandardKernel();
	kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
	RegisterServices(kernel);
	NinjectServiceLocator.SetServiceLocator(kernel);
	return kernel;
}

I decided to go with this solution instead of actual implementation of Microsoft ServiceLocator class to keep it simple, which in fact is working in similar way.

After creating instance provider object, we apply it to all endpoints inside ApplyDispatchBehavior.

Last thing is to actually registering service types inside Ninject. Typically we are creating all data necessary to make service proxy inside web.config file. WCF channel can be created from such configuration with ChannelFactory class. Lets implement this functionality inside class that implements Ninject.Activation.Provider<T> type available from Ninject assembly.

public class ConfigServiceProvider<TService> : Provider<TService>
{

	protected override TService CreateInstance(IContext context)
	{
		var @interface = typeof(TService);
		var interfaceTypeName = @interface.FullName;
		var endpointsConfig = (ClientSection)ConfigurationManager.GetSection("system.serviceModel/client");
		string address = null;
		foreach (ChannelEndpointElement endpoint in endpointsConfig.Endpoints)
		{
			if (endpoint.Contract == interfaceTypeName)
			{
				address = endpoint.Address.OriginalString;
				break;
			}
		}
		var factory = new ChannelFactory<TService>(new WSHttpBinding(), address);
		return factory.CreateChannel();
	}
}

First provider is accessing configuration of all services, then searching inside of configuration for matching interface type name. If finds one, address of WCF service endpoint is passed to ChannelFactory class which will create service proxy. With such provider we can do actual type binding inside Ninject module:

public class WcfModule : NinjectModule
{
	public override void Load() { }

	public IBindingWhenInNamedWithOrOnSyntax BindServiceFromConfig()
	{
		return Bind().ToProvider<ConfigServiceProvider>();
	}
}

public class ServicesModule : WcfModule
{
	public override void Load()
	{
		BindServiceFromConfig();
	}
}

WcfModule class can be placed inside some library so we can use it in more than project. I am sure that not only one of them is using WCF services 🙂 ServicesModule on the other hand should be placed inside assembly with services interfaces and loaded from NinjectWebCommon class inside WCF project.

And that is all. WCF web application registers services interfaces inside Ninject, creates kernel and setting its instance inside our custom ServiceLocator class. After that when service instance is accessed from .NET framework, NinjectBehaviorAttribute do its magic and acquires instance of NinjectInstanceProvider class, which is asking for instance of specified service from kernel. Kernel from its binding creates ConfigServiceProvider through one is created actual instance of proxy thanks to our configuration.

Leave a Reply

Your email address will not be published. Required fields are marked *

Solve : *
13 + 17 =