使用NOLOCK的实体框架

时间:2009-05-29 15:38:49

标签: entity-framework ado.net

如何在Entity Framework上使用NOLOCK功能? XML是唯一的方法吗?

9 个答案:

答案 0 :(得分:201)

不,但您可以启动交易并设置isolation level to read uncommited。这基本上与NOLOCK相同,但它不是基于每个表执行,而是为事务范围内的所有内容执行此操作。

如果这听起来像你想要的那样,这就是你如何去做...

//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
    System.Transactions.TransactionScopeOption.Required, 
    transactionOptions)
)

//declare our context
using (var context = new MyEntityConnection())
{
    //any reads we do here will also read uncomitted data
    //...
    //...
    //don't forget to complete the transaction scope
    transactionScope.Complete();
}

答案 1 :(得分:81)

扩展方法可以使这更容易

public static List<T> ToListReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        List<T> toReturn = query.ToList();
        scope.Complete();
        return toReturn;
    }
}

public static int CountReadUncommitted<T>(this IQueryable<T> query)
{
    using (var scope = new TransactionScope(
        TransactionScopeOption.Required, 
        new TransactionOptions() { 
            IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
    {
        int toReturn = query.Count();
        scope.Complete();
        return toReturn;
    }
}

答案 2 :(得分:26)

如果你需要大量的东西,我们找到的最好的方法是每次实际启动一个事务管理器,那么只需通过运行这个简单的方法在创建对象上下文之后设置连接上的默认事务隔离级别命令:

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx

使用这种技术,我们能够创建一个简单的EF提供程序,为我们创建上下文,并且每次为所有上下文实际运行此命令,以便我们始终默认为“未提交读取”。

答案 3 :(得分:19)

虽然我绝对同意使用Read Uncommitted事务隔离级别是最好的选择,但是有些时候你被迫通过经理或客户端的请求使用NOLOCK提示而没有理由反对这种接受。

使用Entity Framework 6,您可以像这样实现自己的DbCommandInterceptor:

public class NoLockInterceptor : DbCommandInterceptor
{
    private static readonly Regex _tableAliasRegex = 
        new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))", 
            RegexOptions.Multiline | RegexOptions.IgnoreCase);

    [ThreadStatic]
    public static bool SuppressNoLock;

    public override void ScalarExecuting(DbCommand command, 
        DbCommandInterceptionContext<object> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        if (!SuppressNoLock)
        {
            command.CommandText = 
                _tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
        }
    }
}

使用此类,您可以在应用程序启动时应用它:

DbInterception.Add(new NoLockInterceptor());

并有条件地关闭将NOLOCK提示添加到当前线程的查询中:

NoLockInterceptor.SuppressNoLock = true;

答案 4 :(得分:9)

加强Doctor Jones已接受的答案并使用PostSharp;

首先“ ReadUncommitedTransactionScopeAttribute

[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        //declare the transaction options
        var transactionOptions = new TransactionOptions();
        //set it to read uncommited
        transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
        //create the transaction scope, passing our options in
        using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
        {
            //declare our context
            using (var scope = new TransactionScope())
            {
                args.Proceed();
                scope.Complete();
            }
        }
    }
}

然后,只要你需要,

    [ReadUncommitedTransactionScope()]
    public static SomeEntities[] GetSomeEntities()
    {
        using (var context = new MyEntityConnection())
        {
            //any reads we do here will also read uncomitted data
            //...
            //...

        }
    }

能够使用拦截器添加“NOLOCK”也很不错,但在连接到其他数据库系统(如Oracle)时无法正常工作。

答案 5 :(得分:6)

为了解决这个问题,我在数据库上创建了一个视图,并在视图的查询中应用了NOLOCK。然后我将视图视为EF中的表。

答案 6 :(得分:2)

不,不是真的 - 实体框架基本上是一个比实际数据库更严格的层。您的查询是在ESQL - Entity SQL中制定的 - 它首先针对您的实体模型,并且由于EF支持多个数据库后端,因此您无法将“本机”SQL直接发送到您的后端。

NOLOCK查询提示是SQL Server特有的,不会对任何其他受支持的数据库起作用(除非他们也实现了相同的提示 - 我强烈怀疑)。

马克

答案 7 :(得分:2)

随着EF6的推出,Microsoft建议使用BeginTransaction()方法。

您可以在EF6 +和EF Core

中使用BeginTransaction而不是TransactionScope
{{1}}

答案 8 :(得分:0)

一种选择是使用存储过程(类似于Ryan提出的视图解决方案),然后从EF执行存储过程。这样,存储过程执行脏读,而EF只管理结果。