skip to Main Content

2 methods from my controller for some reason trigger this error:
An exception occurred in the database while saving changes for context type ‘API.Data.DataContext’.
System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext

I have tried everything in this thread: Entity Framework Core: Entity Framework Core: A second operation started on this context before a previous operation completed But nothing seems to work for me.

ITs different for me because I cant use dbContext as tranziant

The issue happens in PowerUp and DeleteHero methods

Controller

public class HeroesController : BaseApiController
{

    private readonly IUserRepository _userRepository;
    public readonly IHeroRepository _heroRepository;
    public HeroesController(IUserRepository userRepository, IHeroRepository heroRepository)
    {
        _userRepository = userRepository;
        _heroRepository = heroRepository;
    }

    [HttpPost("{create}")]
    public async Task<ActionResult> CreateHero(RegisterHeroDto registerHeroDto)
    {
        var user = await this.GetActiveUser();

        var existingUserHero = await _heroRepository.IsUserHasHero(user, registerHeroDto.Name);

        if (existingUserHero == true) { return BadRequest("You already have a hero with that name"); }

        var hero = _heroRepository.HeroBuilder(registerHeroDto, user);

        user.Heroes.Add(hero);

        if (await _userRepository.SaveAllAsync()) { return Ok(); }

        return BadRequest("Failed to create hero");
    }

    public async Task<ActionResult<IEnumerable<HeroDto>>> GetHeroes()
    {
        var user = await this.GetActiveUser();

        var heroesDto = await _heroRepository.HeroesDtoConverter(user);

        return Ok(heroesDto);
    }

    [HttpPost("powerUp/{id}")]
    public async Task<ActionResult> PowerUp(int id)
    {
        var user = await this.GetActiveUser();

        var hero = await _heroRepository.GetHeroById(id);

        var canHeroPowerUp = _heroRepository.PowerUpAuthorizer(hero);

        if (canHeroPowerUp == false)
        {
            return BadRequest(hero.Name + " has 0 power-ups left for today");
        }

        _heroRepository.PowerUp(hero);

        var heroesDto = await _heroRepository.HeroesDtoConverter(user);

        if (await _userRepository.SaveAllAsync()) { return Ok(); }

        return BadRequest("Failed to power up " + hero.Name);
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteHero(int id)
    {
        var user = await this.GetActiveUser();

        var hero = await _heroRepository.GetHeroById(id);

        _heroRepository.DeleteHero(user, hero);

        var heroesDto = await _heroRepository.HeroesDtoConverter(user);

        if (await _userRepository.SaveAllAsync()) { return Ok(); }

        return BadRequest("Failed to remove hero");
    }

    private Task<AppUser> GetActiveUser()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var user = await this._userRepository.GetUserByIdAsync(userId);

        return user;
    }
}

Hero Repository:

    public class HeroRepository : ControllerBase, IHeroRepository
{
    private readonly DataContext _context;
    public readonly IMapper _mapper;
    public HeroRepository(DataContext context, IMapper mapper)
    {
        _context = context;
        _mapper = mapper;
    }

    public async Task<List<Hero>> GetAllHeroes(AppUser user)
    {
        // refreshPowerUps(user.Heroes.ToList());

        // var heroes = user.Heroes
        //             .OrderBy(a => a.CurrentPower)
        //             .ToList();

        var heroes = await _context.Heroes
                            .Where(a => a.AppUserId == user.Id)
                            .OrderBy(a => a.CurrentPower)
                            .ToListAsync();

        // var updatedHeroes = refreshPowerUps(heroes);

        return heroes;
    }

    // private async void refreshPowerUps(List<Hero> heroes)
    // {
    //     foreach (Hero hero in heroes)
    //     {
    //         if (hero.LastPowerUp.Date.Day != DateTime.Now.Day)
    //         {
    //             hero.PowerUpsToday = 0;
    //         }
    //     }
    //     await SaveAllAsync();
    // }

    public async Task<Hero> GetHeroById(int id)
    {
        return await _context.Heroes
                .Where(x => x.Id == id)
                .FirstOrDefaultAsync();
    }

    public Hero HeroBuilder(RegisterHeroDto registerHeroDto, AppUser user)
    {
        var startingPower = registerHeroDto.Ability == "attacker" ? 100 : 50;

        var hero = new Hero
        {
            Name = registerHeroDto.Name,
            Ability = registerHeroDto.Ability,
            SuitColors = registerHeroDto.SuitColors,
            StartingPower = startingPower,
            CurrentPower = startingPower,
            AppUserId = user.Id
        };

        return hero;
    }

