Много раз я спрашивал себя, что какой IoC контейнер подойдет для того или иного проекта. Их производительность — это только одна сторона медали. Полное сравнение производительности можно найти здесь. Другая сторона медали — простота и скорость обучения. Так что я решил сравнить несколько контейнеров с этой точки зрения и взял Autofac, Simple Injector, StructureMap, Ninject, Unity, Castle Windsor. На мой взгляд, это наиболее популярные IoC контейнеры. Вы можете найти некоторые из них в списке 20 лучших пакетов NuGet и также я добавил другие по своим предпочтениям. Лично мне очень нравится Autofac и во время работы над этой статьей я еще больше утвердился, что это лучший выбор в большинстве случаев.


Здесь описываются основы IoC контейнеров, таких как конфигурация и регистрации компонентов. Есть мысль так же провести сравнение управления lifetime scope и продвинутых фитч. Примеры кода можно найти в репозитории LifetimeScopesExamples GitHub.


Документация


Во время работы над статьей мне необходимо было обращаться к документации некоторых из IoC. К сожалению, не каждый IoC контейнер имеет хорошее описание и я был вынужден искать решение в Google. Таким образом получилось следующее резюме.


Качество Комментарий
Autofac Супер Документация содержит всё, что необходимо. Дополнительно гуглить ничего не пришлось. Примеры понятные и полезные.
Simple Injector Хорошо Документация похожа на предыдущий, но выглядит чуть сырее. Несколько моментов пришлось погуглить, но решение быстро нашлось.
Structure Map Средне Не все случаи описаны в документации. Описания таких вещей, как регистрация с expression, property и method injections плохие. Необходимо было гуглить.
Ninject Есть Не все случаи описаны. Описания таких вещей, как регистрация с expression, property и method injections плохие. Необходимо было гуглить. Решения искались тяжело.
Unity Плохо Несмотря на количество текста, документация бесполезна, т.к. приходится разбираться в "простынях" текста. Все случаи пришлось гуглить, при этом их сложно найти.
Castle Windsor Средне Не все случаи описаны, или имеют непонятные примеры. Пришлось погуглить.

Ссылки на документация, чтобы вы сами убедились:



Конфигурация


Здесь я не рассматриваю конфигурацию посредством XML. Все примеры описывают частые случаи конфигурирования IoC контейнеров посредством их интерфейса. Здесь вы можете найти следующее:


  • Внедрение через конструкторы.
  • Внедрение с помощью свойств.
  • Внедрение с помощью методов.
  • Регистрация с помощью выражений, когда вы можете указать дополнительную логику по созданию.
  • Регистрация по соглашению, когда вы можете автоматически регистрировать всё (просто всё).
  • Регистрация с помощью модулей, когда вы можете указать класс, который инкапсулирует конфигурацию.

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


Модель объекта и тестового сценария


Для того чтобы проверить контейнеры IoC я создал простую модель. Есть несколько её модификаций, чтобы использовать property и method injection. Некоторые из IoC контейнеров требуют использования специальных атрибутов, чтобы инициализировать через свойства или методы. Я явно написал об этом в каждой секции.


/*************
* Interfaces *
**************/
public interface IAuthorRepository{
    IList<Book> GetBooks(Author parent);
}

public interface IBookRepository{
    IList<Book> FindByParent(int parentId);
}

public interface ILog{
    void Write(string text);
}
/***********************************************
* Implementation for injection via constructor *
***********************************************/
internal class AuthorRepositoryCtro : IAuthorRepository{
    private readonly IBookRepository _bookRepository;
    private readonly ILog _log;
    public AuthorRepositoryCtro(ILog log, IBookRepository bookRepository)    {
        _log = log;
        _bookRepository = bookRepository;
    }
    public IList<Book> GetBooks(Author parent)    {
        _log.Write("AuthorRepository:GetBooks()");
        return _bookRepository.FindByParent(parent.Id);
}}

internal class BookRepositoryCtro : IBookRepository{
    private readonly ILog _log;
    public BookRepositoryCtro(ILog log)    {
        _log = log;
    }
    public IList<Book> FindByParent(int parentId)    {
        _log.Write("BookRepository:FindByParent()");
        return null;
}}

internal class ConsoleLog : ILog{
    public void Write(string text)    {
        Console.WriteLine("{0}", text);
}}

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


