не удалось сделать работу Autofac вложенных областей (вложенные несколько единиц работ должны иметь новый dbcontext)

Я пытаюсь реализовать Единицу работы с Autofac и Mediatr. Вот как проходит поток

architecture

но я не мог заставить Autofac отправлять тот же экземпляр Unit OfWork (который принимает DbContext как параметр) внутри области. Я хочу выполнить всю эту область внутри одной транзакции, а это значит, что когда я доберусь до точки processHandler, она должна создать экземпляр DbContext и поделиться одним и тем же экземпляром с вложенными обработчиками. так что я могу создать транзакцию на уровне обработчика процесса и передать одну и ту же транзакцию вложенным обработчикам.

здесь моя установка DI

 builder.Register(ctx =>
        {
            var contextSvc = ctx.Resolve<IContextService>(); // owin context 
            var connBuilder = ctx.Resolve<IDbConnectionBuilder>(); 
            return SapCommandDb.Create(contextSvc.GetMillCode(), connBuilder.BuildConnectionAsync(IntegrationConnectionName, contextSvc.GetMillCode()).Result);
        }).AsSelf().InstancePerLifetimeScope();

        builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IDomainRepository<>)).InstancePerLifetimeScope();
        builder.RegisterType<EFUnitOfWork>().As<IEFUnitOfWork>().InstancePerLifetimeScope();



 public class ProcessHandler : AsyncRequestHandler<IntermediateDocument.Command>
    {
        IMediator _mediator;
        Func<Owned<IEFUnitOfWork>> _uow;
        ILifetimeScope _scope;
        public ProcessHandler(
            ILifetimeScope scope,
            Func<Owned<IEFUnitOfWork>> uow,
            IMediator mediator)
        {
            _mediator = mediator;
            _scope = scope;
            _uow = uow;
        }
        protected async override Task Handle(Command request, CancellationToken cancellationToken)
        {
            foreach (var transaction in request.Transactions)
            {
                using (var scope = _scope.BeginLifetimeScope("custom"))
                {
                    using (var uo = _uow())
                    {
                        await uo.Value.Execute(async () =>
                        {
                            await _mediator.Send(new NestedHandlerGetBySwitch.Command(transaction));
                        });
                    }
                }
            }
        }
    }

вышеупомянутый является обработчиком процесса

public class NestedHandler1 : AsyncRequestHandler<NestedHandler.Command>
        {
            IMediator _mediator;
            IEFUnitOfWork _uow;
            public NestedHandler1(
                IEFUnitOfWork uow,
                IMediator mediator)
            {
                _mediator = mediator;
                _uow = uow;
            }
            protected async override Task Handle(Command request, CancellationToken cancellationToken)
            {
                _uow.Repository.Add(request);
            }
        }

выше приведен пример вложенного обработчика. Мне нужен тот же экземпляр _uow из обработчика процесса.

EFUNitOFWork выглядит

public class EfUnitOfWork : IEFUnitOfWork {
    private DbContext _context;
    ABCRepository aBCRepository;
    public ABCRepository ABCRepository { get {
            return aBCRepository = aBCRepository ?? new ABCRepository(_context);
        } }
    public EfUnitOfWork(DbContext context)
    {
        _context = context;
    }

    public Task Add(Entity entity) {
        await _context.AddAsync(entity);
    }
}

Что я делаю неправильно?

Спасибо.

+3
17 сент. '18 в 16:14
источник поделиться
2 ответа

IMediator просит AutoFac создать экземпляр NestedHandler1, поэтому он будет иметь ту же область действия, что и IMediator.

Один из способов его решения - разрешить IMediator из "пользовательского" диапазона времени жизни и использовать его вместо того, чтобы вводить его в конструктор, и убедиться, что UnitOfWork правильно зарегистрирован в этой области:

using (var uo = _uow())
{
    using (var scope = _scope.BeginLifetimeScope("custom", x => x.RegisterInstance(uo.Value))
    {
        var mediator = scope.Resolve<IMediator>();

        await uo.Value.Execute(async () =>
        {
            await mediator.Send(new NestedHandlerGetBySwitch.Command(transaction));
        });
    }
}
0
05 окт. '18 в 12:49
источник

У вас немного беспорядок между UnitsOfWork, Mediators и т.д.

Пусть все упрощается и выводит реализацию из требований.

  1. Вы должны иметь один DbContext, разделяемый несколькими компонентами.
  2. Один запрос может обрабатывать несколько операций несколькими обработчиками.

Учитывая эти два факта, мы можем сделать вывод о том, что нам нужны две разные области жизни: первый для обмена DbContext (мы будем называть это "UnitOfWork"), а второй - для каждой операции (позвольте назвать эту "операцию"),

Обработка этой структуры будет осуществляться следующим образом:

public class ProcessHandler : AsyncRequestHandler<IntermediateDocument.Command>
{
    // ...ctor 

    protected async override Task Handle(Command request, CancellationToken cancellationToken)
    {
        // inside this using every component asking for an
        // IEFUnitOfWork will get the same instance 
        using (var unitOfWorkScope = _scope.BeginLifetimeScope("UnitOfWork"))
        {
            foreach (var transaction in request.Transactions)
            {
                // we don't need this inner scope to be tagged, AFAICT
                // so probably "Operation" could be omitted.
                using (var scope = unitOfWorkScope.BeginLifetimeScope("Operation"))
                {
                    // I'm not sure what a "mediator" is, I'm just copying the example code
                    var mediator = scope.Resolve<IMediator>();

                    await mediator.Send(...do something with transaction);
                } // here mediator will be disposed, once for each transaction instance
            }
        } // here everything resolved inside unitOfWorkScope will be disposed (and possibly committed).
    }
}

DbContext должен быть зарегистрирован как

builder.RegisterType<EFUnitOfWork>().As<IEFUnitOfWork>().InstancePerMatchingLifetimeScope("UnitOfWork");

Вполне возможно, что вам не нужен IEFUnitOfWork, но вы можете просто поделиться DbContext, зарегистрировав его в области UnitOfWork. Другими словами, помеченный областью Autofac может полностью заменить ваш класс AFAICT.

Ссылка на документацию Autofac:
https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope

0
09 окт. '18 в 13:15
источник

Посмотрите другие вопросы по меткам или Задайте вопрос