Внедрение зависимости Microsoft. Как я могу получить данные в конструктор внедрил класс в несколько слоев - PullRequest
1 голос
/ 29 марта 2019

Это основной проект dotNet 2.2, использующий Microsoft.Extensions.DependencyInjection.

У меня есть 3 класса.Класс A использует класс B в конструкторе.Класс B использует класс C, а класс C использует интерфейс ITenant.

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

пример:

public A(IB b)
public B(IC c)
public C(ITenant t)

Они настроены вКонтейнер впрыска выглядит следующим образом:

services.AddTransient<IA, A>();
services.AddTransient<IB, b>();
services.AddTransient<IC, c>();
services.AddTransient<ITenant , HttpTenant>()>();

В веб-проекте контроллер использует класс A в качестве параметра конструктора, а также контейнер createClass A и все его зависимости.Реализация ITenant (HttpTenant) извлекает имя клиента из заголовка HTTP-запроса и получает информацию базы данных из файла конфигурации.Все работает отлично.

Теперь мне нужно вызвать это из службы Windows, которая не включает HTTP-запрос.У меня есть обработчик, который отвечает на очередь сообщений, и класс A является параметром конструкции.Для службы Windows у меня есть другой ITenant (WindowServiceTenant):

services.AddTransient<ITenant , WindowServiceTenant>()>();

Я не могу понять, как получить код арендатора в WindowServiceTenant.

  • Арендатор определяется при запускевремя, основанное на чтении значения из очереди сообщений.
  • К моменту создания экземпляра моего обработчика также создается экземпляр WindowServiceTenant.
  • Я не знаю арендатора до установки обработчика.

Мне нужно получить ссылку на этот экземпляр WindowServiceTenant и предоставить клиента.Или эта реализация WindowServiceTenant нуждается в ссылке на обработчик, который инициировал создание экземпляра.

Есть идеи?

1 Ответ

0 голосов
/ 31 марта 2019

Существует два основных решения:

  1. Настройте экземпляр WindowServiceTenant с необходимым значением, прежде чем разрешить обработчик
  2. Сообщите значение через окружающее состояние, например, значение, которое доступно для потока (ThreadLocal<T>) или асинхронной операции (AsyncLocal<T>)

Первый вариант требует, чтобы WindowServiceTenant был зарегистрирован как сервис Scoped и создание IServiceScope, из которого вы определяете WindowServiceTenant и соответствующий обработчик:

// Registration
services.AddScoped<WindowServiceTenant>();
services.AddScoped<ITenant>(c => c.GetRequiredService<WindowServiceTenant>());

// Usage
using (var scope = serviceProvider.CreateScope())
{
    var services = serviceScope.ServiceProvider;

    var tenant = services.GetRequiredService<WindowServiceTenant>();

    // Set the right tenant based on a value from the queue
    tenant.SetTenantValue(...);

    // Resolve and execute handler
    var handler = services.GetRequiredService(handlerType);
}

Предыдущий листинг кода выполняет следующие действия:

  • Он регистрирует WindowServiceTenant как по конкретному типу, так и по интерфейсу таким образом, что оба разрешения WindowServiceTenant и ITenant приводят к одному и тому же экземпляру в течение одной области обслуживания. Это важно, потому что в противном случае состояние устанавливается для этого экземпляра области. Наличие нескольких экземпляров в одной и той же области обслуживания, очевидно, не даст правильного результата.
  • Когда ваше сообщение обрабатывается, вы начинаете новый IServiceScope, используя CreateScope метод расширения для IServiceProvider.
  • В этой области вы решаете WindowServiceTenant. Вы разрешаете этот конкретный тип, поскольку у абстракции ITenant не будет возможности установить правильное значение (поскольку это деталь реализации)
  • Значение клиента хранится в очереди внутри экземпляра WindowServiceTenant. Поскольку этот же экземпляр повторно используется во время области действия службы, он будет внедрен в любой граф разрешенных объектов, который зависит от ITenant.
...