Search code examples
c#wcfdependency-injectionstructuremap

How to get rid of ObjectFactory in code using StructureMap


After upgrading our solution using the latest StructureMap version (3.1.6.191) I got a lot of obsolete warnings. These warnings come from StructureMap where the ObjectFactory class will be deprecated in future releases (4.0+).

I am using WCF and we want to let StructureMap hook in the WCF pipeline using an implementation of the IInstanceProvider:

public class StructureMapInstanceProvider : IInstanceProvider

This class uses the ObjectFactory to get an instance, how can we get an instance of a type when there is no static class anymore of my container to resolve it ?


Solution

  • ObjectFactory is going away because many consider it anti-pattern to access the container from within the application (known as the Service Locator Pattern). This tightly couples your code to the container and makes it difficult to maintain the configuration because it is not very easy to determine what dependencies a class needs.

    Dependency Injection is different than using a Service Locator. With Dependency Injection, the object graph is resolved near the start of the application in the composition root. Once the application is created, it has no reference to the IoC container and therefore is not tightly coupled to it. Dependencies are explicitly defined in the class constructor, so you need not look further to discover what dependencies a class requires when registering it.

    During runtime, you inevitably need to create instances of classes. For that, you may turn to one of many Creational Patterns (of which Abstract Factory is most common and IInstanceProvider implements) or alternatively, you can inject a method that uses the container to create those instances.

    I recommend reading the book Dependency Injection in .NET. There is a section (7.3) that specifically goes over wiring up WCF with a composition root by implementing the ServiceHost, ServiceHostFactory, and IInstanceProvider.

    Here is a basic example of a WCF composition root that uses StructureMap (although I haven't verified it works).

    StructureMap Registry

    Here is where you register your types with the container. You may use more than one registry if you like.

    public class MyRegistry : Registry
    {
        public MyRegistry()
        {
            // Register all types
            this.For<ISomeService>().Use<SomeService>();
        }
    }
    

    ServiceHostFactory

    This is where we instantiate the container and register the type mappings.

    public class MyServiceHostFactory : ServiceHostFactory
    {
        private readonly IContainer container;
    
        public MyServiceHostFactory()
        {
            this.container = new Container(r => r.AddRegistry<MyRegistry>());
        }
    
        protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
        {
            return new MyServiceHost(this.container, serviceType, baseAddresses);
        }
    }
    

    ServiceHost

    This is where we inject the container. Yes, we need to do this in at least one place. It is okay here because this is all part of the composition root to plug into WCF.

    public class MyServiceHost : ServiceHost
    {
        public MyServiceHost(IContainer container, Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
            if (container == null)
                throw new ArgumentNullException("container");
    
            var contracts = this.ImplementedContracts.Values;
            foreach (var c in contracts)
            {
                c.Behaviors.Add(new MyInstanceProvider(container, serviceType));
            }
        }
    }
    

    IInstanceProvider

    Once again we are still in the composition root of the application, so we can inject the container to resolve our instances.

    Your services that are resolved by this Abstract Factory should not have a reference to the container (neither static nor injected).

    public partial class MyInstanceProvider : IInstanceProvider, IContractBehavior
    {
        private readonly IContainer container;
        private readonly Type serviceType;
    
        public MyInstanceProvider(IContainer container, Type serviceType)
        {
            if (container == null)
                throw new ArgumentNullException("container");
    
            this.container = container;
            this.serviceType = serviceType;
        }
    
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return this.GetInstance(instanceContext);
        }
    
        public object GetInstance(InstanceContext instanceContext)
        {
            return this.container.GetInstance(this.serviceType);
        }
    
        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
            // Allow the lifetime management behavior of StructureMap to release dependencies
        }
    
        public void ApplyDispatchBehavior(
            ContractDescription contractDescription, ServiceEndpoint endpoint,
            DispatchRuntime dispatchRuntime)
        {
            dispatchRuntime.InstanceProvider = this;
        }
    }
    

    Usage

    Just add something along these lines to your .svc file to register the custom MyServiceHostFactory to resolve your WCF services.

    <%@ ServiceHost Factory = "MyNamespaceName.MyServiceHostFactory, MyAssemblyName" Service = "MyNamespaceName.MyWcfService" %>
    

    References used: