3

私は何が欠けていますか?

次のような TransactionScope を使用して NOLOCK で読み取ろうとしています。

var scopeOptions = new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted };
using (var scope = new TransactionScope(TransactionScopeOption.Required, scopeOptions))
{
   using (var db = new MyDbContext(ConnectionStringEntities))
   {
      // Simple read with a try catch block...
   }
   scope.Complete();
}

SQLクエリにNOLOCKが追加されていることを期待していました(SQLプロファイラーとカスタムDbCommandInterceptorを調べましたが、そこにはありません...

更新:さらに調査した結果、NOLOCK の「ヒント」(SQL Server 固有 - また、1 つのテーブルのみに固有) なしで、選択したカーソルが結局使用されているのではないかと思います。現在のトランザクションを取得するコードをいくつか見つけて、正しく選択されたトランザクションの分離 (ReadUncommitted / Serializable など) が表示されているようです。まだテストしたいのですが、ご意見があればお知らせください。

現在の .net TransactionScope IsolationLevel を取得する

Transaction trans = Transaction.Current;
System.Transactions.IsolationLevel level = trans.IsolationLevel;
LogService.Instance.Debug($"Transaction IsolationLevel = {level.ToString()}");
4

3 に答える 3

5

したがって、Entity FrameworkはIsolationLevelを尊重しているように見えますが、NOLOCKヒントを使用していないだけです(おそらくデータベース固有すぎるためです)。ちなみに、EFに対する私の主な不満は、さまざまなデータベースタイプに対してあまり最適化されていないことです。もう 1 つの例は、新しい ID が AspNetUsers の GUID プライマリ キーを文字列として保存している場合です (最適化が不足しているため)、それ以外 (およびその他のいくつか) EF は素晴らしいです!

どこにも問題の解決策が見つかりませんでした。すべてのクエリで NOLOCK を使用することは絶対にしたくありませんでした。

  1. NoLockInterceptor - オンザフライで NOLOCK を追加するため ( Entity Framework with NOLOCK ):

    /// <summary>
    /// Add "WITH (NOLOCK)" hint to SQL queries, SQL Server specifc - may break queries on different databases.
    /// (conditionally turn off with NoLockInterceptor.AddNoLockHintToSqlQueries = false to change on runtime)
    /// <para>
    /// https://stackoverflow.com/questions/926656/entity-framework-with-nolock
    /// </para>
    /// </summary>
    public class NoLockInterceptor : DbCommandInterceptor
    {
        private static readonly Regex TableAliasRegex = new Regex(
            @"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))",
            RegexOptions.Multiline | RegexOptions.IgnoreCase);
    
        /// <summary>
        /// Add "WITH (NOLOCK)" hint to SQL queries - unique to each thread 
        /// (set to true only when needed and then back to false)
        /// </summary>
        [ThreadStatic]
        public static bool AddNoLockHintToSqlQueries;
    
        public NoLockInterceptor()
        {
            // Do not use by default for all queries
            AddNoLockHintToSqlQueries = false;
        }
    
        public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
        {
            if (AddNoLockHintToSqlQueries)
            {
                command.CommandText = TableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
            }
        }
    
        public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
        {
            if (AddNoLockHintToSqlQueries)
            {
                command.CommandText = TableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
            }
        }
    }
    
  2. TransactionWrapper - NoLockInterceptor 動作を呼び出し、トランザクションの繰り返し使用にも役立ちます ( http://haacked.com/archive/2009/08/18/simpler-transactions.aspx/ ):

    /// <summary>
    /// Transaction wrapper for setting pre-defined transaction scopes
    /// <para>
    /// http://haacked.com/archive/2009/08/18/simpler-transactions.aspx/
    /// </para>
    /// </summary>
    public static class TransactionWrapper
    {
        /// <summary>
        /// Set transaction scope and using NoLockInterceptor for adding SQL Server specific "WITH (NOLOCK)" 
        /// to ReadUncommitted isolation level transactions (not supported by Entity Framework)
        /// </summary>
        /// <param name="isolationLevel"></param>
        /// <param name="transactionScopeOption"></param>
        /// <param name="timeout"></param>
        /// <param name="action"></param>
        public static void SetScope(IsolationLevel isolationLevel, TransactionScopeOption transactionScopeOption,
            TimeSpan timeout, Action action)
        {
            var transactionOptions = new TransactionOptions { IsolationLevel = isolationLevel, Timeout = timeout };
            using (var transactionScope = new TransactionScope(transactionScopeOption, transactionOptions))
            {
                if (isolationLevel == IsolationLevel.ReadUncommitted)
                    NoLockInterceptor.AddNoLockHintToSqlQueries = true;
    
                action();
                transactionScope.Complete();
    
                if (isolationLevel == IsolationLevel.ReadUncommitted)
                    NoLockInterceptor.AddNoLockHintToSqlQueries = false;
            }
        }
    }
    

次のように使用します。

var timeout = TimeSpan.FromSeconds(ConfigVariables.Instance.Timeout_Transaction_Default_In_Seconds);
TransactionWrapper.SetScope(IsolationLevel.ReadUncommitted, TransactionScopeOption.Required, timeout, () =>
{
    using (var db = new MyDbContext(MyDbContextConnectionStringEntities))
    {
       // Do stuff...
    }
});

NOLOCK は、ReadUncommitted トランザクション分離レベル スコープを持つクエリにのみ追加されるようになりました。

于 2016-04-04T13:03:18.643 に答える
2

Entity Framework で NOLOCK ヒントをレンダリングすることはできません。コミットされていないデータを読み取りたい場合は、TransactionOptions に IsolationLevel.ReadUncommited を指定して TransactionScope を追加することによって行ったのと同じように、別のことを行う必要があります。

独自のコマンド インターセプターまたは独自の EF プロバイダーを作成することもできます。

https://msdn.microsoft.com/en-us/data/dn469464.aspx

于 2016-04-03T14:54:33.427 に答える