总结我的代码,我有一个使用 IRepository<E>DbContextStrategy<E>
DbContextStrategy<E> 扩展自 DbContext 并使用 DbSet<E> 对数据库进行 LINQ 操作。
E 只是泛型传递的实体类型。

在我的 Web API Controller 中,我使用这个存储库接口(interface)来获取我的资源。

但是,某些实体依赖于其他实体。例如,在 PUTPOST 请求中,我应该验证输入的外键以检查它们是否有效。

因此,我需要实例化一个新的 IRepository<X>,其中 X 是外部实体类型。

为了让我的开发尽可能简单,我生成了一层基类,这些基类将为我处理配置、缓存、依赖注入(inject)和 HTTP 方法绑定(bind)。

在我最小的子 Controller 中,我有以下方法:

/// Base class provides me with the Season entity fetched from the database.
/// An IRepository<Season> is open in the background, and therefore, a DbContext too.
protected override Season UpdateEntity(Season entity, SeasonPostDTO dto)
{
     Task<bool> validatingShow = ValidateShow(dto.Show);

     entity.Number = dto.Number;
     entity.ReleaseDate = dto.ReleaseDate;
     entity.Image = dto.Image;
     entity.TVShowId = dto.Show;
     entity.BlameUserId = Convert.ToInt32(GetCurrentUserId());

     validatingShow.Wait();
     if (!validatingShow.Result)
          throw new EntityNotFoundException("Show not found");

     return entity;
}

这是将处理实体更新的方法。基本 Controller 将调用它,然后调用 repository.Edit(entity) 将更新 DbContext 中的实体。操作完成后,IRepository<Season> 就被处理掉了。

ValidateShow 是一个私有(private)方法,它只检查 showId 是否存在:
private async Task<bool> ValidateShow(int id) {

     //This will instantiate a new IRepository<TVShow>, and therefore a new DbContext
     return await UseAsyncDependency<TVShow, bool>(async (showRepo) => {

         return (await showRepo.ReadAsync(id)) != null;

     });
}

然而,ValidateShow 方法只是停留在无限循环中。我已经调试了该方法,并且调用正确地委托(delegate)给 DbSet<TVShow> 并且循环发生在: context.Entities.FindAsync(keys)

该方法工作正常,因为我使用了相同的 ReadAsync 方法来获取 Season 实体。

但是当打开两个不同的 DbContext 时,它​​似乎会产生某种死锁。 ( DbSet<Season>DbSet<TVShow> )

我应该注意到两个 DbContext 都连接到同一个数据库。

从 IRepository 到 DbSet 的异步/等待执行流程
IRepository<E> 调用 SelectAsync(keys) 上的 IDao<E> 方法,SelectAsync(keys) 调用 DbContextStrategy<E> 上的 ojit_code 方法。

这是代码跟踪:

DefaultRepository : IRepository :
public async Task<E> ReadAsync(params object[] keys) {
    if(keys == null || keys.Length < 1) return null;
    return await dao.SelectAsync(keys);
}

DefaultDao : IDao
public async Task<E> SelectAsync(params object[] keys) {
    return await ForEachStrategyAsync(async (strategy) => {
        return await strategy.SelectAsync(keys);
    }, (entity) => {
        return entity != null;
    });
}

private async Task<R> ForEachStrategyAsync<R>(Func<IPersistenceStrategy<E>, Task<R>> function,
                                              Func<R, bool> assertion) {

    R lastResult = default(R);
    foreach(IPersistenceStrategy<E> strategy in strategies) {
         lastResult = await function(strategy);
         if(assertion(lastResult)) break;
    }
    return lastResult;
}

DbContextStrategy : IPersistenceStrategy
public async Task<E> SelectAsync(params object[] keys) {
    return await context.Entities.FindAsync(keys);
}

最佳答案

DbContext 的每个实例都有自己的事务,这可能会导致死锁,但您需要进行写入操作才能发生这种情况,并且 DbContext 仅在您调用 SaveChanges() 时才将更改保存到数据库中,而您在调用 validatingShow.Wait() 之前不会调用该事务

这个 Wait() 调用更有可能是一个问题。如果您使用 async/await,您应该真正为整个调用堆栈使用 async/await(UpdateEntity() 及以上,包括您的 Web API Controller 的方法)

这里描述了发生这种情况的原因 http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

关于c# - 多个 DbContext 会产生死锁吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27230389/

10-13 08:31