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

    В настоящее время трудно представить себе приложение на 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 за короткое время. Если я недостаточно осветил какую-либо тему: пожалуйста отпишите это в комментариях.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 4

      0
      Собственно поэтому в своем проекте используем autofaq — почти все из коробки. Странно что он не на столько распространен как остальные библиотеки.
      Еще есть подобный код (правда проект mvc2 и относительно «старый» autofaq):
                  Assembly executingAssembly = Assembly.GetExecutingAssembly();
                  builder.RegisterControllers(executingAssembly).HttpRequestScoped();
      
                  builder.RegisterAssemblyTypes(
                      typeof (SomeClass).Assembly,
                      typeof (OtherClassFromDifferentAssembly).Assembly,
                      typeof (AnotherClassOfThirdAssembly).Assembly,
                      executingAssembly);
      
                  builder.RegisterAssemblyTypes(executingAssembly)
                      .Where(t => t.Name.EndsWith("Repository") ||
                                  (t.FullName.StartsWith("OurApplication.Models") &&
                                   t.GetInterfaces().Any()))
                      .AsImplementedInterfaces();
      

      после Unity — просто счастье
        0
        Собственно сейчас что Unity, что Ninject, что Autofack представляют примерно одинаковый функционал. Похожая реализация на Unity.
        var servicesAssembly = Assembly.GetAssembly(typeof (IService));
        
                    foreach (var type in servicesAssembly.GetTypes()
                        .Where(t => t.Name.EndsWith("Service") && t.IsClass))
                    {
                        var interfaceType = type.GetInterfaces().FirstOrDefault(i => i.Name.EndsWith("Service") &&                                                                             !(i.Namespace ?? string.Empty).EndsWith("Base"));
                        if (interfaceType != null)
                        {
                            container.RegisterType(interfaceType, type);
                        }
                    }
        
          0
          в юнити меня в основном раздражало задание Lifetime Scope. Да и в целом количество действий, которые необходимо предпринять. В приведенном мной коде это, например, первые 2 строчки.
          Возможно в новой версии что-то изменилось (давно не следил).
            0
            Я соглашусь с вами, все же я тоже использую autofack, ни сколько из-за возможностей, сколько из-за качества исполнения этих возможностей. В данной ситуации Unity смотрится убого. Но не смотря на это возможности Unity, Ninject, Autofack примерно равны.

        Only users with full accounts can post comments. Log in, please.