使用Linq to SQL时的NOLOCK问题

79

如何让Linq2Sql在其SQL语句中发出NOLOCK指令? 如果可以的话,具体应该怎么做?


刚刚发现了这个问题,部分重叠,但还是要给出应有的赞誉:https://dev59.com/xnVD5IYBdhLWcg3wKoWH我会保持问题一段时间以防万一。 - Scott McKenzie
5个回答

95

没错,这里是我博客上的文章

The NOLOCK hint is essentially the same as wrapping a query in a transaction whose "isolation level" is set to "read uncommitted". It means that the query doesn't care if stuff is in the process of being written to the rows it's reading from - it'll read that "dirty" data and return it as part of the result set.

Turns out that you can do the whole "read uncommitted" transaction thing using the old System.Transactions namespace introduced in .NET 2.0. Here's some sample code:

using (var txn = new TransactionScope(
    TransactionScopeOption.Required, 
    new TransactionOptions
    {
        IsolationLevel = IsolationLevel.ReadUncommitted
    }
))
{
    // Your LINQ to SQL query goes here
}

So I'm creating a new TransactionScope object and telling it to use a read-uncommitted isolation level. The query within the "using" statement now acts as if all its tables were reading with the NOLOCK hint.

以下是在谷歌搜索 "linq sql nolock" 的第一批结果:

InfoQ:使用 LINQ to SQL 和 LINQ to Entities 实现 NOLOCK

Matt Hamilton - LINQ to SQL 和 NOLOCK 提示:真棒!

Scott Hanselman's Computer Zen - 获取 LINQ to SQL 和 LINQ to ...


6
如果我想要完全正确,这些选项实际上并没有在 SQL 中“发出 NOLOCK” - 它们使用事务的隔离设置。虽然这意思相同,但不符合问题的技术要求。 - Matt Hamilton
3
复制了您博客中的文本,这样就不必通过链接点击才能得到答案。 - Eric
2
@Matt:这就是重点!创建一个规范的来源!请参见:http://meta.stackexchange.com/questions/8724/how-to-deal-with-google-questions/8729#8729 - Eric
7
我同意Matt关于内容盗用的看法。Matt花时间在他的博客上写了这篇文章,现在SOF正在获得流量。 - Andrew Harry
5
在博客上无法点赞或踩回答,因此没有办法对其质量进行审核。我认为在 SO 上附带归属信息复制简短片段是正确的做法。 - Kirk Woll
显示剩余8条评论

26

关于King的LinqPad My Extensions添加的进一步说明:

public static IQueryable<T> DumpNoLock<T>(this IQueryable<T> query)
{
    using (var txn = GetNewReadUncommittedScope())
    {
        return query.Dump();
    }   
}

public static System.Transactions.TransactionScope GetNewReadUncommittedScope()
{
    return new System.Transactions.TransactionScope(
        System.Transactions.TransactionScopeOption.RequiresNew,
        new System.Transactions.TransactionOptions
        {
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
        });
}
public static IQueryable<T> DumpNoLock<T>(this IQueryable<T> query, string description)
{
    using (var txn = GetNewReadUncommittedScope())
    {
        return query.Dump(description);
    }   
}

public static List<T> ToListNoLock<T>(this IQueryable<T> query)
{
    using (var txn = GetNewReadUncommittedScope())
    {
        return query.ToList();
    }   
}

public static U NoLock<T,U>(this IQueryable<T> query, Func<IQueryable<T>,U> expr)
{
    using (var txn = GetNewReadUncommittedScope())
    {
        return expr(query);
    }   
}

最后一个意味着你可以在任何未显式写入NoLock的评估查询上执行NOLOCK(就像我上面为ToListNoLock所做的一样)。例如:

somequery.NoLock((x)=>x.Count()).Dump();

将使用 NOLOCK 评估查询。

请注意,您必须确保正在评估查询。例如,.NoLock((x)=>x.Distinct()).Count().Dump() 不会从 .Distinct().Count().Dump() 中有任何有用的区别。


11

一个简单的方法可能是在您的DataContext类上运行一个命令。

using (var dataContext = new DataContext())
{
  dataContext.ExecuteCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");

  // Your SQL query
}

也许这个答案嵌入了Kings的答案,会是一个不错的附加内容。 - Pierre

9

这里有一个与LINQPAD一起使用的扩展方法。

    public static IQueryable<T> Dump2<T>(this IQueryable<T> query)
{
    using (var txn = new System.Transactions.TransactionScope(TransactionScopeOption.RequiresNew, 
        new TransactionOptions
        {       
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted
        }))
    {
        return query.Dump();
    }   
}

然后您可以这样调用:

MyTable.Where(t => t.Title = "Blah").Dump2();

3
在我的情况下,Entity Framework 5(基于@Soppus的回答):
private FoobarEntities db = new FoobarEntities();
public FoobarController()
{
    db.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED");
}

1
谢谢,您的解决方案避免了其他请求可能出现的“COMException:事务管理器已禁用其支持远程/网络事务”的错误。 - Olivier de Rivoyre

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接