Freigeben über


Tutorial: Verwenden von Verbindungsresilienz und Befehlsinterception mit Entity Framework in einer ASP.NET MVC-App

Bisher wurde die Anwendung lokal in IIS Express auf Ihrem Entwicklungscomputer ausgeführt. Um eine echte Anwendung für andere Benutzer über das Internet verfügbar zu machen, müssen Sie sie auf einem Webhostinganbieter bereitstellen und die Datenbank auf einem Datenbankserver bereitstellen.

In diesem Tutorial erfahren Sie, wie Sie die Verbindungsresilienz und das Abfangen von Befehlen verwenden. Es handelt sich um zwei wichtige Features von Entity Framework 6, die besonders nützlich sind, wenn Sie in der Cloudumgebung bereitstellen: Verbindungsresilienz (automatische Wiederholungsversuche für vorübergehende Fehler) und Befehlsinterception (erfassen Sie alle an die Datenbank gesendeten SQL-Abfragen, um sie zu protokollieren oder zu ändern).

Dieses Tutorial zur Verbindungsresilienz und Zum Abfangen von Befehlen ist optional. Wenn Sie dieses Tutorial überspringen, müssen einige kleinere Anpassungen in den nachfolgenden Tutorials vorgenommen werden.

In diesem Tutorial:

  • Aktivieren der Verbindungsresilienz
  • Aktivieren der Befehlsinterception
  • Testen der neuen Konfiguration

Voraussetzungen

Aktivieren der Verbindungsresilienz

Wenn Sie die Anwendung in Windows Azure bereitstellen, stellen Sie die Datenbank in Windows Azure SQL Database, einem Clouddatenbankdienst, bereit. Vorübergehende Verbindungsfehler treten in der Regel häufiger auf, wenn Sie eine Verbindung mit einem Clouddatenbankdienst herstellen, als wenn Ihr Webserver und Ihr Datenbankserver direkt im selben Rechenzentrum verbunden sind. Selbst wenn ein Cloudwebserver und ein Clouddatenbankdienst im selben Rechenzentrum gehostet werden, gibt es mehr Netzwerkverbindungen zwischen ihnen, die Probleme haben können, z. B. Lastenausgleichsmodule.

Auch ein Clouddienst wird in der Regel von anderen Benutzern gemeinsam genutzt, was bedeutet, dass seine Reaktionsfähigkeit von ihnen beeinflusst werden kann. Und Ihr Zugriff auf die Datenbank unterliegt möglicherweise einer Drosselung. Drosselung bedeutet, dass der Datenbankdienst Ausnahmen auslöst, wenn Sie versuchen, häufiger darauf zuzugreifen, als in Ihrer Vereinbarung zum Servicelevel (SLA) zulässig ist.

Viele oder die meisten Verbindungsprobleme, wenn Sie auf einen Clouddienst zugreifen, sind vorübergehend, d. h., sie lösen sich in kurzer Zeit von selbst. Wenn Sie also einen Datenbankvorgang ausprobieren und eine Art von Fehler erhalten, der in der Regel vorübergehend ist, können Sie den Vorgang nach einer kurzen Wartezeit erneut versuchen, und der Vorgang ist möglicherweise erfolgreich. Sie können Ihren Benutzern eine viel bessere Erfahrung bieten, wenn Sie vorübergehende Fehler behandeln, indem Sie es automatisch erneut versuchen, sodass die meisten von ihnen für den Kunden unsichtbar sind. Das Feature zur Verbindungsresilienz in Entity Framework 6 automatisiert diesen Prozess der Wiederholung fehlgeschlagener SQL-Abfragen.

Die Verbindungsresilienzfunktion muss für einen bestimmten Datenbankdienst entsprechend konfiguriert werden:

  • Sie muss wissen, welche Ausnahmen wahrscheinlich vorübergehend sind. Sie möchten Fehler wiederholen, die durch einen vorübergehenden Verlust der Netzwerkkonnektivität und nicht durch Programmfehler verursacht werden, z. B. .
  • Es muss eine angemessene Zeit zwischen Wiederholungen eines fehlgeschlagenen Vorgangs warten. Sie können zwischen Wiederholungsversuchen länger auf einen Batchprozess warten als für eine Onlinewebseite, auf der ein Benutzer auf eine Antwort wartet.
  • Es muss eine angemessene Anzahl von Wiederholungen versuchen, bevor es aufgibt. Möglicherweise möchten Sie es in einem Batchprozess häufiger wiederholen als in einer Onlineanwendung.