private static void Main(string[] args){
    var resolver = Configuration.Simple();    
    /***********************************************************
     * both resolving use the same method of IBookRepository   *
     * it depends on lifetime scope configuration whether ILog *
     * would be the same instance (the number in the output    *
     * shows the number of the instance)                       *
     ***********************************************************/
    // the 1st resolving
    var books = resolver.Resolve<IAuthorRepository>().GetBooks(new Author());
    // the 2nd resolving
    resolver.Resolve<IBookRepository>().FindByParent(0);
    System.Console.WriteLine("Press any key...");
    System.Console.ReadKey();
}

Внедрение через конструкторы


Конфигурация для этого не требует каких-либо специальных атрибутов или имен в своем базовом варианте.


Autofac


var builder = new ContainerBuilder();
builder.RegisterType<AuthorRepositoryCtro>().As<IAuthorRepository>();
builder.RegisterType<BookRepositoryCtro>().As<IBookRepository>();
builder.RegisterType<ConsoleLog>().As<ILog>();
var container = builder.Build();

Simple Injector


var container = new Container();
container.Register<IAuthorRepository, AuthorRepositoryCtro>();
container.Register<IBookRepository, BookRepositoryCtro>();
container.Register<ILog, ConsoleLog>();

StructureMap


var container = new Container();
container.Configure(c =>
{
    c.For<IAuthorRepository>().Use<AuthorRepositoryCtro>();
    c.For<IBookRepository>().Use<BookRepositoryCtro>();
    c.For<ILog>().Use<ConsoleLog>();
});

Ninject


var container = new StandardKernel();
container.Bind<IAuthorRepository>().To<AuthorRepositoryCtro>();
container.Bind<IBookRepository>().To<BookRepositoryCtro>();
container.Bind<ILog>().To<ConsoleLog>();

Unity


var container = new UnityContainer();
container.RegisterType<IAuthorRepository, AuthorRepositoryCtro>();
container.RegisterType<IBookRepository, BookRepositoryCtro>();
container.RegisterType<ILog, ConsoleLog>();

Castle Windsor


var container = new WindsorContainer();
container.Register(Component.For<IAuthorRepository>().ImplementedBy<AuthorRepositoryCtro>());
container.Register(Component.For<IBookRepository>().ImplementedBy<BookRepositoryCtro>());
container.Register(Component.For<ILog>().ImplementedBy<ConsoleLog>());

Внедрение с помощью свойств


Некоторые IoC контейнеры требуют использования специальных атрибутов, которые помогают распознавать свойства для инициализации. Мне лично не нравится этот подход, поскольку модель объекта и IoC контейнер становится сильно связаны. Ninject требует использования атрибута [Inject], Unity требует атрибут [Dependency]. В то же время Castle Windsor не требует ничего, чтобы инициализировать свойства, т.к. у него это происходит по умолчанию.


Autofac


var builder = new ContainerBuilder();
builder.RegisterType<AuthorRepositoryCtro>().As<IAuthorRepository>().PropertiesAutowired();
builder.RegisterType<BookRepositoryCtro>().As<IBookRepository>().PropertiesAutowired();
builder.RegisterType<ConsoleLog>().As<ILog>();
var container = builder.Build();

Simple Injector


У него нет встроенных возможностей для этого, но можно использовать конфигурацию с помощью экспрешнов.

StructureMap


var container = new Container();
container.Configure(c =>
{
    c.For<IAuthorRepository>().Use<AuthorRepositoryProp>();
    c.For<IBookRepository>().Use<BookRepositoryProp>();
    c.For<ILog>().Use(() => new ConsoleLog());
    c.Policies.SetAllProperties(x => {
        x.OfType<IAuthorRepository>();
        x.OfType<IBookRepository>();
        x.OfType<ILog>();
    });
});

Ninject


var container = new StandardKernel();
container.Bind<IAuthorRepository>().To<AuthorRepositoryProp>();
container.Bind<IBookRepository>().To<BookRepositoryProp>();
container.Bind<ILog>().To<ConsoleLog>();

Unity


var container = new UnityContainer();
container.RegisterType<IAuthorRepository, AuthorRepositoryProp>();
container.RegisterType<IBookRepository, BookRepositoryProp>();
container.RegisterType<ILog, ConsoleLog>();

Castle Windsor


