#c# #dependency-injection #.net-6.0
Вопрос:
С помощью Autofac довольно легко создать дочернюю область контейнера и зарегистрировать дополнительные службы. Как бы я добился того же с внедрением зависимостей .net?
То IServiceProvider
, что я пытался внедрить в класс, которому необходимо создавать дочерние контейнеры, предлагает только CreateScope()
метод, который создает IServiceScope
, что у agian есть только ServiceProvider
свойство и нет возможности зарегистрировать дополнительные службы. Есть ли что-нибудь еще, что я должен ввести, что позволило бы мне зарегистрировать больше сервисов в контейнере?
Ответ №1:
Я не знаю, как работает Autofac, но я могу объяснить вам, как работает Microsoft DI.
Прежде всего, контейнер Microsoft DI разработан таким образом, чтобы у вас было две основные абстракции:
IServiceCollection
: это объект, который вы используете для регистрации служб в своих приложениях. Думайте об этом как об объекте построителя для фактического контейнера DI.ServiceProvider
: это фактический контейнер DI, который вы получаете отIServiceCollection
объекта, вызывая методIServiceCollection.BuildServiceProvider
расширения. Поведение этого объекта описываетсяIServiceProvider
интерфейсом (ServiceProvider
класс реализуетIServiceProvider
интерфейс).
Таким образом, работа с контейнером DI состоит из двух этапов: сначала вам нужен IServiceCollection
объект, чтобы вы могли регистрировать службы, указав тип реализации и срок службы (переходный, область действия или одноэлементный), затем вы можете создать ServiceProvider
и использовать его для разрешения служб в вашем приложении. Конкретный тип, фактически используемый для IServiceCollection
интерфейса, — это ServiceCollection
класс.
Когда вы создаете коллекцию служб для получения ServiceProvider
экземпляра, вы фактически получаете корневой контейнер для своего приложения. Он называется корневым контейнером, потому что вы можете создать иерархию поставщиков услуг, имеющих свой корень в корневом контейнере приложения.
Учитывая корневой контейнер для вашего приложения, для создания дочернего контейнера вам необходимо создать область, которая в основном используется для разрешения служб. Каждая область имеет свой собственный контейнер, который представляет собой объект, реализующий IServiceProvider
интерфейс, который можно использовать для разрешения служб внутри этой области.
Это общая схема для этого:
// create the IServiceCollection instance var services = new ServiceCollection(); // register services on the IServiceCollection instance services.AddSingletonlt;IFooService, FooServicegt;(); services.AddScopedlt;IBarService, BarServicegt;(); services.AddTransientlt;IBuzzService, BuzzServicegt;(); // create the root container using var rootContaier = services.BuildServiceProvider(); // create a scope using var scope = rootContainer.CreateScope(); // gets a reference to the container for the scope var scopeContainer = scope.ServiceProvider; // use the scope container to resolve services var fooService = scopeContainer.GetRequiredServicelt;IFooServicegt;(); var barService = scopeContainer.GetRequiredServicelt;IBarServicegt;(); var buzzService = scopeContainer.GetRequiredServicelt;IBuzzServicegt;(); // do whatever you want with the resolved services fooService.Foo(); barService.Bar(); buzzService.Buzz();
Эти области разрешения зависимостей действительно важны, поскольку они определяют время жизни служб, разрешенных с помощью поставщика услуг области. Таковы правила:
- службы, зарегистрированные в одноэлементном времени жизни, всегда разрешаются корневым контейнером приложения. Даже если вы используете дочерний контейнер для разрешения одноэлементной службы, фактическое разрешение службы делегируется корневому контейнеру. Одноэлементные службы, реализующие
IDisposable
интерфейс, удаляются при удалении корневого контейнера, что обычно происходит при завершении работы приложения. Каждая одноэлементная служба фактически решается один раз из корневого контейнера, и один и тот же экземпляр повторно используется в течение всего срока службы приложения. - службы, зарегистрированные с временным сроком службы, в основном противоположны одноэлементным службам. Каждый раз при разрешении службы создается совершенно новый экземпляр. Учитывая область, каждый раз, когда поставщик услуг области используется для разрешения временной службы, поставщик услуг области создает и отслеживает совершенно новый экземпляр типа реализации. Когда область будет удалена, все временные службы, которые были разрешены из этой области и имеют одноразовый тип реализации, также будут удалены.
- службы, зарегистрированные в течение срока службы области, ведут себя как синглеты для области, из которой они были созданы. Если у вас есть область действия и вы используете поставщика услуг области действия для разрешения службы с областью действия, совершенно новый экземпляр типа реализации создается только при первом разрешении службы. Все последующие запросы на разрешение одной и той же службы внутри одной и той же области будут выполняться путем повторного использования экземпляра типа реализации, созданного при первом разрешении службы в области. Службы с областью действия отслеживаются областью действия: это полезно, поскольку служба с областью действия, тип реализации которой является одноразовым, будет удалена при удалении области действия (то же поведение, что и временные службы).
Из предыдущих правил вы можете вывести четвертое правило: никогда не используйте корневой контейнер для разрешения служб, всегда создавайте область и разрешайте службы из этой области (не забудьте удалить область, когда закончите).
Это следует сделать, поскольку службы с областью действия и переходные службы отслеживаются областью действия и удаляются при удалении области действия. Если вы попытаетесь разрешить временные и ограниченные службы из корневого контейнера, они будут отслеживаться корневым контейнером и будут удалены только при удалении корневого контейнера: обычно это происходит при завершении работы приложения, поскольку срок службы корневого контейнера в основном равен сроку службы приложения. Другими словами, вы создадите утечку памяти, если не будете соблюдать четвертое правило.
Обратите внимание, что если вы используете перегрузку IServiceCollection.BuildServiceProvider
, используя логический параметр, вы можете попросить коллекцию служб создать поставщика услуг, который фактически проверяет, что службы с областью действия никогда не разрешаются из корневого контейнера. Таким образом, вы получите частичную проверку правила номер четыре (только службы с ограниченной областью проверяются на наличие опасного разрешения из корневого контейнера).
Возвращаясь к вашему вопросу, этап регистрации службы выполняется в одном и том же IServiceCollection
экземпляре и не осведомлен о концепции области разрешения службы. Области действия полезны только на более поздней стадии разрешения служб, чтобы определить срок службы разрешенных служб, как описано выше.
Как только вы создадите корневой контейнер из экземпляра коллекции служб, этап регистрации службы будет завершен, и, насколько мне известно, вы можете разрешать только службы, никакие дополнительные регистрации не будут разрешены.
Комментарии:
1. Отличный ответ, я узнал пару новых вещей, хотя и не совсем то, что хотел услышать 😛 Я надеялся, что есть способ, но я принимаю тот факт, что решения нет. Это означает возврат к Autofac, так как одна из серий на момент инициализации неизвестна
IServiceCollection
, но требуется с определенного момента позже.2. @t3chb0t основываясь на моих знаниях, невозможно выполнить регистрацию после создания корневого контейнера приложения. Вы можете попытаться открыть проблему github в репозитории microsoft github (возможно, ASP.NET основной или тот, для которого . Время выполнения NET core) и спросите их, возможно ли это.