Sie können diese Einstellungen manuell für jede Datenbankumgebung konfigurieren, die von einem Entity Framework-Anbieter unterstützt wird. Standardwerte, die in der Regel gut für eine Onlineanwendung funktionieren, die Windows Azure SQL Database verwendet, wurden jedoch bereits für Sie konfiguriert, und dies sind die Einstellungen, die Sie für die Contoso University-Anwendung implementieren werden.

Zum Aktivieren der Verbindungsresilienz müssen Sie nur eine Klasse in Ihrer Assembly erstellen, die von der DbConfiguration-Klasse abgeleitet ist, und in dieser Klasse die SQL-Datenbank Ausführungsstrategie festlegen, die in EF ein weiterer Begriff für Wiederholungsrichtlinie ist.

  1. Fügen Sie im Ordner DAL eine Klassendatei mit dem Namen SchoolConfiguration.cs hinzu.

  2. Ersetzen Sie den Vorlagencode durch den folgenden Code:

    using System.Data.Entity;
    using System.Data.Entity.SqlServer;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolConfiguration : DbConfiguration
        {
            public SchoolConfiguration()
            {
                SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
            }
        }
    }
    

    Das Entity Framework führt den gefundenen Code automatisch in einer Klasse aus, die von abgeleitet wird DbConfiguration. Sie können die DbConfiguration -Klasse verwenden, um Konfigurationsaufgaben im Code auszuführen, die Sie andernfalls in der Web.config-Datei ausführen würden. Weitere Informationen finden Sie unter EntityFramework Code-Based Konfiguration.

  3. Fügen Sie in StudentController.cs eine using -Anweisung für System.Data.Entity.Infrastructurehinzu.

    using System.Data.Entity.Infrastructure;
    
  4. Ändern Sie alle Blöcke, die catch Ausnahmen abfangen DataException , sodass sie stattdessen Ausnahmen abfangen RetryLimitExceededException . Zum Beispiel:

    catch (RetryLimitExceededException /* dex */)
    {
        //Log the error (uncomment dex variable name and add a line here to write a log.
        ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
    }
    

    Sie haben verwendet DataException , um Fehler zu identifizieren, die vorübergehend sein könnten, um eine freundliche Meldung "Versuchen Sie es erneut" zu geben. Nachdem Sie jedoch eine Wiederholungsrichtlinie aktiviert haben, wurden die einzigen Fehler, die wahrscheinlich vorübergehend sind, bereits mehrmals versucht und fehlgeschlagen, und die tatsächlich zurückgegebene Ausnahme wird in die RetryLimitExceededException Ausnahme eingeschlossen.

Weitere Informationen finden Sie unter Entity Framework-Verbindungsresilienz/Wiederholungslogik.

Aktivieren der Befehlsinterception

Wie testen Sie nun, nachdem Sie eine Wiederholungsrichtlinie aktiviert haben, um zu überprüfen, ob sie wie erwartet funktioniert? Es ist nicht so einfach, einen vorübergehenden Fehler zu erzwingen, insbesondere wenn Sie lokal ausführen, und es wäre besonders schwierig, tatsächliche vorübergehende Fehler in einen automatisierten Komponententest zu integrieren. Um die Verbindungsresilienz zu testen, benötigen Sie eine Möglichkeit, Abfragen abzufangen, die Entity Framework an SQL Server sendet, und die SQL Server Antwort durch einen Ausnahmetyp zu ersetzen, der normalerweise vorübergehend ist.

Sie können auch die Abfrageinterception verwenden, um eine bewährte Methode für Cloudanwendungen zu implementieren: Protokollieren sie die Latenz und den Erfolg oder Fehler aller Aufrufe externer Dienste wie Datenbankdienste. EF6 bietet eine dedizierte Protokollierungs-API , die die Protokollierung vereinfachen kann. In diesem Abschnitt des Tutorials erfahren Sie jedoch, wie Sie die Abfangfunktion von Entity Framework direkt verwenden, sowohl für die Protokollierung als auch für die Simulation vorübergehender Fehler.

