не удалось заставить работать вложенные области Autofac (вложенное несколько единиц работ должно иметь новый dbcontext) - PullRequest
0 голосов
/ 17 сентября 2018

Я пытаюсь внедрить Unit of Work с 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);
    }
}

что я делаю не так?

Спасибо.

Ответы [ 2 ]

0 голосов
/ 09 октября 2018

У вас небольшой беспорядок между UnitsOfWork, Mediators и прочим.

Давайте сделаем все просто и выведем реализацию из требований.

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

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

Обработка этой структуры будет обработанавот так:

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 голосов
/ 05 октября 2018

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));
        });
    }
}
...