【问题标题】:Let a query read data before is committed - Sql Server让查询在提交之前读取数据 - Sql Server
【发布时间】:2018-04-24 22:16:36
【问题描述】:

我有一个名为 ProjectActivity 的表 主键:ProjectCode 和 ActivityId 以及 ProjectCode 上的索引 我们的网站在此表上生成查询(不涉及其他表)并始终请求具有相同 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 分钟)时,我们的网站无法执行任何查询(整个表被锁定)。 我们应该得到这个场景: 在提交交易之前,网站应该查询“旧”数据。这种场景可以实现吗?我可以弄清楚我们必须使用锁定和隔离级别来欺骗。

提前感谢您的任何提示。

【问题讨论】:

  • 也许您应该重新考虑清除然后重新填充您的项目代码集的决定 - 这听起来不是一种有效的方法。
  • 我不这么认为。我的活动经常更新。我应该检查每个 id 是否已插入,如果已插入,是否已更新某些列。

标签: sql-server c#-4.0 isolation-level


【解决方案1】:

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

当您想读取未提交的数据时,有一些方法可以使用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

【讨论】:

  • 简而言之,您所说的风险涉及数据库一致性,还是仅考虑我读取可能被删除的数据这一事实?
  • 关于隔离级别设置为ReadUncommitted的查询时读取过时或已删除数据的风险
【解决方案2】:

您向我指出了正确的方向,但我想我需要 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;
            }
        }

【讨论】:

  • 和我建议的一样,只是隔离级别不同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多