Erstellen einer Protokollierungsschnittstelle und -klasse

Eine bewährte Methode für die Protokollierung besteht darin, dies mithilfe einer Schnittstelle zu tun, anstatt Aufrufe von System.Diagnostics.Trace oder einer Protokollierungsklasse hart zu codieren. Dies erleichtert es, den Protokollierungsmechanismus später zu ändern, wenn Sie dies jemals tun müssen. In diesem Abschnitt erstellen Sie also die Protokollierungsschnittstelle und eine Klasse, um sie zu implementieren./p>

  1. Erstellen Sie einen Ordner im Projekt, und nennen Sie ihn Protokollierung.

  2. Erstellen Sie im Ordner Logging eine Klassendatei mit dem Namen ILogger.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code:

    using System;
    
    namespace ContosoUniversity.Logging
    {
        public interface ILogger
        {
            void Information(string message);
            void Information(string fmt, params object[] vars);
            void Information(Exception exception, string fmt, params object[] vars);
    
            void Warning(string message);
            void Warning(string fmt, params object[] vars);
            void Warning(Exception exception, string fmt, params object[] vars);
    
            void Error(string message);
            void Error(string fmt, params object[] vars);
            void Error(Exception exception, string fmt, params object[] vars);
    
            void TraceApi(string componentName, string method, TimeSpan timespan);
            void TraceApi(string componentName, string method, TimeSpan timespan, string properties);
            void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars);
        }
    }
    

    Die -Schnittstelle bietet drei Ablaufverfolgungsebenen, um die relative Wichtigkeit von Protokollen anzugeben, und eine, die für die Bereitstellung von Latenzinformationen für externe Dienstaufrufe wie Datenbankabfragen konzipiert ist. Die Protokollierungsmethoden verfügen über Überladungen, mit denen Sie eine Ausnahme übergeben können. Dies ist der Fall, sodass Ausnahmeinformationen, einschließlich Stapelüberwachung und inneren Ausnahmen, zuverlässig von der Klasse protokolliert werden, die die Schnittstelle implementiert, anstatt sich darauf zu verlassen, dass dies bei jedem Protokollierungsmethodenaufruf in der gesamten Anwendung erfolgt.

    Mit den TraceApi-Methoden können Sie die Latenz jedes Aufrufs eines externen Diensts wie z. B. SQL-Datenbank nachverfolgen.

  3. Erstellen Sie im Ordner Logging eine Klassendatei namens Logger.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code:

    using System;
    using System.Diagnostics;
    using System.Text;
    
    namespace ContosoUniversity.Logging
    {
        public class Logger : ILogger
        {
            public void Information(string message)
            {
                Trace.TraceInformation(message);
            }
    
            public void Information(string fmt, params object[] vars)
            {
                Trace.TraceInformation(fmt, vars);
            }
    
            public void Information(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceInformation(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void Warning(string message)
            {
                Trace.TraceWarning(message);
            }
    
            public void Warning(string fmt, params object[] vars)
            {
                Trace.TraceWarning(fmt, vars);
            }
    
            public void Warning(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceWarning(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void Error(string message)
            {
                Trace.TraceError(message);
            }
    
            public void Error(string fmt, params object[] vars)
            {
                Trace.TraceError(fmt, vars);
            }
    
            public void Error(Exception exception, string fmt, params object[] vars)
            {
                Trace.TraceError(FormatExceptionMessage(exception, fmt, vars));
            }
    
            public void TraceApi(string componentName, string method, TimeSpan timespan)
            {
                TraceApi(componentName, method, timespan, ""); 
            }
    
            public void TraceApi(string componentName, string method, TimeSpan timespan, string fmt, params object[] vars)
            {
                TraceApi(componentName, method, timespan, string.Format(fmt, vars));
            }
            public void TraceApi(string componentName, string method, TimeSpan timespan, string properties)
            {
                string message = String.Concat("Component:", componentName, ";Method:", method, ";Timespan:", timespan.ToString(), ";Properties:", properties);
                Trace.TraceInformation(message);
            }
    
            private static string FormatExceptionMessage(Exception exception, string fmt, object[] vars)
            {
                // Simple exception formatting: for a more comprehensive version see 
                // https://code.msdn.microsoft.com/windowsazure/Fix-It-app-for-Building-cdd80df4
                var sb = new StringBuilder();
                sb.Append(string.Format(fmt, vars));
                sb.Append(" Exception: ");
                sb.Append(exception.ToString());
                return  sb.ToString();
            }
        }
    }
    

    Die Implementierung verwendet System.Diagnostics, um die Ablaufverfolgung durchzuführen. Dies ist ein integriertes Feature von .NET, das das Generieren und Verwenden von Ablaufverfolgungsinformationen erleichtert. Es gibt viele "Listener", die Sie mit der System.Diagnostics-Ablaufverfolgung verwenden können, um z. B. Protokolle in Dateien zu schreiben oder sie in Blobspeicher in Azure zu schreiben. Weitere Informationen finden Sie unter Problembehandlung für Azure-Websites in Visual Studio unter Einige der Optionen und Links zu anderen Ressourcen. In diesem Tutorial sehen Sie sich nur Protokolle im Visual Studio-Ausgabefenster an .

    In einer Produktionsanwendung sollten Sie möglicherweise andere Ablaufverfolgungspakete als System.Diagnostics in Betracht ziehen, und die ILogger-Schnittstelle macht es relativ einfach, zu einem anderen Ablaufverfolgungsmechanismus zu wechseln, wenn Sie sich dafür entscheiden.

Erstellen von Interceptorklassen

Als Nächstes erstellen Sie die Klassen, die das Entity Framework jedes Mal aufruft, wenn es eine Abfrage an die Datenbank sendet, eine zum Simulieren vorübergehender Fehler und eine für die Protokollierung. Diese Interceptorklassen müssen von der DbCommandInterceptor -Klasse abgeleitet werden. In sie schreiben Sie Methodenüberschreibungen, die automatisch aufgerufen werden, wenn die Abfrage ausgeführt werden soll. Mit diesen Methoden können Sie die Abfrage untersuchen oder protokollieren, die an die Datenbank gesendet wird, und Sie können die Abfrage ändern, bevor sie an die Datenbank gesendet wird, oder sie selbst an Entity Framework zurückgeben, ohne die Abfrage an die Datenbank zu übergeben.

  1. Um die Interceptorklasse zu erstellen, die jede an die Datenbank gesendete SQL-Abfrage protokolliert, erstellen Sie im Ordner DAL eine Klassendatei namens SchoolInterceptorLogging.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorLogging : DbCommandInterceptor
        {
            private ILogger _logger = new Logger();
            private readonly Stopwatch _stopwatch = new Stopwatch();
    
            public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                base.ScalarExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ScalarExecuted(command, interceptionContext);
            }
    
            public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                base.NonQueryExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.NonQueryExecuted(command, interceptionContext);
            }
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                base.ReaderExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
            public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ReaderExecuted(command, interceptionContext);
            }
        }
    }
    

    Für erfolgreiche Abfragen oder Befehle schreibt dieser Code ein Informationsprotokoll mit Latenzinformationen. Für Ausnahmen wird ein Fehlerprotokoll erstellt.

  2. Um die Interceptorklasse zu erstellen, die dummy transiente Fehler generiert, wenn Sie "Throw" in das Suchfeld eingeben, erstellen Sie im Ordner DAL eine Klassendatei namens SchoolInterceptorTransientErrors.cs, und ersetzen Sie den Vorlagencode durch den folgenden Code:

    using System;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure.Interception;
    using System.Data.Entity.SqlServer;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Reflection;
    using System.Linq;
    using ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorTransientErrors : DbCommandInterceptor
        {
            private int _counter = 0;
            private ILogger _logger = new Logger();
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                bool throwTransientErrors = false;
                if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "%Throw%")
                {
                    throwTransientErrors = true;
                    command.Parameters[0].Value = "%an%";
                    command.Parameters[1].Value = "%an%";
                }
    
                if (throwTransientErrors && _counter < 4)
                {
                    _logger.Information("Returning transient error for command: {0}", command.CommandText);
                    _counter++;
                    interceptionContext.Exception = CreateDummySqlException();
                }
            }
    
            private SqlException CreateDummySqlException()
            {
                // The instance of SQL Server you attempted to connect to does not support encryption
                var sqlErrorNumber = 20;
    
                var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single();
                var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 });
    
                var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true);
                var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic);
                addMethod.Invoke(errorCollection, new[] { sqlError });
    
                var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single();
                var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() });
    
                return sqlException;
            }
        }
    }
    

    Dieser Code überschreibt nur die ReaderExecuting -Methode, die für Abfragen aufgerufen wird, die mehrere Datenzeilen zurückgeben können. Wenn Sie die Verbindungsresilienz für andere Arten von Abfragen überprüfen möchten, können Sie auch die NonQueryExecuting Methoden und ScalarExecuting überschreiben, wie es der Protokollierungs-Interceptor tut.

    Wenn Sie die Seite Student ausführen und "Throw" als Suchzeichenfolge eingeben, erstellt dieser Code einen Dummy SQL-Datenbank Ausnahme für Fehlernummer 20, ein Typ, der in der Regel vorübergehend ist. Andere Fehlernummern, die derzeit als vorübergehend erkannt werden, sind 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 und 40613, aber diese können sich in neuen Versionen von SQL-Datenbank ändern.

    Der Code gibt die Ausnahme an Entity Framework zurück, anstatt die Abfrage auszuführen und Abfrageergebnisse zurückzugeben. Die vorübergehende Ausnahme wird viermal zurückgegeben, und dann wird der Code auf die normale Prozedur zum Übergeben der Abfrage an die Datenbank zurückgesetzt.

    Da alles protokolliert wird, können Sie sehen, dass Entity Framework vierMal versucht, die Abfrage auszuführen, bevor sie schließlich erfolgreich ausgeführt wird. Der einzige Unterschied in der Anwendung besteht darin, dass das Rendern einer Seite mit Abfrageergebnissen länger dauert.

    Die Anzahl der Wiederholungsversuche von Entity Framework ist konfigurierbar. Der Code gibt vierMal an, da dies der Standardwert für die SQL-Datenbank-Ausführungsrichtlinie ist. Wenn Sie die Ausführungsrichtlinie ändern, ändern Sie hier auch den Code, der angibt, wie oft vorübergehende Fehler generiert werden. Sie können den Code auch ändern, um weitere Ausnahmen zu generieren, sodass Entity Framework die RetryLimitExceededException Ausnahme auslöst.

    Der Wert, den Sie in das Feld Suchen eingeben, befindet sich in command.Parameters[0] und command.Parameters[1] (einer wird für den Vornamen und einer für den Nachnamen verwendet). Wenn der Wert "%Throw%" gefunden wird, wird "Throw" in diesen Parametern durch "an" ersetzt, sodass einige Kursteilnehmer gefunden und zurückgegeben werden.

    Dies ist nur eine bequeme Möglichkeit, die Verbindungsresilienz zu testen, indem Sie einige Eingaben in die Benutzeroberfläche der Anwendung ändern. Sie können auch Code schreiben, der vorübergehende Fehler für alle Abfragen oder Updates generiert, wie weiter unten in den Kommentaren zur DbInterception.Add-Methode erläutert.

  3. Fügen Sie in Global.asax die folgenden using Anweisungen hinzu:

    using ContosoUniversity.DAL;
    using System.Data.Entity.Infrastructure.Interception;
    
  4. Fügen Sie der -Methode die Application_Start hervorgehobenen Zeilen hinzu:

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        DbInterception.Add(new SchoolInterceptorTransientErrors());
        DbInterception.Add(new SchoolInterceptorLogging());
    }
    

    Diese Codezeilen bewirken, dass Ihr Interceptorcode ausgeführt wird, wenn Entity Framework Abfragen an die Datenbank sendet. Beachten Sie, dass Sie diese unabhängig aktivieren und deaktivieren können, da Sie separate Interceptorklassen für die Simulation und Protokollierung vorübergehender Fehler erstellt haben.

    Sie können Interceptors mit der DbInterception.Add -Methode an einer beliebigen Stelle im Code hinzufügen. Sie muss sich nicht in der Application_Start -Methode befinden. Eine weitere Möglichkeit besteht darin, diesen Code in der DbConfiguration-Klasse zu platzieren, die Sie zuvor erstellt haben, um die Ausführungsrichtlinie zu konfigurieren.

    public class SchoolConfiguration : DbConfiguration
    {
        public SchoolConfiguration()
        {
            SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
            DbInterception.Add(new SchoolInterceptorTransientErrors());
            DbInterception.Add(new SchoolInterceptorLogging());
        }
    }
    

    Achten Sie darauf, unabhängig davon, wo Sie diesen Code platzieren, nicht mehr als einmal für denselben Interceptor auszuführen DbInterception.Add , sonst erhalten Sie zusätzliche Interceptorinstanzen. Wenn Sie beispielsweise den Protokollierungs-Interceptor zweimal hinzufügen, werden für jede SQL-Abfrage zwei Protokolle angezeigt.

    Interceptors werden in der Reihenfolge der Registrierung ausgeführt (der Reihenfolge, in der die DbInterception.Add Methode aufgerufen wird). Die Reihenfolge kann abhängig davon, was Sie im Interceptor tun, von Bedeutung sein. Beispielsweise kann ein Interceptor den SQL-Befehl ändern, den er in der CommandText Eigenschaft abruft. Wenn der SQL-Befehl geändert wird, erhält der nächste Interceptor den geänderten SQL-Befehl, nicht den ursprünglichen SQL-Befehl.

    Sie haben den Simulationscode für vorübergehende Fehler so geschrieben, dass Sie vorübergehende Fehler verursachen können, indem Sie einen anderen Wert in die Benutzeroberfläche eingeben. Alternativ können Sie den Interceptorcode schreiben, um immer die Sequenz vorübergehender Ausnahmen zu generieren, ohne einen bestimmten Parameterwert zu überprüfen. Sie können den Interceptor dann nur hinzufügen, wenn Sie vorübergehende Fehler generieren möchten. Wenn Sie dies tun, fügen Sie den Interceptor jedoch erst nach Abschluss der Datenbankinitialisierung hinzu. Anders ausgedrückt: Führen Sie mindestens einen Datenbankvorgang aus, z. B. eine Abfrage für eine Ihrer Entitätssätze, bevor Sie mit dem Generieren vorübergehender Fehler beginnen. Das Entity Framework führt während der Datenbankinitialisierung mehrere Abfragen aus, die nicht in einer Transaktion ausgeführt werden, sodass Fehler während der Initialisierung dazu führen können, dass der Kontext in einen inkonsistenten Zustand versetzt wird.

