Reputation: 26281
Summarizing my code, I have a IRepository<E>
that uses a DbContextStrategy<E>
.
DbContextStrategy<E>
extends from DbContext
and uses a DbSet<E>
for LINQ operations on the database.
E
is just an entity type passed by generics.
In my Web API controller I am using this repository interface to fetch my resources.
However, some entities depend on other entities. And in PUT
or POST
requests for example, I should validate input foreign keys to check if they are valid.
As a result, I would need to instantiate a new IRepository<X>
where X
is the foreign entity type.
In order for me to make my development as easy as possible, I generated a layer of base classes that will handle configuration, cache, dependency injection and HTTP method binding for me.
In my least 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's update. The base controller will call it, and then call the repository.Edit(entity)
which will update the entity in the DbContext
. After the operation, the IRepository<Season>
is disposed.
ValidateShow is a private method that just checks if a showId
exists:
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 ValidateShow
method just stays in an infinite loop. I have debugged the method, and the call gets correctly delegated to the DbSet<TVShow>
and the loop happens at: context.Entities.FindAsync(keys)
.
The method works ok, because I used the same ReadAsync
method to fetch the Season
entity.
But it appears that it generates some kind of deadlock when there are two different DbContext
open. (DbSet<Season>
and DbSet<TVShow>
)
I should note that both DbContext
connects to the same database.
IRepository<E>
calls the SelectAsync(keys)
method on an IDao<E>
, which calls the SelectAsync(keys)
method 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);
}
Upvotes: 3
Views: 1594
Reputation: 1480
Each instance of DbContext
has its own transaction which might cause deadlock but you'd need a write operation for that to happen and DbContext only saves the changes to the database when you call SaveChanges()
which you don't call before calling validatingShow.Wait()
It's much more likely that this Wait()
call is a problem. If you're using async/await you should really use async/await for the whole call stack (UpdateEntity()
and up including your Web API controller's methods)
The reason why this is happening is described here http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
Upvotes: 3