    public async Task<bool> IsUserHasHero(AppUser user, string heroName)
    {
        if (user.Heroes == null)
        {
            user.Heroes = new List<Hero>();
        }

        var existingHero = await _context.Heroes
                            .Where(x => x.AppUserId == user.Id)
                            .FirstOrDefaultAsync(x => x.Name.ToLower() == heroName.ToLower());

        if (existingHero != null)
        {
            return true;
        }

        return false;
    }

    public bool PowerUpAuthorizer(Hero hero)
    {
        if (hero.LastPowerUp.Day == DateTime.Now.Day && hero.PowerUpsToday >= 5)
        {
            return false;
        }

        return true;
    }

    public double PowerUp(Hero hero)
    {
        float powerIncrement = ((new Random().Next(1, 10) * .1f) / 10) + 1;
        float newPower = (float)hero.CurrentPower * powerIncrement;

        hero.CurrentPower = newPower;
        hero.PowerUpsToday++;
        hero.LastPowerUp = DateTime.Now;

        return newPower;
    }

    public async Task<bool> SaveAllAsync()
    {
        return await _context.SaveChangesAsync() > 0;
    }

    public void DeleteHero(AppUser user, Hero hero)
    {
        user.Heroes.Remove(hero);
    }

    public async Task<List<HeroDto>> HeroesDtoConverter(AppUser user)
    {
        var heroes = await GetAllHeroes(user);

        var heroesDto = _mapper.Map<IList<Hero>, IList<HeroDto>>(heroes).ToList();

        return heroesDto;
    }
}

User repository

public class UserRepository : BaseApiController, IUserRepository
{
    private readonly DataContext _context;
    private readonly IMapper _mapper;
    private readonly UserManager<AppUser> _userManager;
    public UserRepository(DataContext context, IMapper mapper,
                          UserManager<AppUser> userManager)
    {
        _mapper = mapper;
        _context = context;
        _userManager = userManager;
    }

    public async Task<AppUser> GetUserByEmailAsync(string email)
    {
        return await _userManager.Users
           .SingleOrDefaultAsync(x => x.Email == email.ToLower());
    }

    public async Task<AppUser> GetUserByIdAsync(string id)
    {
        return await _context.Users
                .Where(x => x.Id == id)
                .Include(a => a.Heroes)
                .SingleOrDefaultAsync();
    }

    public async Task<AppUser> GetUserByUserNameAsync(string userName)
    {
        return await _userManager.Users
            .SingleOrDefaultAsync(x => x.UserName == userName.ToLower());
    }

    public async Task<bool> SaveAllAsync()
    {
        return await _context.SaveChangesAsync() > 0;
    }

    public void Update(AppUser user)
    {
        _context.Entry(user).State = EntityState.Modified;
    }


    public async Task<bool> UserExists(string email)
    {
        return await _userManager.Users.AnyAsync(x => x.Email == email.ToLower());
    }

}

dbContext Declartion

            services.AddDbContext<DataContext>(options =>
        {
            options.UseSqlServer(config.GetConnectionString("DefaultConnection"));
        });

It stopped working after I manipulated the Hero repository and I’m not able to figure out what causing it…
I have tried everything in this thread: Entity Framework Core: A second operation started on this context before a previous operation completed
But nothing seems to work for me. I don’t really get why this type of issue happens.
Can someone please explain to me what causes that issue? And how can I improve myself to avoid this type of error?

Thanks!!!!

2

Answers


  1. Both PowerUp and DeleteHero have the following line:

    var user = await this.GetActiveUser();
    

    However, the content of GetActiveUser is:

    private Task<AppUser> GetActiveUser()
    {
        var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var user = this._userRepository.GetUserByIdAsync(userId);
    
        return user;
    }
    

    The returned value will always be null, as the second line does not await the result from the _userRepository.

    This may also be the source of your problem as another query is hitting the context before the second line in the above is complete.

    Try:

    var user = await this._userRepository.GetUserByIdAsync(userId);
    

    You could also try changing:

    return await _context.SaveChangesAsync() > 0;
    

    to

    return (await _context.SaveChangesAsync()) > 0;
    
    Login or Signup to reply.
  2. when you get this error ,make sure all awaitable methods has "await"

    Basically, I get this error in these situations

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search