Testen der neuen Konfiguration

  1. Drücken Sie F5 , um die Anwendung im Debugmodus auszuführen, und klicken Sie dann auf die Registerkarte Schüler .

  2. Sehen Sie sich das Visual Studio-Ausgabefenster an, um die Ablaufverfolgungsausgabe anzuzeigen. Möglicherweise müssen Sie über einige JavaScript-Fehler nach oben scrollen, um zu den protokollgeschriebenen Protokollen zu gelangen.

    Beachten Sie, dass die tatsächlichen SQL-Abfragen angezeigt werden, die an die Datenbank gesendet werden. Sie sehen einige anfängliche Abfragen und Befehle, die Entity Framework für die ersten Schritte ausführt, indem Sie die Datenbankversion und die Migrationsverlaufstabelle überprüfen (im nächsten Tutorial erfahren Sie mehr über Migrationen). Außerdem wird eine Abfrage für das Paging angezeigt, um herauszufinden, wie viele Schüler es gibt, und schließlich sehen Sie die Abfrage, die die Schülerdaten abruft.

    Protokollierung für normale Abfragen

  3. Geben Sie auf der Seite Kursteilnehmer "Throw" als Suchzeichenfolge ein, und klicken Sie auf Suchen.

    Auslösen einer Suchzeichenfolge

    Sie werden feststellen, dass der Browser für einige Sekunden hängen bleibt, während Entity Framework die Abfrage mehrmals wiederholt. Der erste Wiederholungsversuch erfolgt sehr schnell, dann erhöht sich die Wartezeit vor jeder weiteren Wiederholung. Dieser Prozess des Längeren Wartens, bis jede Wiederholung als exponentielles Backoff bezeichnet wird.

    Wenn die Seite mit Kursteilnehmern angezeigt wird, die "ein" in ihren Namen haben, sehen Sie sich das Ausgabefenster an, und Sie sehen, dass dieselbe Abfrage fünfMal versucht wurde, wobei die ersten vier Mal vorübergehende Ausnahmen zurückgeben. Für jeden vorübergehenden Fehler wird das Protokoll angezeigt, das Sie schreiben, wenn Sie den vorübergehenden Fehler in der SchoolInterceptorTransientErrors Klasse generieren ("Returning transient error for command...") und das Protokoll wird geschrieben, wenn SchoolInterceptorLogging die Ausnahme abgerufen wird.

    Protokollierungsausgabe mit Wiederholungsversuchen

    Da Sie eine Suchzeichenfolge eingegeben haben, wird die Abfrage, die Schülerdaten zurückgibt, parametrisiert:

    SELECT TOP (3) 
        [Project1].[ID] AS [ID], 
        [Project1].[LastName] AS [LastName], 
        [Project1].[FirstMidName] AS [FirstMidName], 
        [Project1].[EnrollmentDate] AS [EnrollmentDate]
        FROM ( SELECT [Project1].[ID] AS [ID], [Project1].[LastName] AS [LastName], [Project1].[FirstMidName] AS [FirstMidName], [Project1].[EnrollmentDate] AS [EnrollmentDate], row_number() OVER (ORDER BY [Project1].[LastName] ASC) AS [row_number]
            FROM ( SELECT 
                [Extent1].[ID] AS [ID], 
                [Extent1].[LastName] AS [LastName], 
                [Extent1].[FirstMidName] AS [FirstMidName], 
                [Extent1].[EnrollmentDate] AS [EnrollmentDate]
                FROM [dbo].[Student] AS [Extent1]
                WHERE ([Extent1].[LastName] LIKE @p__linq__0 ESCAPE N'~') OR ([Extent1].[FirstMidName] LIKE @p__linq__1 ESCAPE N'~')
            )  AS [Project1]
        )  AS [Project1]
        WHERE [Project1].[row_number] > 0
        ORDER BY [Project1].[LastName] ASC:
    

    Sie protokollieren nicht den Wert der Parameter, aber sie können dies tun. Wenn Sie die Parameterwerte anzeigen möchten, können Sie Protokollierungscode schreiben, um Parameterwerte aus der Parameters Eigenschaft des Objekts abzurufen, das DbCommand Sie in den Interceptormethoden erhalten.

    Beachten Sie, dass Sie diesen Test nur wiederholen können, wenn Sie die Anwendung beenden und neu starten. Wenn Sie die Verbindungsresilienz mehrmals in einer einzigen Ausführung der Anwendung testen möchten, können Sie Code schreiben, um den Fehlerindikator in SchoolInterceptorTransientErrorszurückzusetzen.

  4. Um den Unterschied zu sehen, den die Ausführungsstrategie (Wiederholungsrichtlinie) macht, kommentieren Sie die SetExecutionStrategy Zeile in SchoolConfiguration.cs aus, führen Sie die Seite Schüler im Debugmodus erneut aus, und suchen Sie erneut nach "Throw".

    Diesmal beendet der Debugger die erste generierte Ausnahme sofort, wenn er versucht, die Abfrage beim ersten Mal auszuführen.

    Dummy-Ausnahme

  5. Heben Sie die Auskommentierung der SetExecutionStrategy-Zeile in SchoolConfiguration.cs auf.

Abrufen des Codes

Abgeschlossenes Projekt herunterladen

Zusätzliche Ressourcen

Links zu anderen Entity Framework-Ressourcen finden Sie unter ASP.NET Datenzugriff – Empfohlene Ressourcen.

Nächste Schritte

In diesem Tutorial:

  • Aktivierte Verbindungsresilienz
  • Aktivierte Befehlsüberwachung
  • Test der neuen Konfiguration

Fahren Sie mit dem nächsten Artikel fort, um mehr über Code First-Migrationen und die Azure-Bereitstellung zu erfahren.