Как внедрить зависимости в global.asax.cs

#asp.net-mvc-3 #dependency-injection #unity-container

#asp.net-mvc-3 #внедрение зависимостей #unity-контейнер

Вопрос:

Как мне внедрить зависимости в global.asax.cs, то есть в класс MvcApplication?

Ранее использовав шаблон Service Locator (anti-) для внедрения зависимостей, я пытаюсь следовать рекомендациям по наилучшей практике в моем последнем приложении MVC, используя контейнер IOC (в частности, Unity.Mvc3, потому что он поставляется с реализацией IDependencyResolver из коробки) и инъекцией конструктора.

Пока все кажется довольно простым, за исключением пары проблем, одна из которых находится в global.asax.cs (другая предназначена для пользовательских атрибутов, но есть вопрос, касающийся SO, охватывающий это).

Обработчики событий HttpApplication в классе MvcApplication, такие как:

 Application_Start()
Application_EndRequest(object sender, EventArgs e)
Application_AcquireRequestState(object sender, EventArgs e)
  

могут потребоваться внешние зависимости, например, зависимость от ILogService. Итак, как мне внедрить их, не прибегая к шаблону service locator (anti-), например

 private static ILogService LogService
{
    get
    {
        return DependencyResolver.Current.GetService<ILogService>();
    }
}
  

Любая помощь / совет с благодарностью!

Комментарии:

1. Привет, интересный вопрос! Почему вы не можете внедрить зависимости в свой global.asax в OnApplicationStarted? Если приложение не может запуститься, вы будете знать об этом, поскольку ничего не будет работать…

Ответ №1:

Класс в вашем global.asax.cs является корнем вашей композиции, поэтому вы не можете (и не должны) вводить в него что-либо извне.

Однако существует только один экземпляр класса MvcApplication, поэтому, если вам нужен сервис в одном из его методов, вы можете просто объявить его как поле-член, например:

 public class MvcApplication : System.Web.HttpApplication
{
    private readonly ILogService log;

    public MvcApplication()
    {
        this.log = new MyLogService();
    }

    protected void Application_Start()
    {
        // ...

        this.log.Log("Application started");
    }
}
  

Комментарии:

1. Спасибо за отметку быстрого ответа. Как вы проводите модульное тестирование методов, если вы не можете внедрить издевательский экземпляр, например, ILogService?

2. Я этого не делаю. Корень композиции является примером скромного объекта ( xunitpatterns.com/Humble Object.html ). Если вам нужно вызвать из него сложную логику, вы можете делегировать другому классу, который можно протестировать с помощью модуля.

3. Хм, вы говорите в своем сообщении в блоге, что «На контейнер DI следует ссылаться только из корня композиции. Все остальные модули не должны иметь ссылки на контейнер «. Разве это не дает мне свободу вызывать resolve, пока он находится в корне композиции, т. Е. Мой код в порядке, как есть? Одна вещь, которая беспокоит меня в вашем примере, — это ваш вызов this.log = new MyLogService() . Поскольку на данный момент мы уже инициализировали контейнер, то почему бы его не использовать? Если мы затем захотим поменять ILogService на другой, нам нужно изменить его только в одном месте, в контейнере.

4. В своем ответе я не сделал никаких предположений о том, будете ли вы использовать контейнер DI. Если вы используете контейнер, вы можете разрешить его из него, а не создавать его напрямую…

5. «существует только один экземпляр класса MvcApplication» просто неверно — для каждого запроса, который обрабатывается параллельно, существует один экземпляр HttpApplication . Обратите внимание, что Application_Start вызывается НЕ для каждого вновь созданного экземпляра, а только один раз для каждого запущенного веб-приложения. Отличный пост в блоге об этом: blog.andreloker.de/post/2008/05/HttpApplication-instances.aspx

Ответ №2:

Вы должны использовать AutofacConfig.Resolve<T>() вместо using DependencyResolver.Current.GetService<T>() , чтобы получать свои сервисы без ошибок.