是否生成多个数据库上下文生成死锁
本文关键字:上下文 死锁 数据库 是否 | 更新日期: 2023-09-27 18:32:34
总结我的代码,我有一个使用DbContextStrategy<E>
的IRepository<E>
。
DbContextStrategy<E>
从DbContext
扩展,并使用DbSet<E>
对数据库执行 LINQ 操作。
E
只是泛型传递的实体类型。
在我的 Web API 控制器中,我使用此存储库接口来获取我的资源。
但是,某些实体依赖于其他实体。例如,在PUT
或POST
请求中,我应该验证输入外键以检查它们是否有效。
因此,我需要实例化一个新IRepository<X>
其中X
是外部实体类型。
为了使我的开发尽可能简单,我生成了一个基类层,它将为我处理配置、缓存、依赖关系注入和 HTTP 方法绑定。
在我的最小子控制器中,我有以下方法:
/// 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;
}
这是将处理实体更新的方法。基本控制器将调用它,然后调用将更新DbContext
中的实体的repository.Edit(entity)
。手术后,IRepository<Season>
被处置。
ValidateShow 是一种私有方法,它只检查是否存在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
方法只是停留在无限循环中。我已经调试了该方法,并且调用被正确委托给DbSet<TVShow>
并且循环发生在:context.Entities.FindAsync(keys)
。
该方法工作正常,因为我使用相同的ReadAsync
方法来获取Season
实体。
但是,当有两个不同的DbContext
打开时,它似乎会产生某种僵局。( DbSet<Season>
和DbSet<TVShow>
(
我应该指出,这两个DbContext
都连接到同一个数据库。
异步/等待执行流从独立到数据库集
IRepository<E>
在IDao<E>
上调用SelectAsync(keys)
方法,在DbContextStrategy<E>
上调用SelectAsync(keys)
方法。
下面是代码跟踪:
默认存储库 : 独立:
public async Task<E> ReadAsync(params object[] keys) {
if(keys == null || keys.Length < 1) return null;
return await dao.SelectAsync(keys);
}
默认道:爱道
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,则实际上应该对整个调用堆栈(UpdateEntity()
及以上,包括 Web API 控制器的方法(使用 async/await。
这里描述了发生这种情况的原因 http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html