在向存储库运行并行请求时处理DbContext

时间:2017-10-10 19:15:30

标签: aspnetboilerplate

更新到ABP v3.0.0后,我在向存储库运行并行请求时开始获得DbContext disposed异常,这样创建新的UnitOfWork如下:

using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))

我查看了源代码并在AsyncLocalCurrentUnitOfWorkProvider中找到了一些我不太了解的代码。设置当前uow时,它在包装器中设置属性:

private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>();

private static void SetCurrentUow(IUnitOfWork value)
{
    lock (AsyncLocalUow)
    {
        if (value == null)
        {
            if (AsyncLocalUow.Value == null)
            {
                return;
            }

            if (AsyncLocalUow.Value.UnitOfWork?.Outer == null)
            {
                AsyncLocalUow.Value.UnitOfWork = null;
                AsyncLocalUow.Value = null;
                return;
            }

            AsyncLocalUow.Value.UnitOfWork = AsyncLocalUow.Value.UnitOfWork.Outer;
        }
        else
        {
            if (AsyncLocalUow.Value?.UnitOfWork == null)
            {
                if (AsyncLocalUow.Value != null)
                {
                    AsyncLocalUow.Value.UnitOfWork = value;
                }

                AsyncLocalUow.Value = new LocalUowWrapper(value);
                return;
            }

            value.Outer = AsyncLocalUow.Value.UnitOfWork;
            AsyncLocalUow.Value.UnitOfWork = value;
        }
    }
}

private class LocalUowWrapper
{
    public IUnitOfWork UnitOfWork { get; set; }

    public LocalUowWrapper(IUnitOfWork unitOfWork)
    {
        UnitOfWork = unitOfWork;
    }
}

这看起来不是线程安全的,因为任何线程都可以设置新的UnitOfWork然后处置它。

这是一个错误吗?我可以使用我自己的ICurrentUnitOfWorkProvider实现,而不包装,但我不确定这是否正确。

更新

我无法提供DbContext disposed例外的示例,但null reference方法中有一个repository.GetAll()例外。我认为它有同样的原因。

namespace TestParallelEFRequest
{
    public class Ent1 : Entity<int> { public string Name { get; set; } }
    public class Ent2 : Entity<int> { public string Name { get; set; } }
    public class Ent3 : Entity<int> { public string Name { get; set; } }
    public class Ent4 : Entity<int> { public string Name { get; set; } }
    public class DomainStartEvent : EventData {}
    public class DBContext : AbpDbContext {
        public DBContext(): base("Default") {}
        public IDbSet<Ent1> Ent1 { get; set; }
        public IDbSet<Ent2> Ent2 { get; set; }
        public IDbSet<Ent3> Ent3 { get; set; }
        public IDbSet<Ent4> Ent4 { get; set; }
    }

    public class TestService : DomainService, IEventHandler<DomainStartEvent>
    {
        private readonly IRepository<Ent1> _rep1;
        private readonly IRepository<Ent2> _rep2;
        private readonly IRepository<Ent3> _rep3;
        private readonly IRepository<Ent4> _rep4;
        public TestService(IRepository<Ent1> rep1, IRepository<Ent2> rep2, IRepository<Ent3> rep3, IRepository<Ent4> rep4) { 
                _rep1 = rep1;_rep2 = rep2;_rep3 = rep3;_rep4 = rep4;
        }

        Task HandleEntityes<T>(IRepository<T> rep, int i) where T : class, IEntity<int> {
            return Task.Run(() => {
                using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) {
                    Thread.Sleep(i); // Simulating work
                    rep.GetAll();    // <- Exception here
                }
            });
        }

        public void HandleEvent(DomainStartEvent eventData) {
            using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) {
                var t1 = HandleEntityes(_rep1, 10);
                var t2 = HandleEntityes(_rep2, 10);
                var t3 = HandleEntityes(_rep3, 10);
                var t4 = HandleEntityes(_rep4, 1000);
              Task.WaitAll(t1, t2, t3, t4);
            }
        }
    }

    [DependsOn(typeof(AbpEntityFrameworkModule))]
    public class ProgrammModule : AbpModule
    {
        public override void PreInitialize() { Configuration.DefaultNameOrConnectionString = "Default"; }
        public override void Initialize()
        {
            IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
            Database.SetInitializer<DBContext>(null);
        }
    }

    class Program {
        static void Main(string[] args) {
            using (var bootstrapper = AbpBootstrapper.Create<ProgrammModule>()) {
                bootstrapper.Initialize();
                bootstrapper.IocManager.Resolve<IEventBus>().Trigger(new DomainStartEvent());
                Console.ReadKey();
            }
        }
    }
}

0 个答案:

没有答案