使用NOLOCK的实体框架

One*_*Guy 136 c# ado.net entity-framework

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

Rei*_*ica 204

不,但您可以启动事务并将隔离级别设置为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();
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*dre 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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Fra*_*ain 26

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

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
Run Code Online (Sandbox Code Playgroud)

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

通过这种技术,我们能够创建一个简单的EF提供程序,为我们创建上下文,并且每次为我们的所有上下文实际运行此命令,以便我们始终处于"未提交读取"状态.

  • @DoctorJones - 查看:http://msdn.microsoft.com/en-us/library/ms173763.aspx.这是一个测试.在SSMS中,打开一个查询(#1)并运行:`CREATE TABLE ## Test(Col1 INT); BEGIN TRAN; SELECT*FROM ##测试WITH(TABLOCK,XLOCK);`.打开另一个查询(#2)并运行:`SELECT*FROM ## Test;`.SELECT将不会返回,因为它被正在使用独占锁的选项卡#1中的仍然打开的事务阻止.取消#2中的SELECT.在标签#2中运行一次"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED".在选项卡#2中再次运行SELECT,它将返回.一定要在标签#1中运行`ROLLBACK`. (5认同)
  • MSDN文档说:"控制由SQL Server连接发出的Transact-SQL语句的锁定和行版本控制行为." 和"指定语句可以读取已被其他事务修改但尚未提交的行." 我写的这个语句会影响每个SQL语句,它是否在事务内部.我不喜欢在网上与人发生冲突,但基于我们在大型生产环境中使用此声明,您明显错误.不要假设,试试他们! (3认同)
  • @DoctorJones - 关于Microsoft SQL Server,所有查询都是固有的事务.指定显式事务只是将2个或更多语句分组到_same_事务中的一种方法,这样它们就可以被视为原子工作单元."SET TRANSACTION ISOLATION LEVEL ..."命令会影响连接级属性,因此会影响从该点前进(对于THAT连接)所做的所有SQL语句,除非被查询提示覆盖.此行为至少从SQL Server 2000开始,可能在此之前. (3认同)
  • 单独设置事务隔离级别不会产生任何影响.实际上,您需要在事务中运行才能产生任何效果.READ UNCOMMITTED的MSDN文档声明`在READ UNCOMMITTED级别运行的事务不会发出共享锁.这意味着您必须在事务中运行才能获得收益.(摘自http://msdn.microsoft.com/en-gb/library/ms173763.aspx).您的方法可能不那么具有侵入性,但如果您不使用交易,它将无法实现任何目标. (2认同)

Yur*_*kiy 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)");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有了这个类,您可以在应用程序启动时应用它:

DbInterception.Add(new NoLockInterceptor());
Run Code Online (Sandbox Code Playgroud)

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

NoLockInterceptor.SuppressNoLock = true;
Run Code Online (Sandbox Code Playgroud)

  • (?<tableAlias> \] AS\[Extent\d + \](?!WITH \(NOLOCK \)))以防止将nolock添加到导致错误的派生表.:) (2认同)

myu*_*uce 9

加强琼斯博士接受的答案并使用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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,只要你需要它,

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

        }
    }
Run Code Online (Sandbox Code Playgroud)

能够使用拦截器添加"NOLOCK"也很不错,但是当连接到像Oracle这样的其他数据库系统时也无法工作.


小智 6

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


Ali*_*Ali 5

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

您可以在 EF6+ 和 EF Core 中使用 BeginTransaction 而不是 TransactionScope

using (var ctx = new ContractDbContext())
using (var transaction = ctx.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
    //any reads we do here will also read uncommitted data
}
Run Code Online (Sandbox Code Playgroud)