January 11, 2009

Avoiding Multiple IOC Container Instances in WCF

I've recently been looking for the best way to use WCF, Unity and NHibernate together. Fortunately there are already some really good posts on integrating WCF with IOC containers:

The UnityContainer, and NHibernate ISessionFactory, should only be created once per application - we want them to have 'global' scope. In the past, the ASP.NET Global Application_Start event handler could be used to do this, but it isn't a good idea to rely on host-specific features for a WCF implementation - in the future we may want to re-host the services outside of ASP.NET where these features are not available (other hosting options include WAS, a Windows Service or self-hosted).

The solutions I've seen so far use a custom ServiceHostFactory to create the IOC container, but because WCF creates a new factory for each service, the factory needs to take care not to create new instances of the IOC container each time.

My solution: 1) Create the IOC container in a static constructor of the custom ServiceHostFactory. This ensures that it is created only once for the application, and in a thread-safe manner. 2) Create and configure any other services (eg. NHibernate, log4net) that require 'per-application' instantiation and register them with the IOC. 3) Pass the IOC container into the custom ServiceHost, and return it from the factory as normal.

Here is the code:

   1:  public class MyServiceHost : ServiceHost
   2:  {
   3:      private IUnityContainer _ioc;
   4:   
   5:      public IUnityContainer IoC
   6:      {
   7:          get { return _ioc; }
   8:      }
   9:   
  10:      public MyServiceHost(IUnityContainer ioc, Type serviceType, Uri[] baseAddresses)
  11:          : base(serviceType, baseAddresses)
  12:      {
  13:          _ioc = ioc;
  14:      }
  15:  }

   1:  public class MyServiceHostFactory : ServiceHostFactory
   2:  {
   3:      private static UnityContainer _ioc;
   4:   
   5:      static MyServiceHostFactory()
   6:      {
   7:          _ioc = InitializeUnity();
   8:   
   9:          InitializeAndRegisterNHibernate();
  10:          InitializeAndRegisterLog4Net();
  11:      }
  12:   
  13:      protected override ServiceHost CreateServiceHost(
  14:          Type serviceType, Uri[] baseAddresses)
  15:      {
  16:          return new MyServiceHost(_ioc, serviceType, baseAddresses);
  17:      }
  18:   
  19:      private static UnityContainer InitializeUnity()
  20:      {
  21:          UnityContainer container = new UnityContainer();
  22:          UnityConfigurationSection config = 
  23:              ConfigurationManager.GetSection("unity") as UnityConfigurationSection;
  24:          config.Containers.Default.Configure(container);
  25:          return container;
  26:      }
  27:   
  28:      private static void InitializeAndRegisterNHibernate()
  29:      {
  30:          NHibernate.Cfg.Configuration config = new NHibernate.Cfg.Configuration();
  31:          config.Configure();
  32:   
  33:          _ioc.RegisterInstance(config.BuildSessionFactory());
  34:      }
  35:   
  36:      private static void InitializeAndRegisterLog4Net()
  37:      {
  38:          log4net.Config.XmlConfigurator.Configure();
  39:   
  40:          _ioc.RegisterInstance(LogManager.GetLogger("Application"));
  41:      }
  42:  }

4 comments:

Dan said...

Kewl! Thanks for the tip.

Anonymous said...

nice. you've just solved one of my problems ;)

rjperes said...

Why not use Common Service Locator and skip passing a pointer to the ServiceHost?

Julian Maughan said...

You only need the IOC (Unity) reference in the custom ServiceHost (MyServiceHost) if you want to use it directly in the service class. If the dependencies are injected into the service class using something like Common Service Factory, or your own IInstanceProvider implementation, you probably won't need it.

Post a Comment