var container = new WindsorContainer();
container.Register(Component.For<IAuthorRepository>().ImplementedBy<AuthorRepositoryProp>());
container.Register(Component.For<IBookRepository>().ImplementedBy<BookRepositoryProp>());
container.Register(Component.For<ILog>().ImplementedBy<ConsoleLog>());

Внедрение с помощью методов


Данный подход, как и предыдущий, может помочь с циклическими ссылками. С другой стороны, это вносит еще один момент, который следует избегать. В нескольких словах API не дает никакого намека на то, что такая инициализация требуется для полноценного создания объекта. Тут чуть подробнее о temporal coupling.


Тут так же некоторые контейнеры IoC требуют использования специальных атрибутов с теми же недостатками. Ninject требует атрибут [Inject] для методов. Unity требует использования атрибута [InjectionMethod]. Все методы, помеченные такими атрибутами, будут выполнены в моментсоздания объекта контейнером.


Autofac


var builder = new ContainerBuilder();
builder.Register(c => {
    var rep = new AuthorRepositoryMtd();
    rep.SetDependencies(c.Resolve<ILog>(), c.Resolve<IBookRepository>());
    return rep;
}).As<IAuthorRepository>();
builder.Register(c => {
    var rep = new BookRepositoryMtd();
    rep.SetLog(c.Resolve<ILog>());
    return rep;
}).As<IBookRepository>();
builder.Register(c => new ConsoleLog()).As<ILog>();
var container = builder.Build();

Simple Injector


var container = new Container();
container.Register<IAuthorRepository>(() => {
    var rep = new AuthorRepositoryMtd();
    rep.SetDependencies(container.GetInstance<ILog>(), container.GetInstance<IBookRepository>());
    return rep;
});
container.Register<IBookRepository>(() => {
    var rep = new BookRepositoryMtd();
    rep.SetLog(container.GetInstance<ILog>());
    return rep;
});
container.Register<ILog>(() => new ConsoleLog());

StructureMap


var container = new Container();
container.Configure(c => {
    c.For<IAuthorRepository>().Use<AuthorRepositoryMtd>()
        .OnCreation((c, o) => o.SetDependencies(c.GetInstance<ILog>(), c.GetInstance<IBookRepository>()));
    c.For<IBookRepository>().Use<BookRepositoryMtd>()
        .OnCreation((c, o) => o.SetLog(c.GetInstance<ILog>()));
    c.For<ILog>().Use<ConsoleLog>();
});

Ninject


var container = new StandardKernel();
container.Bind<IAuthorRepository>().To<AuthorRepositoryMtd>()
    .OnActivation((c, o) => o.SetDependencies(c.Kernel.Get<ILog>(), c.Kernel.Get<IBookRepository>()));
container.Bind<IBookRepository>().To<BookRepositoryMtd>()
    .OnActivation((c, o) => o.SetLog(c.Kernel.Get<ILog>()));
container.Bind<ILog>().To<ConsoleLog>();

Unity


var container = new UnityContainer();
container.RegisterType<IAuthorRepository, AuthorRepositoryMtd>();
container.RegisterType<IBookRepository, BookRepositoryMtd>();
container.RegisterType<ILog, ConsoleLog>();

Castle Windsor


var container = new WindsorContainer();
container.Register(Component.For<IAuthorRepository>().ImplementedBy<AuthorRepositoryMtd>()
    .OnCreate((c, o) => ((AuthorRepositoryMtd) o).SetDependencies(c.Resolve<ILog>(), c.Resolve<IBookRepository>())));
container.Register(Component.For<IBookRepository>().ImplementedBy<BookRepositoryMtd>()
    .OnCreate((c, o) => ((BookRepositoryMtd)o).SetLog(c.Resolve<ILog>())));
container.Register(Component.For<ILog>().ImplementedBy<ConsoleLog>());

Регистрация с помощью выражений


Большинство случаев в предыдущих секциях являются ни чем иным, как регистрация с помощью лямбда-выражений или делегатов. Такой способ регистрации поможет вам добавить некоторую логику в тот момент, когда создаются объекты, но это не динамический подход. Для динамики следует использовать параметризованную регистрацию, чтобы иметь возможность в run-time создавать разные реализации для одного компонента.


Autofac


var builder = new ContainerBuilder();
builder.Register(c => new AuthorRepositoryCtro(c.Resolve<ILog>(), c.Resolve<IBookRepository>()))
       .As<IAuthorRepository>();
