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);
}

      

+3


source to share


1 answer


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

+3


source







All Articles