【问题标题】:Entity Framework get the next available row exclusively实体框架专门获取下一个可用行
【发布时间】:2014-12-19 10:05:09
【问题描述】:

我在带有服务层的 MVC 应用程序中使用 Entity Framework 6.1.1(SQL Server 后端)。

我想从服务层读取数据库以从数据库中获取一行,更新其上的某些内容以告知正在处理它以停止对它的下一次读取,然后返回它。

所以基本上如果 2 个用户调用相同的服务,他们都会得到不同的行,因为第一次读取会更新记录(比如将“处理”列更改为 true)。第二次读取将绕过这条记录并转到下一条。

我想不出在 EF 中使用普通 linq 查询的方法。我能想到的唯一方法是使用存储过程。虽然我还不是 100% 确定如何在 SP 中做到这一点。如果这是唯一的选择,那么我会进行更多调查。

这可以使用 EF 完成吗?

【问题讨论】:

  • 这一切发生的太快了,根本不重要吗?
  • 你可以只选择处理 为 true 的行并以这种方式获得下一行吗?
  • @Derek 在繁忙的网站上,它肯定会发生。读取、更新和保存所需的时间会发生变化。
  • @timothyclifford 这只会得到下一行,不会阻止下一个人得到它。
  • 您可以在 DbContext 中锁定对表的访问,以确保 2 个用户不会得到相同的结果(获取行、更新、保存 lock 块中的更改。类似于模仿存储过程。跨度>

标签: c# entity-framework linq-to-entities


【解决方案1】:

看看这篇文章。它与 SQL 事务隔离级别有关。

SQl 中的默认隔离级别是READ COMMITED

这意味着当您对表中的行提交更改时,在同一时间段内之后的任何查询都必须等待事务完成才能运行查询。

这是一个链接:-

http://gavindraper.com/2012/02/18/sql-server-isolation-levels-by-example/

希望对你有帮助,

我很确定你走错了路。

【讨论】:

  • 感谢您的回答。你能告诉我为什么我走错路了吗?基本上,我有一个要处理的行数的表。用户将要求他们处理下一行,这将是他们使用的。会有很多用户,他们不能在同一个 rowm 上工作。
  • 另外,“Read Committed”是默认设置。但是,我相信在“选择”完成之后才会开始交易。我想我实际上可以按照以下stackoverflow.com/questions/13404061/… 更改事务范围
【解决方案2】:

我不确定此解决方案的可行性,它很简单,并且可能会因为锁定在数据层(而不是在数据库中)而最终降低您的服务性能。

public class MyDbContext : DbContext 
{
    private DbSet<Data> DataTable { get { return this.Set<Data>(); } }

    private static object lockObject = new object();
    public Data GetDataToProcess() 
    { 
       lock (lockObject) 
       {
          Data data = this.DataTable.FirstOrDefault(d => d.Processing == false);
          data.Processing = true;
          this.SaveChanges();
          return data;
       }
    }
 }

然后在您的服务中使用GetDataToProcess 方法,而不是DataTable 属性。

注意:此代码只是一个示例,可能不是正确的方法,它只是一种解决方法。它还需要完成空检查,并可能通过将GetDataToProcess 提取到其他将在内部使用 MyDbContext 实例的类来重构。

【讨论】:

  • 感谢您的回答。如果每次读取都使用相同的上下文,这将起作用。但有些情况意味着情况并非如此。 Web api 和 mvc 应用程序、Web 农场等的不同项目
  • 是的,这只是一个简单的想法。然后你必须将访问检查下移到共享层,即数据库。
【解决方案3】:

我发现这篇文章可能是我需要的。我需要进一步调查

How can I lock a table on read, using Entity Framework?

但基本上看起来您像这样更改特定读取/更新的隔离级别:

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }))
{
   var newUrl = dbEntity.URLs.FirstOrDefault(url => url.StatusID == (int) URLStatus.New);
   if(newUrl != null)
   {
      newUrl.StatusID = (int) URLStatus.InProcess;
      dbEntity.SaveChanges();
   }
   scope.Complete();
}

显然使用我自己的实体/逻辑等

【讨论】:

    【解决方案4】:

    我决定让存储过程完成工作。原因是,按照我的工作单元/代表/服务层的分层方式,这样做更合乎逻辑

        WITH cte AS
        (
            SELECT TOP 1 *
            FROM   ***
            WHERE  ***
            ORDER BY ***
        )
       UPDATE cte WITH (ROWLOCK, READPAST)
       SET
          ***
       OUTPUT INSERTED.*
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-20
      • 1970-01-01
      相关资源
      最近更新 更多