builder.Register(c => new BookRepositoryCtro(c.Resolve<ILog>()))
       .As<IBookRepository>();
builder.Register(c => new ConsoleLog()).As<ILog>();
var container = builder.Build();

Simple Injector


var container = new Container();
container.Register<IAuthorRepository>(() => 
      new AuthorRepositoryCtro(container.GetInstance<ILog>(), container.GetInstance<IBookRepository>()));
container.Register<IBookRepository>(() =>
      new BookRepositoryCtro(container.GetInstance<ILog>()));
container.Register<ILog>(() => new ConsoleLog());

StructureMap


var container = new Container();
container.Configure(r => {
    r.For<IAuthorRepository>()
         .Use(c => new AuthorRepositoryCtro(c.GetInstance<ILog>(), c.GetInstance<IBookRepository>()));
    r.For<IBookRepository>()
         .Use(c => new BookRepositoryCtro(c.GetInstance<ILog>()));
    r.For<ILog>().Use(() => new ConsoleLog());
});

Ninject


var container = new StandardKernel();
container.Bind<IAuthorRepository>().ToConstructor(c => 
          new AuthorRepositoryCtro(c.Inject<ILog>(), c.Inject<IBookRepository>()));
container.Bind<IBookRepository>().ToConstructor(c =>
          new BookRepositoryCtro(c.Inject<ILog>()));
container.Bind<ILog>().ToConstructor(c => new ConsoleLog());

или


container.Bind<IAuthorRepository>().ToMethod(c => 
              new AuthorRepositoryCtro(c.Kernel.Get<ILog>(), c.Kernel.Get<IBookRepository>()));
container.Bind<IBookRepository>().ToMethod(c =>
              new BookRepositoryCtro(c.Kernel.Get<ILog>()));
container.Bind<ILog>().ToMethod(c => new ConsoleLog());

Unity


var container = new UnityContainer();
container.RegisterType<IAuthorRepository>(new InjectionFactory(c =>
        new AuthorRepositoryCtro(c.Resolve<ILog>(), c.Resolve<IBookRepository>())));
container.RegisterType<IBookRepository>(new InjectionFactory(c =>
                                      new BookRepositoryCtro(c.Resolve<ILog>())));
container.RegisterType<ILog>(new InjectionFactory(c => new ConsoleLog()));

Castle Windsor


var container = new WindsorContainer();
container.Register(Component.For<IAuthorRepository>()
        .UsingFactoryMethod(c => new AuthorRepositoryCtro(c.Resolve<ILog>(), c.Resolve<IBookRepository>())));
container.Register(Component.For<IBookRepository>()
                    .UsingFactoryMethod(c => new BookRepositoryCtro(c.Resolve<ILog>())));
container.Register(Component.For<ILog>().UsingFactoryMethod(c => new ConsoleLog()));

Ninject имеет различия между конфигурированием с помощью ToMethod и ToConstructor. В нескольких словах, когда вы используете ToContructor вы также можете использовать условия. Следующая конфигурация не будет работать для ToMethod.


Bind<IFoo>().To<Foo1>().WhenInjectedInto<Service1>();
Bind<IFoo>().To<Foo2>().WhenInjectedInto<Service2>();

Регистрация по соглашению


В некоторых случаях вам не нужно писать код конфигурации вообще. Общий сценарий выглядит следующим образом: сканирование assembly для поиска нужных типов, извлечение их интерфейсов и регистрация их в контейнере, как пара интерфейс-реализация. Это может быть полезно для очень больших проектов, но может быть сложно для разработчика незнакомово с проектом. Следует помнить несколько моментов.


Autofac регистрирует все возможные варианты реализаций и сохраняет их во внутреннем массиве. В соответствии с документацией, он будет использовать самый последний вариант для резолва по умолчанию. Simple Injector не имеет готовых методов для автоматической регистрации. Вы должны сделать это вручную (пример ниже). StructureMap и Unity требуют public классы имплементаций, т.к. их сканеры другие не видят. Ninject требует дополнительный NuGet пакет Ninject.Extensions.Conventions. И он так же требует public-классы имплементаций.


Autofac


var builder = new ContainerBuilder();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces();
var container = builder.Build();

Simple Injector


var container = new Container();
var repositoryAssembly = Assembly.GetExecutingAssembly();
var implementationTypes = from type in repositoryAssembly.GetTypes()
    where type.FullName.Contains("Repositories.Constructors")
          || type.GetInterfaces().Contains(typeof (ILog))
    select type;
