Dependency injection в MVC 3 Framework на примере Autofac

в 12:41, , рубрики: ASP

В настоящее время трудно представить себе приложение на MVC3Framework без использования Dependency injection. Это статья рассчитана на тех кто знает, что такое DI, но никогда не использовал Autofac для этого.
Так же отмечу что более подробно об Autofac вы сможете прочитать тут

Для начала мы должны скачать и включить библиотеки Autofac в проект. Для этого я использую NuGet. Введите в консоли:
PM> Install-Package Autofac Устанавливаем сам Autofac
PM> Install-Package Autofac.Mvc3 И дополнения к Mvc3

Так же вы можете воспользоваться визуальным редактором NuGet, или просто скачать и подключить эти сборки.
Ниже написан код для инициализации Autofac, я постарался включить сюда основные варианты инициализации.

protected void Application_Start()
{
  Trace.TraceInformation("Website initialization started");

  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);

  // Setup IoC container
  var builder = new ContainerBuilder();

  builder.RegisterModule(new ServicesModule());

  builder.Register(c => new Entities()).As<IEntities>()
      .OnActivated(e =>
        {
          var config = DependencyResolver.Current.GetService<IGlobalSettings>();
          e.Instance.RawConnectionString = config.Data.SqlServerConnectionString;
         }).InstancePerHttpRequest();

  builder.RegisterType<LocalizationContext>().PropertiesAutowired().InstancePerHttpRequest();

  builder.Register(c => new DefaultGlobalSettings()).As<IGlobalSettings>().SingleInstance();

  builder.RegisterGeneric(typeof(MongoRepository<>))
             .As(typeof(IMongoRepository<>)).InstancePerHttpRequest();

  builder.Register(c => EnvironmentContext()).As<IEnvironmentContext>().InstancePerHttpRequest();

  builder.RegisterModule(new AutofacWebTypesModule());

  builder.RegisterSource(new ViewRegistrationSource());
  
  builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
  builder.RegisterModelBinderProvider();

  builder.RegisterFilterProvider();
  
  IContainer container = builder.Build();
  DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

  Trace.TraceInformation("Website has been successfully initialized");
}

Теперь рассмотрим, что же на самом деле тут произошло.
Регистрируем модуль для Autofac. Это очень полезно для инициализации классов (классы с модификатором доступа internal), которые находятся в отдельной сборке. Нам достаточно оставить ServicesModule в той же сборке, и пользоваться всеми прелестями DI.

     builder.RegisterModule(new ServicesModule());

Метод, который должен выполняться для получения необходимого объекта, можно указать следующим образом:

     builder.Register(c => EnvironmentContext()).As<IEnvironmentContext>().InstancePerHttpRequest()

То же самое но в виде анонимного делегата.

  builder.Register(c => new Entities()).As<IEntities>()
             .OnActivated(e =>
              {
                  var config = DependencyResolver.Current.GetService<IGlobalSettings>();
                   e.Instance.RawConnectionString = config.Data.SqlServerConnectionString;
               }).InstancePerHttpRequest();

Сопоставить клас с интерфейсом можно следующим образом:

  builder.Register(c => new DefaultGlobalSettings()).As<IGlobalSettings>().InstancePerHttpRequest();

Для того чтоб зарегистрировать Generic классы воспользуемся следующим кодом:

  builder.RegisterGeneric(typeof(MongoRepository<>))
                        .As(typeof(IMongoRepository<>)).InstancePerHttpRequest();

Для того чтоб инжектировать свойства класса достаточно сделать следующую инициализацию. Отмечу, что DI в конструктор проходит намного быстрее чем на свойство, поэтому использовать подобную инжекцию следует только в крайних случаях.

  builder.RegisterType<LocalizationContext>().PropertiesAutowired().InstancePerHttpRequest();

Если заранее известно свойство можно воспользоваться и такой инициализацией:

  builder.Register(c => new LocalizationContext { GlobalSettings = c.Resolve<IGlobalSettings>() });

Инициализация классов HttpContextBase, HttpRequestBase, HttpResponseBase, HttpServerUtilityBase, HttpSessionStateBase, HttpApplicationStateBase, HttpBrowserCapabilitiesBase, HttpCachePolicyBase, VirtualPathProvider происходит регистрацией встроенного модуля autofac.

  builder.RegisterModule(new AutofacWebTypesModule());

Autofac позволяет проводить инжекцию и в ModelBinder.

 builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
 builder.RegisterModelBinderProvider();

Инжекция в фильтры (классы производные от ActionFilterAttribute) происходит следующим образом:

 builder.RegisterFilterProvider();

Инжекция во View происходит регистрацией следующего модуля.

 builder.RegisterSource(new ViewRegistrationSource());

Как вы заметили код сопровождается вызовом:
.InstancePerDependency() — используется по умолчанию, создает новый объект при каждом вызове.
.InstancePerHttpRequest() -один экземпляр на HttpRequest
.SingleInstance() — создает только один экземпляр класса.
.InstancePerLifetimeScope() — создает экземпляр для конкретного LifetimeScope используеться следующим образом:

using (var lifetime = container.BeginLifetimeScope())
{
  var component = lifetime.Resolve<SomeComponent>();
  // component, and any of its disposable dependencies, will
  // be disposed of when the using block completes
}

Получаем экземпляр контейнера.

 IContainer container = builder.Build();

Устанавливаем DependencyResolver

 DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

На этом все. Теперь у вас есть минимальные навыки работы с autofac, и вы можете смело начинать его использовать в своих проекта. Мне было достаточно сложно выделить именно те моменты которые, помогли бы вам освоить Autofac за короткое время. Если я недостаточно осветил какую-либо тему: пожалуйста отпишите это в комментариях.

Автор: hubaxis

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js