Как внедрить зависимость в поведение конвейера NServiceBus? - PullRequest
0 голосов
/ 27 января 2020

Я следил за примерами NServiceBus, в частности, за тем, как использовать DbContext инфраструктуры объектов (ядра), интегрированный с Sql Постоянство, чтобы я мог сохранять изменения состояния dbcontext вместе с исходящими сообщениями. Вот пример: https://docs.particular.net/samples/entity-framework-core/

Я немного изменил код единицы работы, чтобы поддержать создание DbContext в виде pnet ядра DI. Соответствующий код выглядит следующим образом:

    public class UnitOfWork<TDbContext>
        where TDbContext : DbContext
    {
        private Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
        private TDbContext _context;
        private IServiceProvider _serviceProvider;

        public UnitOfWork(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceProvider serviceProvider)
        {
            _contextFactory = contextFactory;
            _serviceProvider = serviceProvider;
        }

        public TDbContext GetDataContext(SynchronizedStorageSession storageSession)
        {
            if (_context == null)
            {
                _context = _contextFactory(storageSession, _serviceProvider);
            }
            return _context;
        }
    }

    public class UnitOfWorkSetupBehavior<TDbContext> : Behavior<IIncomingLogicalMessageContext>
        where TDbContext : DbContext
    {
        private readonly Func<SynchronizedStorageSession, IServiceProvider, TDbContext> _contextFactory;
        private readonly IServiceScopeFactory _serviceScopeFactory;

        public UnitOfWorkSetupBehavior(Func<SynchronizedStorageSession, IServiceProvider, TDbContext> contextFactory, IServiceScopeFactory serviceScopeFactory)
        {
            _contextFactory = contextFactory;
            _serviceScopeFactory = serviceScopeFactory;
        }

        public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next)
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var uow = new UnitOfWork<TDbContext>(_contextFactory, scope.ServiceProvider);
                context.Extensions.Set(uow);

                await next().ConfigureAwait(false);

                context.Extensions.Remove<UnitOfWork<TDbContext>>();
            }
        }
    }

    public static class EndpointConfigurationExtensions
    {
        public static void RegisterUnitOfWork<TDbContext>(this EndpointConfiguration endpointConfiguration, IServiceScopeFactory serviceScopeFactory)
            where TDbContext : DbContext
        {
            var pipeline = endpointConfiguration.Pipeline;
            pipeline.Register(new UnitOfWorkSetupBehavior<TDbContext>((storageSession, serviceProvider) =>
            {
                var dbConnection = storageSession.SqlPersistenceSession().Connection;
                var dbContextFactory = serviceProvider.GetService<IDbContextConnectionFactory<TDbContext>>();
                var dbContext = dbContextFactory.GetDbContext(dbConnection);

                //Use the same underlying ADO.NET transaction
                dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction);

                //Call SaveChanges before completing storage session
                storageSession.SqlPersistenceSession().OnSaveChanges(x => dbContext.SaveChangesAsync());

                return dbContext;
            }, serviceScopeFactory), "Sets up unit of work for the message");
        }
    }

    public static class UnitOfWorkContextExtensions
    {
        public static TDbContext DataContext<TDbContext>(this IMessageHandlerContext context)
            where TDbContext : DbContext
        {
            var uow = context.Extensions.Get<UnitOfWork<TDbContext>>();
            return uow.GetDataContext(context.SynchronizedStorageSession);
        }
    }

Чтобы это работало, поведению необходим внедренный IServiceScopeFactory.

Теперь все примеры, которые мне удалось найти при регистрации поведения, показывают только тип, созданный вручную и передается в конвейер конфигурации конечной точки.

Есть ли способ получить доступ к IServiceScopeFactory с помощью метода Invoke поведения (возможно, с помощью контекста через какое-то расширение?), или можно зарегистрировать поведение сам по себе такой, что я могу создать его с помощью сервисов, созданных контейнером DI?

К вашему сведению, я взглянул на этот вопрос и ответ, который дал мне идею внедрить IServiceScopeFactory. К сожалению, ответ не показывает, как на самом деле получить экземпляр интерфейса.

1 Ответ

0 голосов
/ 27 января 2020

Вы бы использовали context.builder.Build<T>(); в методе Invoke для разрешения любых объектов, таких как IServiceScopeFactory.

Убедитесь, что IServiceScopeFactory зарегистрирован в контейнере DI. Например, во время инициализации конечной точки:

endpointConfiguration.RegisterComponents(registration: x =>
    {
        x.ConfigureComponent<IServiceScopeFactory>(yourServiceScopeFactory);
    });

Вы также можете сделать это, создав Элемент

...