Search

Code Station

Thoughts on C#, ASP.NET, MVC, Angular, WCF, Entity Framework, SQL, IIS

WCF Services – Dependency Injection with StructureMap

Dependency Injection is a well known design pattern for many years. Development Teams are well aware of its benefits and it has been adequately documented.

In simple words it discourages creating dependencies in classes using new keyword or factories.

It promotes the idea of injecting the dependencies through constructors or setter/getter properties.

Dependency Injection is best enabled by using IOC containers; there are many available in .Net world, few of the most famous ones

  1. Ninject
  2. StructureMap
  3. Autofac
  4. Castle Windsor
  5. Unity
  6. MEF

My favorite is StructureMap, it’s easy to use and performs well in comparison to other IOC containers, not the fastest but definitely in top 5.

IOC Performance Comparison

I have used StructureMap with WCF and this blog shows how I did it.

A typical n tier application will have services which will have dependency on Business Logic classes; Business Logic classes would eventually have dependency on data access classes /ORM(s).

Following is an example of how the business logic class is instantiated, with this approach RecordService must know how to create a dependency.

This will also be problematic if tomorrow you want to replace the implementation of IRecordLogic with a newer version

Capture.PNG

Lets inject dependency via constructor

capture

but now when I run the service I get an error

capture

This happens because the underlying infrastructure doesn’t know how to plug the dependency, in order to plug this dependency we have to make modifications in the WCF pipeline.

Windows Communication Foundation (WCF) is highly extensible, and we will attach IOC container by hooking into one of the extension points.

Following are the steps that we need to follow in order to create and use an extension point.

  1. Create an implementation of IInstanceProvider (System.ServiceModel.Dispatcher).
    1. IInstanceProvider interface contains methods that build service objects.
    2. In order to fill the dependency we  will have to create our own implementation of it and attach it with in service pipeline.
    3. GetInstance method is the place where we will add StructureMap container and lookup (Singleton is our wrapper over Container, much like ObjectFactory of StructureMap version 2.6.*.
          public class TestInstanceProvider : IInstanceProvider
          {
              private Type serviceType;
              public TestInstanceProvider(Type serviceType) {
                  this.serviceType = serviceType;
              }
              public object GetInstance(InstanceContext instanceContext) {
                  return GetInstance(instanceContext, null);
              }
              public object GetInstance(InstanceContext instanceContext, Message message) {
                  return Singleton.Container.GetInstance(serviceType);
              }
              public void ReleaseInstance(InstanceContext instanceContext, object instance) {
                  if(instance is IDisposable){
                      (instance as IDisposable).Dispose();
                  }
              }
          }
      
  2. Create  a custom behavior by implementing IServiceBehavior
    1. Override the ApplyDispatchBehavior and attach our custom implementation of IInstanceBehavior in the dispatch runtime.
          public class TestServiceBehavior : IServiceBehavior
          {
              public void AddBindingParameters(
                  ServiceDescription serviceDescription,
                  ServiceHostBase serviceHostBase,
                  Collection<ServiceEndpoint> endpoints,
                  BindingParameterCollection bindingParameters) { }
              public void ApplyDispatchBehavior(
                  ServiceDescription serviceDescription,
                  ServiceHostBase serviceHostBase) {
                  foreach(ChannelDispatcher channel in serviceHostBase.ChannelDispatchers)            {
                      foreach(EndpointDispatcher endpoint in channel.Endpoints) {
                          endpoint.DispatchRuntime.InstanceProvider =
                              new TestInstanceProvider(serviceDescription.ServiceType);
                      }
                  }
              }
              public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase){ }
          }
      
  3. Create a new BehaviorExtensionElement and instantiate custom behavior in CreateBehavior method.
    1. BehaviorExtensionElement helps in creating a configuration element that lets a developer attach his/her custom ServiceBehavior in the pipeline.
          public class TestExtensionElement : BehaviorExtensionElement {
              public override Type BehaviorType {
                  get {
                      return typeof(TestServiceBehavior);
                  }
              }
              protected override object CreateBehavior() {
                  return new TestServiceBehavior();
              }
          }
      
  4.   Attach new behavior extension element in the configuration file
      <system.serviceModel>
        <extensions>
          <behaviorExtensions>
            <!--Our extension element-->
            <add name="DependencyInjector" type="WebApp.Framework.TestHarness.Infrastructure.TestExtensionElement, WebApp.Framework.TestHarness, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
          </behaviorExtensions>
        </extensions>
        <services>
          <service name="WebApp.Framework.TestHarness.Services.RecordService">
            <endpoint address="" binding="basicHttpBinding" contract="WebApp.Framework.TestHarness.IRecordService" />
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="true"/>
              <!--Adding custom element in behavior-->
              <DependencyInjector/>
            </behavior>
          </serviceBehaviors>
          <endpointBehaviors/>
        </behaviors>
        <bindings>
          <basicHttpBinding>
          </basicHttpBinding>
        </bindings>
      </system.serviceModel>
    

Now our extension hook is ready, only thing remaining is adding objects to the IOC container.

This we will do with the help of Registry class in StructureMap, we will add all of our classes that we want to be available for DI in Registry and then we will add registry to the container.

A registry is created by extending from StructureMap.Registry.

public class TestRegistry : Registry
    {
        public TestRegistry()
        {
            For<IRecordLogic>()
 .LifecycleIs<TransientLifecycle>()
 .Use<RecordLogic>();
        }
    }

Now we have the registry ready, lets plug this in the container, this we will do in Global.asax of WCF Service application

    public class Global : System.Web.HttpApplication {
        protected void Application_Start(object sender, EventArgs e) {
            Singleton.Container.Configure(
                config => config.AddRegistry<TestRegistry>()
            );
        }
    }

Our Container Wrapper (Let’s not duel on patterns :)]

    public sealed class Singleton
    {
        private static readonly object locker = new object();
        private static IContainer container;
        private Singleton() { }        
        public static IContainer Container {
            get {
                if (container == null) {
                    lock (locker) {
                        if (container == null) {
                            container = new Container();
                        }
                    }
                }
                return container;
            }
        }
    }

Now lets try to run our service again.

capture

Seems All good, lets access the service with SoapUI

capture

Structure Map Version: 4.4.1.451
.Net Framework Version: 4.6.1

Would be happy to hear any feedback?

Advertisements
Featured post

Blog post title

This is an additional placeholder post. Click the Edit link to modify or delete it, or start a new post.

Blog post title

This is an additional placeholder post. Click the Edit link to modify or delete it, or start a new post.

Blog at WordPress.com.

Up ↑