Autofac, ASP.NET и Microsoft.Practices.ServiceLocation

Я прорабатывал детали реализации IoC в своих веб-приложениях, но таким образом, чтобы использовать Microsoft.Practices.ServiceLocation. Я специально использую Autofac и интеграцию asp.net, но я хотел оставить себя открытым для других контейнеров. По строкам этого вопроса меня беспокоило, как получить доступ к контейнеру в коде веб-приложения.

У меня есть «основная» библиотека, которая в первую очередь определяет интерфейсы, которые нужно разрешить. Эта основная библиотека используется моим веб-приложением, а также другими приложениями. Очень удобно определять общие интерфейсы. Я подумал, что это отличное место для доступа к контейнеру IoC, и сделал это со статическим классом. Уловка заключается во внедрении контейнера в статический класс.

В веб-среде это сложно, потому что контейнер может быть разным для каждого запроса, в то время как в не веб-приложении он, вероятно, будет все время одним и тем же. Сначала я попытался ввести контейнер с помощью метода, но при следующем веб-запросе это быстро не удалось! Итак, я придумал это:

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);
    }
}

Теперь в моем global.asax.cs я делаю так:

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;

И призывы к разрешению зависимостей выглядят как

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

Поэтому вместо того, чтобы передавать конкретный контейнер, я передаю делегату, который знает, как ПОЛУЧИТЬ контейнер. Для не веб-приложений делегат, вероятно, просто вернет то, что обслуживает builder.Build ().

Мой вопрос к экспертам: есть ли в этом смысл? У меня есть простой способ добраться до чего-то, что может разрешать зависимости, не зная, что это за контейнерный продукт или откуда сам контейнер. Что вы думаете?


person n8wrl    schedule 03.09.2009    source источник


Ответы (1)


Мы используем подобный шаблон в основном из-за того, что IoC был введен в архитектуру без DI. Таким образом, необходимо иметь возможность явно вызывать контейнер для получения услуг, что в основном является шаблоном Factory.

Истинное преимущество IoC достигается, когда все зависимости могут быть внедрены, и ваш код больше не зависит от локатора сервисов. У Autofac.Integration.Web есть обработчики, которые будут выполнять инъекцию в объекты вашей страницы, что сделает статический локатор сервисов устаревшим. Я думаю, это предпочтительный способ, хотя (как и в нашем случае) локатора сервисов не всегда можно избежать.

Тем не менее, поскольку вы уже изолировали свое приложение от контейнера с помощью класса IoCContainer, я не вижу причин для дополнительной абстракции AutofacServiceLocator в IoCContainer. Суть в том, что IoCContainer уже является вашим локатором сервисов, и ему должен быть «разрешен» прямой доступ к реализации контейнера.

Вот мой взгляд на ваш класс локатора сервисов:

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>();
    }
}
person Peter Lillevold    schedule 08.10.2009
comment
Это очень круто, Питер, но я надеялся, что смогу повторно использовать свои библиотеки '.Core' в других контекстах, возможно, даже не в Интернете. Тем не менее, я думаю, что это отличный ответ, потому что он помогает мне избегать слишком большого количества абстракций. Спасибо! - person n8wrl; 08.10.2009
comment
Я понимаю вашу точку зрения. Тем не менее, если вы можете полностью отказаться от шаблона локатора сервисов и использовать только внедрение зависимостей, вам вообще не нужно будет решать эту проблему :) - person Peter Lillevold; 08.10.2009