Pull to refresh

Comments 22

Подскажите, пожалуйста, а вот так не будет работать?

.AddTransient<IFeed, Duck>()

Не силён в Microsoft.Extensions.DependencyInjection, но в других популярных контейнерах обычно это срабатывает.

То, что она будет реализовывать интерфейсы IFeed и IDuck - мы задаем через фабричный метод s => s.GetRequiredService() в противном случае, .NET будет создавать новый экземпляр на каждый тип интерфейса. 

По Вашему - везде где, например, инъекция в конструктор .ctor(IFeed feed) будет создаваться новый объект new Duck() в IFeed. Таков .NET.

Жаль, тот же SimpleInjector хоть и прост но достаточно умён для того, чтобы использовать предыдущую регистрацию Duck (в том случае, если она есть).

Мне кажется, дело тут не столько в том, насколько "умён" DI-фреймворк, а в том, что в разных ситуациях разработчику может требоваться разное поведение - иногда ему принципиально важно получить один и тот же экземпляр, внедряя зависимость по любому из его интерфейсов, а в других случаях - принципиально важно, чтобы создавался новый экземпляр. DI от Microsoft предоставляет возможность это сделать, а значит свою задачу решает (пусть и не самым интуитивным образом). Тем более что в подавляющем большинстве случаев это вообще не имеет значение, т.к. класс регистрируется по одному интерфейсу.

Вы знаете я перепроверил, и SimpleInjector в принципе так же себя ведёт - только кидает исключение о том, что для регистраций используются разные lifestyle, из-за чего каждый раз будут получены новые экземпляры (если не отключить автоверификацию контейнера). Жаль, очень жаль :)

А почему в коде везде record-ы? В этом есть какой-то профит в .NET 6 или это просто упрощение/случайность для примера?

Здрасте, если в конструктор передаю классы с дефолтным конструктором ,в этом DI появилась фишка как в Ninject без мэпинга? что бы не писать вот такие партянки

s.AddTransient<Duck>()

s.AddTransient<Duck2>()

s.AddTransient<Duck3>()

s.AddTransient<Duck4>()

Дефолтный - пустой? Не слышал, врят-ли.

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

ну могли бы добавить , чтобы не парится с расширениями и рефлектионами... как в ninject всё из коробки. ну или хотябы с помощью такого вызова:

provider.AddScoped(NotMapped.AutoCreateInstanceWithDefaultConstructors);

Возможно кто-то уже озадачился и сделал nuget с экстеншнами, как это часто бывает (или нет :))

как по мне, это уже не DI, а извращенный локатор сервисов.

Минус я поставил, если что) Сейчас объясню.

Почему извращенный, почему локатор? Смотрим в namespace DuckFeeding

Там нет ссылок на сервис локатор. Вообще никаких. Т.е. эта область кода не завязывается никак на сервисы. В этом и профит.

А точка сборки не отличается для локатора и DI, она в самом начале листинга. Это и инкапсулировано за реализацией, в .NET, я просто показал, как оно внутри работает.

Советую почитать http://sergeyteplyakov.blogspot.com/2013/03/di-service-locator.html

Почему извращенный, почему локатор? Смотрим в namespace DuckFeeding

А если не закрывать глаза, а посмотреть на программу целиком, то что мы видим?
Тот самый локатор и видим:

var sasha = provider.GetRequiredService();
Вообще, в самом языке C# никакого DI нет. А иллюзия поддержки DI создается конкретными фремворками, например — ASP .NET Core. И работает магия DI исключительно потому, что написанные пользователем классы(т.е. их конструкторы) и/или делегаты вызываются изнутри фреймворка, который имеет и использует ссылку на интерфейс IServiceProvider своего контейнера сервисов. Точно так же и ограниченная область, в пределах которой создаются объекты, предоставляющие сервисы с временем жизни Scoped, создается обращением к IServiceScope.ServiceProvider где-то внутри фреймворка.
У вас фреймворка нет, поэтому вам к контейнеру сервисов приходится обращаться явно.
Так что ваш несправедливый минус я убрал.

Вообще, в самом языке C# никакого DI нет. 

Ну нет так нет) Боб Мартин врал нам всё это время! 😱 А ведь мы ему верили... как же я был глуп и наивен! 😭

У вас фреймворка нет

Ну ок, нет так нет. Прям как в анекдоте "Товарищъ сеньор, я свой фреймворк не чувствую"🤕 Правда не ясно, как код компилится и запускается, без фреймворка, ну да ладно, "как-то магически" тоже вариант))

Scoped, создается обращением к IServiceScope.ServiceProvider где-то внутри фреймворка.

Ну ок, но зачем пересказывать статью в комментарии-критике к этой же статье) И не "где-то", а вполне конкретные места есть и я их даже видел)

DI - это я так сократил di ioc. В карму плюсы всем комментаторам, т.к. чот тут совсем тухло стало, я как бы 8 лет сюда не заходил) Но в коммент - минус, т.к. складывается ощущение, будто пишущие вообще не понимают, о чем пишут.

