【问题标题】:MVC Core 2.0 EF Core SqlException: New transaction is not allowed because there are other threads running in the sessionMVC Core 2.0 EF Core SqlException:不允许新事务,因为会话中还有其他线程在运行
【发布时间】:2018-05-20 19:01:39
【问题描述】:

这个问题有很多线程,但大多数似乎是在有人试图循环并保存在 IQueryable 而不是 IList 的上下文中出现的,这不是我正在做的。

我在控制器和存储库之间传递上下文时遇到问题。我在我的应用程序的其他地方遇到了这个问题,但下面是说明和重新创建此问题的简单方法。

首先,我在 Startup.cs 中将我的 DbContext 设置为服务。

services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

然后,我有一个控制器,它被注入到其中。当我直接从控制器使用上下文时,前两个保存工作正常,但是当我从 repo 中使用它时,它会出错,并出现标题中的异常。请注意,如果我在尝试将记录保存在我的存储库中之前将它保存在控制器中,它只会出错。

 private ApplicationDbContext _context;
 private UserManager<ApplicationUser> _userManager;

 private ApplicationUser _user
    {
        get
        {
            return _userManager.GetUserAsync(User).Result;
        }
    }

 public InitiativesController(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
    {
        _context = context;
        _userManager = userManager;
    }

    // GET: Initiatives
    public IActionResult Index()
    {

        Initiative i2 = new Initiative();
        i2.Id = 2;
        i2.InitiativeName = "testname";
        _context.Update(i2);
        _context.SaveChanges(); // Works fine

        var i4 = _context.Initiatives.SingleOrDefault(init => init.Id == 2);
        i4.InitiativeName = "blep";
        _context.Update(i4);
        _context.SaveChanges(); // Works fine


        InitiativeRepository initiativeRepository = new InitiativeRepository(_context, _user);

        var i= initiativeRepository.Get(2);
        i.InitiativeName = "blah";
        initiativeRepository.Update(i); //Throws exception

        var initiatives = initiativeRepository.GetAll();
        return View(initiatives);
    }

这是存储库类:

public class InitiativeRepository
{
    private ApplicationDbContext _context;
    private ApplicationUser _user;

    public InitiativeRepository(ApplicationDbContext context, ApplicationUser user)
    {
        _context = context;
        _user = user;
    }

    public Initiative Get(int id, bool eagerLoad = false)
    {
        if (!eagerLoad)
        {
            return _context.Initiatives.SingleOrDefault(i => i.Id == id);
        }
        else
        {
            return _context.Initiatives.Include(i => i.Tasks).ThenInclude(t => t.ActionItems).
                SingleOrDefault(i => i.Id == id);
        }
    }

    public List<Initiative> GetAll()
    {
        return _context.Initiatives.ToList();
    }

    public Initiative Update(Initiative Entity)
    {
        _context.Update(Entity);
        _context.SaveChanges(); // This line causes the exception.
        return Entity;
    }

}

这应该是任何 DB 写入的入口点,但是,我使用的是 ASP.NET 身份并自动登录此会话,所以我不确定它是否也会影响这一点。

【问题讨论】:

  • 你确定前两个 Updates() 真的在做什么吗?我能想到的唯一解释是这些实际上是 NO-OP(因为 DB 中的数据已经是您尝试设置的内容),但第三个实际上是在尝试进行更改,但它可以t 因为您从该代码外部打开了活动结果集。
  • 是的,我可以更改字符串文字,并且可以看到数据库中反映的更新。我认为您是正确的,因为我在此代码之外打开了一个活动结果集。我关闭了 VS2017 并重新打开,这段代码现在运行良好,但在我的应用程序的某个时刻,另一个区域会出现相同的错误消息。我必须打开一些结果集而不关闭它,但我不知道在哪里。我也不明白为什么会这样断断续续。

标签: c# asp.net-mvc entity-framework asp.net-core-2.0 ef-core-2.0


【解决方案1】:

我不相信依赖注入总是会发生这样的事情。虽然这很烦人,但您可能会得到更好的服务,要么在你的 repo 中做所有事情,要么通过以下方式关闭和处理你的上下文:

using(var context = new context("yourContext"))
{
   //dowork
   context.SaveChanges();  
}

虽然不应该发生,但可能会发生什么,这只是一个猜测。是否注入在传入时捕获了错误的上下文状态。您可以在 repo 的构造函数中进行依赖注入,然后直接在 repo 中使用 CRUD 操作执行所有操作。

private InitiativeRepository _initiativeRepository;


public InitiativesController(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
    _context = context;
    _userManager = userManager;
    _initiativeRepository = new InitiativeRepository(_context, _user);

}

我目前使用带有控制器的 .NET Core 2 Web Api 执行上述模式。我知道任何时候我保留了一个上下文并且没有关闭它,然后将该上下文与另一个使用它的状态混合在一起,我就会遇到麻烦。我也有过不为上下文使用依赖注入的想法,因此他们将其设为默认值的新 .NET Core 内容很有趣。在大型数据库和多用户应用程序完成大量工作之前,它可能会很好用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-26
    • 2016-07-07
    • 2017-03-27
    • 2012-09-07
    相关资源
    最近更新 更多