Запустите новый LifetimeScope, когда сработает потребитель Masstransit - PullRequest
1 голос
/ 13 марта 2020

У нас есть следующий код для настройки recieveEndpoint:

private Action<IServiceBusReceiveEndpointConfigurator> GetReceiveEndpointConfigurator(IEnumerable<Type> consumerTypes)
{
    return c =>
        {
            c.EnableDeadLetteringOnMessageExpiration = true;
            c.SubscribeMessageTopics = false;
            c.MaxDeliveryCount = 3;
            c.EnableDeadLetteringOnMessageExpiration = true;
            c.UseRetry(Retry.Exponential(3, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(1)));
            foreach (var consumerType in consumerTypes)
            {
                c.Consumer(consumerType, p => _componentContext.Resolve(p));
            }
       };
}

Все наши потребители автоматически обнаруживаются путем отражения при запуске нашего приложения. У нас есть DbContext, который мы хотим использовать во многих наших потребителях. Проблема, с которой мы сталкиваемся, заключается в том, что DbContext удаляется из-за его регистрации как InstancePerLifetimeScope. Более подробная информация здесь:

As pNet Core Autofa c распоряжается моим DbContext, даже если он зарегистрирован как SingleInstance

Из этого сообщения поступили два предложения:

  1. Зарегистрируйте DbContext как InstancePerDependency
  2. Создайте новую область действия внутри потребителя, чтобы начать новую LifetimeScope

Первое предложение не будет работать в наше приложение, поскольку у нас есть UnitOfWork, который запускает SaveChangesAsync на DbContext. В результате Repository и UnitOfWork получат два разных экземпляра DbContext, а SaveChangesAsync не сохранят наши изменения, так как ChangeTracker не имеет изменений в реализации UnitOfWork, но они изменения относятся к экземпляру в Repository.

Второе предложение работает отлично. В моем Consumer я создаю новый LifetimeScope и определяю компоненты, которые мне нужны:

public async Task Consume(ConsumeContext<RetailerCreatedEvent> context)
 {
     using (var scope = _serviceProvider.CreateScope())
     {
         var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork<MyDbContext>>();
     }
 }

Это работает, но выглядит не так хорошо.

Есть ли способ запустить новый LifetimeScope до срабатывания Consumer? Или я должен переписать свой шаблон UnitOfWork, чтобы гарантировать повторное использование одного и того же DbContext в Repositories и UnitOfWork?

Предложения очень ценятся

1 Ответ

3 голосов
/ 13 марта 2020

Вам нужно использовать пакет MassTransit.Autofac для разрешения ваших потребителей, который будет использовать AutofacScopeProvider (часть пакета), чтобы создать область действия на всю жизнь и решить вашего потребителя.

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

У ваших потребителей не должно быть никакого кода контейнера, использующего этот пакет, они должны просто добавить DbContext в качестве конструктора зависимость и пусть Autofa c делает работу.

...