var registrations =
    from type in implementationTypes
    select new { Service = type.GetInterfaces().Single(), Implementation = type };
foreach (var reg in registrations)
    container.Register(reg.Service, reg.Implementation);

StructureMap


var container = new Container();
container.Configure(c => c.Scan(x => {
    x.TheCallingAssembly();
    x.RegisterConcreteTypesAgainstTheFirstInterface();
}));

Ninject


var container = new StandardKernel();
container.Bind(x => x.FromThisAssembly().SelectAllClasses().BindDefaultInterfaces());

Unity


var container = new UnityContainer();
container.RegisterTypes(
    AllClasses.FromAssemblies(Assembly.GetExecutingAssembly()), 
    WithMappings.FromAllInterfaces);

Castle Windsor


var container = new WindsorContainer();
container.Register(Classes.FromAssembly(Assembly.GetExecutingAssembly())
    .IncludeNonPublicTypes()
    .Pick()
    .WithService.DefaultInterfaces());

Регистрация с помощью модулей


Модули могут помочь вам разделить вашу конфигурацию. Вы можете сгруппировать их по контексту (доступ к данным, бизнес-объекты) или по назначению (production, test). Некоторые из контейнеров IoC может сканировать сборки в поисках своих модулей. Тут я описал основной способ их использования.


Autofac


public class ImplementationModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<AuthorRepositoryCtro>().As<IAuthorRepository>();
        builder.RegisterType<BookRepositoryCtro>().As<IBookRepository>();
        builder.RegisterType<ConsoleLog>().As<ILog>();
    }
}
/*********
 * usage *
 *********/
var builder = new ContainerBuilder();
builder.RegisterModule(new ImplementationModule());
var container = builder.Build();

Simple Injector


Ничего такого нет.

StructureMap


public class ImplementationModule : Registry
{
    public ImplementationModule()
    {
        For<IAuthorRepository>().Use<AuthorRepositoryCtro>();
        For<IBookRepository>().Use<BookRepositoryCtro>();
        For<ILog>().Use<ConsoleLog>();
    }
}
/*********
 * usage *
 *********/
var registry = new Registry();
registry.IncludeRegistry<ImplementationModule>();
var container = new Container(registry);

Ninject


public class ImplementationModule : NinjectModule
{
    public override void Load()
    {
        Bind<IAuthorRepository>().To<AuthorRepositoryCtro>();
        Bind<IBookRepository>().To<BookRepositoryCtro>();
        Bind<ILog>().To<ConsoleLog>();
    }
}
/*********
 * usage *
 *********/
var container = new StandardKernel(new ImplementationModule());

Unity


public class ImplementationModule : UnityContainerExtension
{
    protected override void Initialize()
    {
        Container.RegisterType<IAuthorRepository, AuthorRepositoryCtro>();
        Container.RegisterType<IBookRepository, BookRepositoryCtro>();
        Container.RegisterType<ILog, ConsoleLog>();
    }
}
/*********
 * usage *
 *********/
var container = new UnityContainer();
container.AddNewExtension<ImplementationModule>();

Castle Windsor


public class ImplementationModule : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<IAuthorRepository>().ImplementedBy<AuthorRepositoryCtro>());
        container.Register(Component.For<IBookRepository>().ImplementedBy<BookRepositoryCtro>());
        container.Register(Component.For<ILog>().ImplementedBy<ConsoleLog>());
    }
}
/*********
 * usage *
 *********/
var container = new WindsorContainer();
container.Install(new ImplementationModule());

PS


В следующих текстах рассмотрю lifetime scope management и advanced features.

Поделиться с друзьями
-->

