Do many DbContext generate deadlock?
To summarize my code, I have IRepository<E>
one that uses DbContextStrategy<E>
.
DbContextStrategy<E>
extends from DbContext
and uses operations DbSet<E>
for LINQ on the database.
E
is just a generic type of entity.
In my web API controller, I use this repository interface to fetch my resources.
However, some objects depend on other objects. For example, in queries PUT
or POST
I have to check the input of foreign keys to check if they are valid.
As a result, I need to instantiate a new IRepository<X>
one where X
is the type of the external object.
In order to make my development as simple as possible, I created a base class layer that will handle the configuration, cache, dependency, and HTTP method binding for me.
In my junior child controller, I have the following method:
/// 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;
}
This is the method that will handle the entity update. The base controller will call it and then call it repository.Edit(entity)
, which will update the object in DbContext
. After the operation is located IRepository<Season>
.
ValidateShow is a private method that just checks for existence 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;
});
}
However, the method is ValidateShow
just in an infinite loop. I debugged this technique, and the call is properly delegated to DbSet<TVShow>
, and the cycle occurs at context.Entities.FindAsync(keys)
.
The method works fine because I used the same method ReadAsync
to retrieve the object Season
.
But it looks like it creates some kind of dead end when there are two different DbContext
open. ( DbSet<Season>
and DbSet<TVShow>
)
It should be noted that both DbContext
connect to the same database.
Async / Waiting for thread from IRepository to DbSet
IRepository<E>
calls a method SelectAsync(keys)
on IDao<E>
, which calls a method SelectAsync(keys)
on DbContextStrategy<E>
.
Here is the code trace:
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);
}
source to share
Each instance DbContext
has its own transaction, which can deadlock, but you need a write operation to do so, and the DbContext only stores changes to the database when called SaveChanges()
, which you do not call before callingvalidatingShow.Wait()
Most likely this challenge Wait()
is the problem. If you are using async / await, you should really use async / await for the entire call stack ( UpdateEntity()
and up, including the Web API controller methods).
The reason this is happening is described here http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
source to share