Ну нет так нет) Боб Мартин врал нам всё это время!

Вы хотите мне возразить? В таком случае покажите ту конструкцию (одну или несколько) языка C# из language reference которая реализует DI без явного вызова библиотечной функции/метода.
Правда не ясно, как код компилится и запускается, без фреймворка, ну да ладно, «как-то магически» тоже вариант))

Дык, я код компилил и запускал лет десять, наверное, прежде чем столкнулся с первым в своей жизни фреймворком (это была Borland Object Windows Library) — ту штуку, с помощью которой я это делал называли «система программирования» или ещё как-то примерно так. Ключевое отличие этой штуки от фреймворка — она не навязывала структуру программы.
Впрочем, если вам нравится называть систему программирования C# словом «фреймворк» — ваше право. Только, в таком случае перефразирую предыдущий вопрос: покажите, где в этом фреймворке живет DI. Классы из пространства имен Microsoft.Extensions.DependencyInjection не предлагать: про них по самому названию пространства имен понятно, что это — расширение, набор неких дополнительных возможностей.
Если вы хотите сказать при этом за IServiceProvider — что он из основной системной библиотеки — то я вам скажу, что в DI просто использовали подходящий интерфейс, который был задолго до DI (в древние времена он использовался для взаимодействия с COM).
Ну ок, но зачем пересказывать статью в комментарии-критике к этой же статье) И не «где-то», а вполне конкретные места есть и я их даже видел)

Дык, я тоже видел. А про то, как контейнер сервисов инициализуется в ASP.NET Core Generic Host Template даже статью писал здесь, на Хабре. Но я за другое хотел сказать: пользовательский код, который использует DI, типа контроллеров MVC — он вызывается изнутри фреймворка, и именно потому может невозбранно использовать магию DI для подстановки ссылок на реализации интерфейсов или классов в конструкторах и т.д.
Без ASP.NET Core или чего-то подобного, т.е. на чистом C#, такой код написать нельзя: где-то потребуется явно вызвать локатор сервисов.
Ну, а так в статье все хорошо — разве что, лично я не люблю уток ;-)
PS За плюс к карме благодарю.

Для начала объясню по понятиям:

DI - это внедрение зависимости. И пример: два класса, один без DI, второй с инъекцией в конструктор (DuckDi). Это реализация принципа IoC, когда зависимости мы отдаем на "внешний код", например, IoC контейнеру, которым является ServiceCollection. Он сам уже по тем или иным принципам выбирает, какой IFeed выдать утке.

class Duck{
  IFeed Feed
  Duck()
    {
    	Feed = new MyFeed();
    }
  }
}

class DuckDi{
  IFeed Feed;
  Duck(IFeed feed)
  {
  	Feed = feed;
  }
}

А вот пример применения паттерна ServiceLocator. Разница с Ioc Contaner в том, где определяются зависимости. Если в самом классе локатора - то, это локатор. Если где-то снаружи - то это контейнер. Как раз в namespace DuckFeed - контейнер снаружи.

class Duck{
 IFeed Feed;
  Duck(){
    var locator = new ServiceLocator();
   	Feed = locator.GetService<IFeed>() 
  }
}
class ServiceLocator{
 T GetService<T>(){
   return new ServFeed();
 }
}

Думаю теперь, когда я привел примеры, тема сабжа стала понятнее. Это как раз то, что я и пропустил в статье в начале, т.к. об этом написано куча статей и даже книг, тот самый Мартин - как раз одну такую написал, рекомендую к прочтению.

Фре́ймворк — программная платформа, определяющая структуру программной системы; программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта.

Без .NET Framework скомпилированный код C# даже не запуститься, поэтому он и Framework. Т.к. компилируется он в байт код для машины CLR, а не в прямые команды процессора.

Ответ на коммент ниже:

2. При вызове конструктора можно вместо ссылки на сервис указать ключевое слово, которое вызывает запрос в DI, типа так:
var obj=new Xyzzy(inject,inject).

Тут я вообще не понимаю, в чем смысл?

А система программирования находит и подставляет реализации сервисов из контейнера DI.

Она и так это делает, только без всяких атрибутов.

Подумал, что после предыдущего комментария меня опять могут понять неправильно, и решил пояснить, как могла бы выглядеть поддержка DI в самом языке C#.

1. Классы, которые реализуют сервисы, помечаются специальным атрибутом, типа так
[DependencyInjection]
class Abc: IAbc
{

}
Основываясь на этом атрибуте система программирования создает и наполняет контейнер DI.
2. При вызове конструктора можно вместо ссылки на сервис указать ключевое слово, которое вызывает запрос в DI, типа так:
var obj=new Xyzzy(inject,inject).
А система программирования находит и подставляет реализации сервисов из контейнера DI.

К сожалению, ничего подобного в C# не завезли.

Да просто в ветке смешались Dependency Inversion и Dependecy Injection)

А откуда вы такие идеи берете?

Можно было бы еще выделить один уровень абстракции и разделить, но просто больше кода будет.

Sign up to leave a comment.

Articles