Asp.Net 在上一个操作完成之前在此上下文中启动了第二个操作

标签 asp.net asp.net-web-api

出于某种原因,来 self 的 Controller 的 2 个方法触发了此错误: 保存上下文类型“API.Data.DataContext”的更改时数据库发生异常。 System.InvalidOperationException:在上一个操作完成之前,在此上下文中启动了第二个操作。这通常是不同线程同时使用同一个 DbContext 实例造成的

我已经尝试了这个线程中的所有内容:Entity Framework Core:Entity Framework Core: A second operation started on this context before a previous operation completed但似乎没有什么对我有用。

它对我来说不同,因为我不能将 dbContext 用作 tranziant

问题发生在 PowerUp 和 DeleteHero 方法中

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

英雄库:

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

用户存储库

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 声明

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

在我操作 Hero 存储库后它停止工作,我无法弄清楚是什么原因造成的... 我已经尝试了这个线程中的所有内容:Entity Framework Core: A second operation started on this context before a previous operation completed 但似乎没有什么对我有用。我真的不明白为什么会发生这种类型的问题。 有人可以向我解释导致该问题的原因吗?我该如何改进自己以避免此类错误?

谢谢!!!!

最佳答案

PowerUp 和 DeleteHero 都有以下行:

var user = await this.GetActiveUser();

但是GetActiveUser的内容是:

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

    return user;
}

返回值将始终为 null,因为第二行不等待 _userRepository 的结果。

这也可能是您问题的根源,因为在上面的第二行完成之前,另一个查询正在命中上下文。

尝试:

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

你也可以尝试改变:

return await _context.SaveChangesAsync() > 0;

return (await _context.SaveChangesAsync()) > 0;

关于Asp.Net 在上一个操作完成之前在此上下文中启动了第二个操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70072714/

相关文章:

c# - 如何从单元格内的控件获取表格行

xamarin.ios - Monotouch 使用 ASP.NET Web Api

.net - 在 DelegatingHandler 中设置 WWW-Authenticate 响应头会导致对 w2k3 的破坏

jquery - 在datalist中找到div并提交按钮

javascript - jcarousel 也可以与数据库一起使用吗?

asp.net - Visual Studio 中的慢 "Find All References"

c# - 为什么 VS 2015 Web Api 项目模板不使用 app.UseWebApi Extension

c# - WebApi 返回 HTTP 响应后是否可以执行代码?

jquery - POST到Web API OData,接收到的实体为null

mysql - 运行没有强名称签名的第三方程序集的自定义版本