3

Was fehlt mir?Warum ignoriert Entity Framework TransactionScope (nicht mit NOLOCK hinzufügen)?

Ich versuche, mit NOLOCK mit einer Transaction wie folgt zu lesen:

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(); 
} 

ich mit NOLOCK zu der SQL-Abfrage hinzugefügt, um zu sehen erwartet (in SQL Profiler und auch eine benutzerdefinierten DbCommandInterceptor - aber es ist nicht da ...

UPDATE: nach etwas mehr Forschung, frage ich mich, ob der ausgewählte Cursor überhaupt verwendet wird, nur ohne den NOLOCK "Hinweis" (SQL Server spezifisch - und auch spezifisch für nur eine Tabelle) Ich habe einen Code gefunden, der die aktuelle Transaktion enthält, und es scheint so zu sein ow die richtige ausgewählte Transaktionsisolations (ReadUncommitted/Serializable etc.) Ich möchte immer noch, es testen, aber lassen Sie mich wissen, wenn Sie irgendwelche Gedanken

Get current .net TransactionScope IsolationLevel

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

Antwort

5

So sieht es aus wie Entity Framework die Isolation nicht respektiert, nur, dass es nicht den NOLOCK-Hinweis zu verwenden (wahrscheinlich, weil es zu Datenbank spezifisch ist), und dies durch die Art und Weise meiner Hauptbeschwerde gegen EF -, dass es ist nicht sehr für verschiedene Datenbanktypen optimiert, ein anderes Beispiel ist, wo die neue Identität einen GUID-Primärschlüssel für AspNetUsers als String speichert (wiederum mangels Optimierung), abgesehen davon (und ein paar andere Dinge) EF ist genial!

ich keine Lösung für mein Problem überall finden konnte, wollte ich auf jeden Fall nicht alle meine Anfragen verwenden NOLOCK machen - nur die noch nicht übergeben, so dass ich am Ende zwei Lösungen kombiniert (mit einigen Änderungen):

  1. NoLockInterceptor - für das Hinzufügen von NOLOCK on the fly (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 - zum aufrufen der NoLockInterceptor Verhalten und auch nützlich für die wiederholte Verwendung von Transaktionen (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; 
         } 
        } 
    } 
    

es wie folgt verwendet:

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 jetzt nur auf Anfragen mit einer ReadUncommitted Transaktionsisolations hinzugefügt wird Level-Bereiche.

2

Sie können nicht Entity Framework das machen lassen zu NOLOCK Hinweis. Wenn Sie nicht festgeschriebene Daten lesen möchten, müssen Sie etwas anderes tun, als Sie getan haben, indem Sie TransactionScope mit IsolationLevel.ReadUncommited zu den TransactionOptions hinzufügen.

Schreiben Sie Ihre eigenen Befehl Abfangjäger oder Ihre eigenen EF-Provider würde auch funktionieren.

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

+0

TransactionScope verwendet, um mit früheren Versionen von EF zu arbeiten (vor etwa 4 Jahren), wissen Sie, ob das in EF6 geändert oder vielleicht ein Fehler? – Yovav

+0

Es funktioniert immer noch, aber es gibt neue API, die Transaktionen wie DbContext.Database.BeginTransaction und DbContext.Database.UseTransaction verarbeiten können. –

1

Ich habe versucht Transaktionsbereich und dann die Aufrufe der DB Profil. EF beginnt und beendet die Transaktion, ändert aber niemals die Isolationsstufe von Read Committed.