Сравнение конфигураций Dependency Injection фреймворков

    Много раз я спрашивал себя, что какой 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

      –2
      Это чувство, когда в статье про IoC-контейнеры нет ни одного xml-конфига.
      PS: а можно еще сравнение со Spring вне конкурса? В районе 2011 писал одно приложение сразу на java и шарпе, удивился насколько спринг тогда обгонял unity.
        +2
        XML умышленно не стал рассматривать, т.к. для этого нужна отдельная статья. В добавок Spring не хочу, т.к. он мне так же не нравится, как и Unity.
          +1
          XML-это тормоза. Понятно, что это гибко, но уже давно все XML конфиги удалены и забыты.
          Зачем вам такая гибкость? Заменить CacheService с обычного на распределенный? Это примерно как с базой данных — ой, мы используем сотни абстракций, репозиториев и.т.п. на случай, если нам нужно будет сменить базу, а на деле это нужно в 0.0001% случаев.
          Проще иметь Setup.cs на каждую сборку где статически описаны все зависимости (можно с группировкой) и все эти Setup тоже вызываются из кода без рефлексии и.т.п. Все четко и понятно.
            0
            Это кстати решается через те же автофабрики типа IIndex<Key,Service>. Можно куда угодно настройку вкрутить что бы нужную имплементацию получать.
              0
              C такой логикой IoC-контейнер не нужен, да и вообще вынос любых настроек\ресурсов в файл.
              Если:
              1) Вы готовы ради того, чтобы выводить 11, а не 10 строк на страницу лезть в код и пересобирать ПО;
              2) Вам нужно, чтобы приложение запускалось на пару секунд быстрее (на время чтения конфига, ведь на самом деле тормозит не XML, а создание\инициализация объектов);
              То да, пишите Setup.cs\Init.cs, создавайте там все объекты руками и руками же собирайте зависимости.

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

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

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

                  Я для этого IoC то заводить не готов.
              0
              Если исходить из примеров, а так же из логики что «тот лучше, для правильного использования которого надо читать меньше документации», то Unity лучший из представленных: почти во всех примерах используется один способ регистрации. Далее, способ регистрации и внедрения важны, но есть и еще один, не менее важный, вопрос — управление жизненным циклом объектов, особенно в части IDisposable.
                0
                В следующей будет обзор.
                0
                После «Autofac Качество Супер» читать не стал.
                  0
                  аргументируйте, плиз?
                    +1
                    В принципе сейчас это самый бодрый DI контейнер. Который может работать как от простенького Register -> ConstructorInjection до разных наворотов с владением объектом(Диспоузеры, Owned и тд.), скоупами, автоматическими фабриками, работой с метаданными и любыми другими хотелками. Например EventBus на Autofac+Castle Dynamic Proxy.
                    Плюс пачка готовых интеграций ко всему чему только можно.
                    0
                    Добавьте пожалуйста к сравнению DI, встроенный в ASP.NET Core.
                      +1
                      Посмотрите ещё в сторону DryIoc — лидера по производительности в обзоре, на который вы дали ссылку. Приличная документация, качественный код, покрытый тестами, автор (белорус, насколько я понял) поддерживает проект уже довольно долгое время и оперативно реагирует на баг-репорты, поставка в виде дллэлки или файлом в проект. В моём проекте эта библиотека помогла тем, что смогла «решить» довольно запутанный граф зависимостей с generics и множественными конструкторами (конечно, это промах в архитектуре, но всё же). Короче, маст, как говорится, хэв.
                        +1
                        Ему бы на гитхаб, а то даже следить за проектом неудобно.
                        И вот это немного дико выглядит…
                        di.Register(Made.Of(() => GetSession(Arg.Of())));
                        di.WithWebApi(config);

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое