LearnAspNet / LearnLessons

GNU General Public License v3.0
0 stars 1 forks source link

Lesson 3. Механизм DI. #6

Open Jenausmax opened 2 years ago

Jenausmax commented 2 years ago

Изучение механизмов DI. Ответить на вопросы.

xJennyx commented 2 years ago

Что такое DI? Это механизм, позволяющий сделать взаимодействующие в приложении объекты слабосвязанными. Эти объекты связаны между собой через абстракции, например, через интерфейсы. Это позволяет всей системе быть более гибкой, адаптируемой и расширяемой. Главным моментом данного механизма является зависимость - некоторая сущность, от которой зависит другая сущность.

xJennyx commented 2 years ago

Зачем использовать механизм DI? При использовании DI, например, при связывании двух классов. Мы можем использовать интерфейс, который будет использоваться в одном из классов, это снимает зависимость одного класса от другого. В интерфейсе будет использоваться абстрактный метод. И уже в классе будет вызываться объект этого интерфейса. Т.е. один из классов перестает зависеть от конкретной реализации другого.

xJennyx commented 2 years ago

Жизненный цикл зависимостей. Какие бывают. С точки зрения жизненного цикла сервисы(зависимости) могут представлять один из следующих типов: 1) Transient: при каждом обращении к сервису создается новый объект сервиса. В течении одного запроса может быть несколько обращений к сервису, соответственно при каждом обращении будет создаваться новый объект. Подобная модель жизненного цикла наиболее подходит для легковесных сервисов, которые не хранят данных о состоянии 2) Scoped: для каждого запроса создается свой объект сервиса. То есть если в течение одного запроса есть несколько обращений к одному сервису, то при всех этих обращениях будет использоваться один и тот же объект сервиса 3) Singleton: объект сервиса создается при первом обращении к нему, все последующие запросы используют один и тот же ранее созданный объект сервиса.

Для создания каждого типа сервиса предназначен соответствующий метод: AddTransient(), AddScoped(), AddSingleton().

xJennyx commented 2 years ago

Как подключить в DI несколько зависимостей одного сервиса? Таких способов несколько. Через фабрику services.AddFactory<IProcessor, string>() .Add("A") .Add("B"); public MyClass(IFactory<IProcessor, string> processorFactory) { var x = "A"; //some runtime variable to select which object to create var processor = processorFactory.Create(x); }

public class FactoryBuilder<I, P> where I : class { private readonly IServiceCollection _services; private readonly FactoryTypes<I, P> _factoryTypes;

        public FactoryBuilder(IServiceCollection services)
        {
                    _services = services;
                    _factoryTypes = new FactoryTypes<I, P>();
        }
        public FactoryBuilder<I, P> Add<T>(P p) where T : class, I
        {
                     _factoryTypes.ServiceList.Add(p, typeof(T));
                     _services.AddSingleton(_factoryTypes);
                     _services.AddTransient<T>();
                      return this;
        }

}

public class FactoryTypes<I, P> where I : class { public Dictionary<P, Type> ServiceList { get; set; } = new Dictionary<P, Type> (); }

public interface IFactory<I, P> { I Create(P p); } public class Factory<I, P> : IFactory<I, P> where I : class { private readonly IServiceProvider _serviceProvider; private readonly FactoryTypes<I, P> _factoryTypes;

       public Factory(IServiceProvider serviceProvider, FactoryTypes<I, P> factoryTypes)
      {
                   _serviceProvider = serviceProvider; _factoryTypes = factoryTypes;
      }
      public I Create(P p)
      {
                 return (I)_serviceProvider.GetService(_factoryTypes.ServiceList[p]);
      }

} //расширение namespace Microsoft.Extensions.DependencyInjection { public static class DependencyExtensions { public static IServiceCollection AddFactory<I, P>(this IServiceCollection services, Action<FactoryBuilder<I, P>> builder) where I : class { services.AddTransient<IFactory<I, P>, Factory<I, P>>(); var factoryBuilder = new FactoryBuilder<I, P>(services); builder(factoryBuilder); return services; } } }

xJennyx commented 2 years ago

Через IEnumerable public interface IFactory<I, P> { I Create(P p); } public class Factory<I, P> : IFactory<I, P> where I : class { private readonly IServiceProvider _serviceProvider; private readonly FactoryTypes<I, P> _factoryTypes; public Factory(IServiceProvider serviceProvider, FactoryTypes<I, P> factoryTypes) { _serviceProvider = serviceProvider; _factoryTypes = factoryTypes; } public I Create(P p) { return (I)_serviceProvider.GetService(_factoryTypes.ServiceList[p]); } } //расширение namespace Microsoft.Extensions.DependencyInjection { public static class DependencyExtensions { public static IServiceCollection AddFactory<I, P>(this IServiceCollection services, Action<FactoryBuilder<I, P>> builder) where I : class { services.AddTransient<IFactory<I, P>, Factory<I, P>>(); var factoryBuilder = new FactoryBuilder<I, P> (services); builder(factoryBuilder); return services; } } } // In Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddScoped(IService, ServiceA); services.AddScoped(IService, ServiceB); services.AddScoped(IService, ServiceC); } // Any class that uses the service(s) public class Consumer { private readonly IEnumerable _myServices; public Consumer(IEnumerable myServices) { _myServices = myServices; } public UseServiceA() { var serviceA = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceA)); serviceA.DoTheThing(); } public UseServiceB() { var serviceB = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceB)); serviceB.DoTheThing(); } public UseServiceC() { var serviceC = _myServices.FirstOrDefault(t => t.GetType() == typeof(ServiceC)); serviceC.DoTheThing(); } }

xJennyx commented 2 years ago

через типизированный интерфейс public interface IMyInterface where T : class, IMyInterface

xJennyx commented 2 years ago

Какие сервисы можно внедрять друг в друга без ошибок?

Можно в Transient сервис внедрять как Scoped так и Transient