Комментарии (15)


  1. DarkOrion
    31.05.2016 10:52
    -2

    Это чувство, когда в статье про IoC-контейнеры нет ни одного xml-конфига.
    PS: а можно еще сравнение со Spring вне конкурса? В районе 2011 писал одно приложение сразу на java и шарпе, удивился насколько спринг тогда обгонял unity.


    1. ETman
      31.05.2016 10:54
      +2

      XML умышленно не стал рассматривать, т.к. для этого нужна отдельная статья. В добавок Spring не хочу, т.к. он мне так же не нравится, как и Unity.


    1. ZOXEXIVO
      31.05.2016 11:04
      +1

      XML-это тормоза. Понятно, что это гибко, но уже давно все XML конфиги удалены и забыты.
      Зачем вам такая гибкость? Заменить CacheService с обычного на распределенный? Это примерно как с базой данных — ой, мы используем сотни абстракций, репозиториев и.т.п. на случай, если нам нужно будет сменить базу, а на деле это нужно в 0.0001% случаев.
      Проще иметь Setup.cs на каждую сборку где статически описаны все зависимости (можно с группировкой) и все эти Setup тоже вызываются из кода без рефлексии и.т.п. Все четко и понятно.


      1. Razaz
        31.05.2016 11:41

        Это кстати решается через те же автофабрики типа IIndex<Key,Service>. Можно куда угодно настройку вкрутить что бы нужную имплементацию получать.


      1. DarkOrion
        31.05.2016 18:45

        C такой логикой IoC-контейнер не нужен, да и вообще вынос любых настроек\ресурсов в файл.
        Если:
        1) Вы готовы ради того, чтобы выводить 11, а не 10 строк на страницу лезть в код и пересобирать ПО;
        2) Вам нужно, чтобы приложение запускалось на пару секунд быстрее (на время чтения конфига, ведь на самом деле тормозит не XML, а создание\инициализация объектов);
        То да, пишите Setup.cs\Init.cs, создавайте там все объекты руками и руками же собирайте зависимости.

        Если вам нужно гибкое приложение — то IoC-контейнер, настройки, конфиги.

        Наверное у нас с вами разные кейсы использования IoC, я вижу да, что люди уходят от xml-конфигов, но часто это просто размазывание его аннотациями по всему коду (что несколько убивает его смысла, на мой взгляд).


        1. Razaz
          31.05.2016 20:39
          +4

          В принципе выставлять весь конфиг наружу — плохая идея. Можно выставлять настройки тех компонент, которые можно менять. Остальное нефиг трогать.
          Смысл DI в IoC, а не в конфигах всего через Xml.


        1. areht
          31.05.2016 21:19

          > 1) Вы готовы ради того, чтобы выводить 11, а не 10 строк на страницу лезть в код и пересобирать ПО;

          Я для этого IoC то заводить не готов.


  1. goncharov_a_v
    31.05.2016 14:30

    Если исходить из примеров, а так же из логики что «тот лучше, для правильного использования которого надо читать меньше документации», то Unity лучший из представленных: почти во всех примерах используется один способ регистрации. Далее, способ регистрации и внедрения важны, но есть и еще один, не менее важный, вопрос — управление жизненным циклом объектов, особенно в части IDisposable.


    1. ETman
      31.05.2016 14:32

      В следующей будет обзор.


  1. correy
    31.05.2016 14:30

    После «Autofac Качество Супер» читать не стал.


    1. ETman
      31.05.2016 14:31

      аргументируйте, плиз?


    1. Razaz
      31.05.2016 16:35
      +1

      В принципе сейчас это самый бодрый DI контейнер. Который может работать как от простенького Register -> ConstructorInjection до разных наворотов с владением объектом(Диспоузеры, Owned и тд.), скоупами, автоматическими фабриками, работой с метаданными и любыми другими хотелками. Например EventBus на Autofac+Castle Dynamic Proxy.
      Плюс пачка готовых интеграций ко всему чему только можно.


  1. just_dmitry
    31.05.2016 18:04

    Добавьте пожалуйста к сравнению DI, встроенный в ASP.NET Core.


  1. djank
    31.05.2016 18:54
    +1

    Посмотрите ещё в сторону DryIoc — лидера по производительности в обзоре, на который вы дали ссылку. Приличная документация, качественный код, покрытый тестами, автор (белорус, насколько я понял) поддерживает проект уже довольно долгое время и оперативно реагирует на баг-репорты, поставка в виде дллэлки или файлом в проект. В моём проекте эта библиотека помогла тем, что смогла «решить» довольно запутанный граф зависимостей с generics и множественными конструкторами (конечно, это промах в архитектуре, но всё же). Короче, маст, как говорится, хэв.


    1. Razaz
      31.05.2016 20:50
      +1

      Ему бы на гитхаб, а то даже следить за проектом неудобно.
      И вот это немного дико выглядит…
      di.Register(Made.Of(() => GetSession(Arg.Of())));
      di.WithWebApi(config);