views:

551

answers:

1

I've been working thru the details of implementing IoC in my web apps but in a way that leverages Microsoft.Practices.ServiceLocation. I am specifically using Autofac and the asp.net integration, but I wanted to leave myself open to other containers. Along the lines of this question, i was concerned about how to access the container in my web app code.

I have a 'core' library that primarily defines interfaces to be resolved. This core library is used by my web app and other apps as well. Very handy to have common interfaces defined. I thought this was an excellent place to put access to the IoC container, and I did so with a static class. The trick is injecting the container into the static class.

It's tricky in a web environment becuase the container may be different for each request, while in a non-web app it will probably be the same all the time. At first I tried injecting the container direclty with a method but that quickly failed on the next web request! So I came up with this:

public static class IoCContainer
{
    public static void SetServiceLocator(Func<IServiceLocator> getLocator)
    {
        m_GetLocator = getLocator;
    }
    static private Func<IServiceLocator> m_GetLocator = null;

    public static T GetInstance<T>(string typeName)
    {
        return m_GetLocator().GetInstance<T>(typeName);
    }
}

Now in my global.asax.cs I do this:

protected void Application_Start(object sender, EventArgs e)
{
    var builder = new Autofac.Builder.ContainerBuilder();
    ... register stuff ...
    var container = builder.Build();
    _containerProvider = new Autofac.Integration.Web.ContainerProvider(container);
    Xyz.Core.IoCContainer.SetServiceLocator(() => 
        new AutofacContrib.CommonServiceLocator.AutofacServiceLocator
            (_containerProvider.RequestContainer));
}
public IContainerProvider ContainerProvider
{
    get { return _containerProvider; }
}
static IContainerProvider _containerProvider;

And calls to resolve dependences look like

var someService = Xyz.Core.GetInstance<ISomeService>();

So rather than pass a specific container I pass a delegate that knows how to GET a container. For non-web applications the delegate would probably just return what builder.Build() serves up.

My question to the experts is, does this make sense? I have an easy way to get to something that can resolve dependencies without knowing what the container product is or where the container itself comes from. What do you think?

+2  A: 

We use a similar pattern mostly due to the fact that IoC was introduced into a non-DI architecture. Thus the need to be able to explicitly call the container to get services, which basically is the Factory pattern.

The true benefit of IoC is achieved when all dependencies can be injected and your code no longer have a dependency on the service locator. Autofac.Integration.Web have handlers that will perform injection into your page objects which will make the static service locator obsolete. Imo this is the preferred way, though (as in our case also) service locator cannot always be avoided.

That said, since you already have isolated your app from the container using IoCContainer class, I see no reason to have the additional abstraction of AutofacServiceLocator within IoCContainer. Bottom line is that IoCContainer is already your service locator and should be "allowed" direct access to the container implementation.

Here is my take on your service locator class:

public static class IoCContainer
{
    private static IContext GetContainer()
    {
        var cpa = 
             (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
        return cpa.ContainerProvider.RequestContainer;
    }

    public static T GetInstance<T>()
    {
        return GetContainer().Resolve<T>();
    }
}
Peter Lillevold
This is very cool Peter but I was hoping to be able to reuse my '.Core' libraries in other contexts, perhaps not even web-based. Still, I think this is a great answer because it helps ground me to avoid too many abstractions. Thanks!
n8wrl
I see your point. Still, if you can do away with the service locator pattern completely, and only use dependency injection, you wouldn't need to solve this problem at all :)
Peter Lillevold