让查询在提交之前读取数据 - Sql 服务器

Let a query read data before is committed - Sql Server

我有一个 Table 名为 ProjectActivity 使用主键:ProjectCode 和 ActivityId 以及 ProjectCode 上的索引 我们的网站在此 table 上生成查询(不涉及其他 table)并且始终请求具有相同 ProjectCode

的行

有时我们在 c# 中启动一个作业,删除具有特定 ProjectCode 的每一行,并在事务中为该特定 ProjectCode 插入新行。

            using (var dbContextTransaction = db.Database.BeginTransaction())
        {
            try
            {

                db.Database.ExecuteSqlCommand("delete FROM [TS_Repository].[dbo].[ProjectActivities] where ProjectCode = {0} ", project.Code);

                LogManager.WriteRequestLog("Deleted Existing Project Activities");

                if (ProjectActivities.Count > 0)
                {
                    db.Set<ProjectActivity>().AddRange(ProjectActivities);
                    db.SaveChanges();

                }

                dbContextTransaction.Commit();

                LogManager.WriteRequestLog("Refresh ProjectActivities succesfully for " + project.Title);
            }
            catch (Exception exc)
            {

                dbContextTransaction.Rollback();
                throw exc;
            }
        }

我们的问题是,在处理此事务(3/4 分钟)时,我们的网站无法执行任何查询(整个 table 已锁定)。 我们应该得到这个场景: 在提交交易之前,网站应该查询 "old" 数据。这个场景可以实现吗?我可以弄清楚我们必须用锁和隔离级别来欺骗。

提前感谢您的任何提示。

是的,您可以使用特定的 隔离级别 执行查询,该级别可以在 table 或行被锁定时读取数据。我不会参与有关读取未提交数据的风险的讨论,我想您已经知道了,@iamdave 对此分享了一个很好的 link。

当你想读取未提交的数据时,EF有一些方法可以做到这一点,但一种简单明了的方法是使用事务。在 TransactionScope 构造函数中,您可以设置 IsolationLevel,如下所示:

using (new TransactionScope(
           TransactionScopeOption.Required, 
           new TransactionOptions 
           { 
              IsolationLevel = IsolationLevel.ReadUncommitted 
           })) 
{
        // here you put your code
}

这是另一个关于 EF 和隔离级别的好读物:entity-framework-and-transaction-isolation-level

你向我指出了正确的方向,但我想我需要 SNAPSHOT 隔离级别,因为我已经指定我需要阅读旧行版本。可能是我没解释的那么好...

所以,我已经决定使用 SNASPHOT:

using (var dbContextTransaction = db.Database.BeginTransaction(System.Data.IsolationLevel.Snapshot))
        {
            try
            {


                db.Database.ExecuteSqlCommand("delete FROM [TS_Repository].[dbo].[ProjectActivities] where ProjectCode = {0} ", project.Code);

                LogManager.WriteRequestLog("Deleted Existing Project Activities");

                if (ProjectActivities.Count > 0)
                {
                    db.Set<ProjectActivity>().AddRange(ProjectActivities);
                    db.SaveChanges();

                }

                dbContextTransaction.Commit();

                LogManager.WriteRequestLog("Refresh ProjectActivities succesfully for " + project.Title);
            }
            catch (Exception exc)
            {

                dbContextTransaction.Rollback();
                throw exc;
            }
        }