0

Во время начальной загрузки моего приложения я регистрирую в основном переходные процессы и типы областей с инфраструктурой Injection Dependency, которая поставляется с .Net Core. Тем не менее, у меня есть один тип, который зарегистрирован как singleton, это мой InProcessBus (я использую архитектуру CQRS в стиле).IServiceProvider.GetSingleton, разрешающий нуль в Asp.Net Core DI

services.AddSingleton<InProcessBus>(new InProcessBus()); 
...   
services.AddSingleton<ICommandSender>(y => y.GetService<InProcessBus>()); 
services.AddSingleton<IEventPublisher>(y => y.GetService<InProcessBus>()); 

Как вы можете видеть, я использую реализацию фабричных функции для фактических типов, которые будут использоваться в контроллере из API. Действительно странная вещь, когда контроллер загружается, и среда выполнения пытается построить инжектор ICommandSender, он не может его решить и сообщает об ошибке.

Если я проверил serviceCollection, я вижу, что реализацияFactory правильно зарегистрирована по типу под свойством ImplementationInstance.

Углубляясь и прямое разрешением типа в конце ConfigureServices() метода Startup класса утверждает, что контейнер разрешение null.

ServiceLocator.GetService<ICommandSender>() // Is Null 

Почему метод ImplementationFactory против синглтона, который явно существует в контейнере, не разрешается во время выполнения?

+0

Это странно. Хотя вы можете избежать использования 'y.GetService()' и использовать 'y.GetRequiredService ()', который будет генерировать исключение, если оно не может быть разрешено, а не возвращать null. Хотя это, вероятно, не решит вашу проблему. Есть ли еще больше? Также вы не можете разрешить его внутри 'ConfigureServices', в то время контейнер еще не построен. Раньше точка, которую вы можете разрешить, находится в режиме 'Configure' (если вы используете стандартный Startup.cs, который поставляется с шаблонами) – Tseng

+0

Вы уверены, что хотите использовать встроенный контейнер? Стили архитектуры CQRS очень эффективны, так как они действительно упрощают использование сквозных проблем с использованием декораторов. Невозможно применять родовые декораторы, используя встроенный контейнер. – Steven

+0

@Tseng, спасибо за подсказку GetRequiredService, явное исключение лучше нуля. Обычно я не мог бы разрешить да, но я использовал BuildServiceProvider() для создания собственного провайдера для тестирования с встроенным, когда у меня возникла проблема ... –

ответ

3

Я нашел то, что было не так, и думал, что отправлю его здесь, так как в нем есть важный урок.

Короткий ответ:

Defer serviceCollection.BuildServiceProvider() к после того, как вы зарегистрировали все одноэлементные экземпляры в противном случае он не будет в состоянии решить их.

Длинный ответ:

Проблема на самом деле не имеет ничего общего с контейнером ядра жерех DI (хорошо Сорта). Проблема заключается в сроках, когда я звоню BuildServiceProvider на экземпляр serviceCollection, чтобы предоставить мне IServiceProvider.

В моей реализации я применил шаблон ServiceLocator, который обертывает фреймворк DI, который поставляется с ядром, поэтому я не вношу дополнительной зависимости в другие архитектурные слои. Когда я создаю мой IServiceContainer основанный локатор я немедленно решить ServiceProvider

public ServiceLocator(
     IServiceCollection serviceCollection) 
    { 
     _serviceCollection = serviceCollection; 
     _serviceProvider = _serviceCollection.BuildServiceProvider(); 
    } 

И здесь проблема закралась. Время, когда это ServiceProvider получить созданные явно не имеет значения для Transients и Scoped экземпляров. Но для синглтонов, имеющих управляемую продолжительность жизни, это. После всех регистраций вам необходимо отложить создание своего ServiceProviders.

С моего вопроса ServiceLocator был построен до фрагментов, что означает, что экземпляр Singleton не был бы доступен для ServiceProvider при выполнении функции implementationFactory.

Откладывая создание ServiceProvider в первый раз, мое приложение разрешает один из моих пользовательских типов во время выполнения решения проблемы.

+0

У вас, вероятно, возникнут другие проблемы с этим подходом (игнорируя тот факт, что Service Locator является анти-шаблоном и превосходит всю цель DI/IoC): теперь у вас будет два контейнера в вашей системе, один из которых создает ASP.NET Core до вызывающий 'Configure' и тот, который вы создали для вашего анти-шаблонного локатора, что будет проблемой для облачных сервисов (DbContext), поскольку ваши сервисы, которые используют ASP.NET Core IoC, и те, которые используют ваш локатор сервисов, будут иметь разные экземпляры, SaveChanges() ', например, не будет защищать изменения из вашего другого контекста, и вы потеряете преимущество в работе. – Tseng

+0

Также вы могли бы разработать« _pattern, который обертывает структуру DI, которая поставляется с ядром, чтобы я не вводил дополнительную зависимость в моих других архитектурных слоях »? Вам не нужны никакие другие зависимости в ваших других слоях для 'Microsoft.Extensions.DependencyInjection'. Вам это нужно только в веб-приложении (где вы строите граф объекта в корне композиции). Ваш подход кажется очень неправильным с архитектурной точки зрения, и он создает жесткую зависимость и становится жестким/невозможным для модульного тестирования – Tseng

Смежные вопросы