带有TransactionScope的EF6 - IsolationLevel.ReadUncommitted但首先获得了ReadCommitted

Kev*_*vin 5 c# sql-server transactionscope read-uncommitted entity-framework-6

在MSSQL 2008上使用EF进行查询更新时,存在性能和锁定问题.所以我把ReadUncommitted事务隔离级别,希望解决它,像这样,

之前

using (myEntities db = new myEntities())
{
            var data = from _Contact in db.Contact where _Contact.MemberId == 13 select _Contact; // large results
            for (var item I data)
                  item.Flag = 0;
            db.SaveChanges(); // Probably db lock
}
Run Code Online (Sandbox Code Playgroud)

using (var scope =
    new TransactionScope(TransactionScopeOption.RequiresNew,
    new TransactionOptions() { IsolationLevel = IsolationLevel.ReadUncommitted }))
{
    using (myEntities db = new myEntities ())
    {
            var data = from _Contact in db.Contact where _Contact.MemberId == 13 select _Contact; // large results but with nolock

            for (var item I data)
                  item.Flag = 0;
            db.SaveChanges(); // Try avoid db lock
    }
}
Run Code Online (Sandbox Code Playgroud)

我们使用SQL分析器来确认.但是,按顺序获取这些脚本,(对于第一个脚本,预计读取未提交.)

审核登录

set transaction isolation level read committed
Run Code Online (Sandbox Code Playgroud)

SP:StmtStarting

SELECT 
 [Extent1].[ContactId] AS [ContactId], 
 [Extent1].[MemberId] AS [MemberId], 
FROM [dbo].[Contact] AS [Extent1]
WHERE [Extent1].[MemberId] = @p__linq__0
Run Code Online (Sandbox Code Playgroud)

审核登录

set transaction isolation level read uncommitted
Run Code Online (Sandbox Code Playgroud)

虽然我可以重新发送此请求并使其顺序正确(将显示以下请求的读取 - 未提交,相同的SPID),我想知道为什么它在read-committed命令之后发送了read-uncommitted命令以及如何使用EF和TransactionScope进行修复?谢谢.

Chr*_*oll 5

我认为这是由于依赖审计登录事件而引起的。这没有显示客户端告诉服务器“设置事务隔离级别未提交读”的那一刻。它显示了以后从池中选择该连接并重新使用该连接时的隔离级别。

我通过添加Pooling=false到我的连接字符串来验证这一点。然后,审核登录始终显示事务隔离级别已读已提交。

到目前为止,在SQL事件探查器中,我发现无法看到EF设置事务级别的时刻,也没有任何显式的begin tran

我可以种确认的,它正在建立的地方,通过读取并记录级别:

    const string selectIsolationLevel = @"SELECT CASE transaction_isolation_level  WHEN 0 THEN 'Unspecified'  WHEN 1 THEN 'ReadUncommitted'  WHEN 2 THEN 'ReadCommitted'  WHEN 3 THEN 'Repeatable'  WHEN 4 THEN 'Serializable'  WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL  FROM sys.dm_exec_sessions  where session_id = @@SPID";

    static void ReadUncommitted()
    {
        using (var scope =
            new TransactionScope(TransactionScopeOption.RequiresNew,
            new TransactionOptions{ IsolationLevel = IsolationLevel.ReadUncommitted }))
        using (myEntities db = new myEntities())
        {
            Console.WriteLine("Read is about to be performed with isolation level {0}", 
                db.Database.SqlQuery(typeof(string), selectIsolationLevel).Cast<string>().First()
                );
            var data = from _Contact in db.Contact where _Contact.MemberId == 13 select _Contact; // large results but with nolock

            foreach (var item in data)
                item.Flag = 0;

            //Using Nuget package https://www.nuget.org/packages/Serilog.Sinks.Literate
            //logger = new Serilog.LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger();
            //logger.Information("{@scope}", scope);
            //logger.Information("{@scopeCurrentTransaction}", Transaction.Current);
            //logger.Information("{@dbCurrentTransaction}", db.Database.CurrentTransaction);

            //db.Database.ExecuteSqlCommand("-- about to save");
            db.SaveChanges(); // Try avoid db lock
            //db.Database.ExecuteSqlCommand("-- finished save");
            //scope.Complete();
        }
    }
Run Code Online (Sandbox Code Playgroud)

(我说“有点”,因为每个语句都在各自的会话中运行)

也许这是一个很长的说法,是的,即使您无法通过Profiler证明EF事务也可以正常工作。


Pat*_*ick 3

根据 ADO.NET 文档SQL Server 中的快照隔离中的以下注释,只要底层连接被池化,隔离级别就不会绑定到事务范围:

如果连接是池化的,则重置其隔离级别不会重置服务器上的隔离级别。因此,使用同一池内部连接的后续连接将其隔离级别设置为池连接的隔离级别。关闭连接池的另一种方法是为每个连接显式设置隔离级别。

因此,我得出的结论是,在 SQL Server 2012 之前,将隔离设置为任何其他级别都不ReadCommitted需要在创建有问题的 SqlConnection 时关闭连接池或显式设置每个连接中的隔离级别以避免意外行为(包括死锁)。或者,可以通过调用ClearPool Method来清除连接池,但由于此方法既不绑定到事务作用域,也不绑定到底层连接,所以我认为当多个连接针对同一池内部连接同时运行时,这是不合适的。

参考SQL 论坛中SQL Server 2014 重置隔离级别的帖子以及我自己的测试,当使用 SQL Server 2014 和 TDS 7.3 或更高版本的客户端驱动程序时,此类解决方法已过时。