Reputation: 9578
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 ?
Upvotes: 3
Views: 5742
Reputation: 56869
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).
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>();
}
}
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);
}
}
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));
}
}
}
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;
}
}
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:
Upvotes: 8