Share via


Verbindungsresilienz von ASP.NET Web Forms und Abfangen von Befehlen

von Erik Reitan

In diesem Tutorial ändern Sie die Wingtip Toys-Beispielanwendung, um die Verbindungsresilienz und das Abfangen von Befehlen zu unterstützen. Durch Aktivierung der Verbindungsresilienz führt die Wingtip Toys-Beispielanwendung automatisch wiederholungsbasierte Datenaufrufe durch, wenn vorübergehende Fehler auftreten, die typisch für eine Cloudumgebung sind. Durch die Implementierung der Befehlsüberwachung fängt die Wingtip Toys-Beispielanwendung außerdem alle SQL-Abfragen ab, die an die Datenbank gesendet werden, um sie zu protokollieren oder zu ändern.

Hinweis

Dieses Web Forms Tutorial basiert auf dem folgenden MVC-Tutorial von Tom Dykstra:
Verbindungsresilienz und Befehlsüberwachung mit dem Entity Framework in einer ASP.NET MVC-Anwendung

Sie lernen Folgendes:

  • Bereitstellen von Verbindungsresilienz
  • Implementieren der Befehlsüberwachung

Voraussetzungen

Stellen Sie vor dem Start sicher, dass die folgende Software auf Ihrem Computer installiert ist:

Verbindungsstabilität

Wenn Sie die Bereitstellung einer Anwendung in Windows Azure in Betracht ziehen, sollten Sie die Datenbank in WindowsAzure SQL Database, einem Clouddatenbankdienst, bereitstellen. 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.

Außerdem wird ein Clouddienst in der Regel von anderen Benutzern freigegeben, was bedeutet, dass seine Reaktionsfähigkeit von ihnen beeinträchtigt werden kann. Und Ihr Zugriff auf die Datenbank unterliegt möglicherweise einer Einschränkung. Drosselung bedeutet, dass der Datenbankdienst Ausnahmen auslöst, wenn Sie versuchen, häufiger darauf zuzugreifen, als in Ihrer Sla (Service Level Agreement ) zulässig ist.

Viele oder die meisten Verbindungsprobleme, die beim Zugriff auf einen Clouddienst auftreten, sind vorübergehend, d. h. sie lösen sich selbst in kurzer Zeit. Wenn Sie also einen Datenbankvorgang ausprobieren und einen 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 zum Wiederholen fehlerhafter SQL-Abfragen.

Das Feature zur Verbindungsresilienz muss für einen bestimmten Datenbankdienst entsprechend konfiguriert werden:

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

Sie können diese Einstellungen manuell für jede Datenbankumgebung konfigurieren, die von einem Entity Framework-Anbieter unterstützt wird.

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

Implementieren der Verbindungsresilienz

  1. Laden Sie das WingtipToys-Beispiel Web Forms Anwendung in Visual Studio herunter, und öffnen Sie es.

  2. Fügen Sie im Ordner Logic der WingtipToys-Anwendung eine Klassendatei mit dem Namen WingtipToysConfiguration.cs hinzu.

  3. Ersetzen Sie den vorhandenen Code durch folgenden Code:

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

Das Entity Framework führt automatisch den Code aus, der in einer Klasse gefunden wird, 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.

  1. Öffnen Sie im Ordner Logic die Datei AddProducts.cs .

  2. Fügen Sie eine using Anweisung für System.Data.Entity.Infrastructure hinzu, wie in Gelb hervorgehoben:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using System.Data.Entity.Infrastructure;
    
  3. Fügen Sie der AddProduct -Methode einen catch Block hinzu, damit der RetryLimitExceededException als gelb markiert protokolliert wird:

    public bool AddProduct(string ProductName, string ProductDesc, string ProductPrice, string ProductCategory, string ProductImagePath)
    {
        var myProduct = new Product();
        myProduct.ProductName = ProductName;
        myProduct.Description = ProductDesc;
        myProduct.UnitPrice = Convert.ToDouble(ProductPrice);
        myProduct.ImagePath = ProductImagePath;
        myProduct.CategoryID = Convert.ToInt32(ProductCategory);
    
        using (ProductContext _db = new ProductContext())
        {
            // Add product to DB.
            _db.Products.Add(myProduct);
            try
            {
                _db.SaveChanges();
            }
            catch (RetryLimitExceededException ex)
            {
                // Log the RetryLimitExceededException.
                WingtipToys.Logic.ExceptionUtility.LogException(ex, "Error: RetryLimitExceededException -> RemoveProductButton_Click in AdminPage.aspx.cs");
            }
        }
        // Success.
        return true;
    }
    

Durch Hinzufügen der RetryLimitExceededException Ausnahme können Sie eine bessere Protokollierung bereitstellen oder dem Benutzer eine Fehlermeldung anzeigen, in der er den Prozess erneut ausprobieren kann. Durch das Abfangen der RetryLimitExceededException Ausnahme wurden die einzigen Fehler, die wahrscheinlich vorübergehend sind, bereits mehrmals versucht und fehlgeschlagen. Die tatsächlich zurückgegebene Ausnahme wird in die RetryLimitExceededException Ausnahme eingeschlossen. Darüber hinaus haben Sie auch einen allgemeinen Catch-Block hinzugefügt. Weitere Informationen zur RetryLimitExceededException Ausnahme finden Sie unter Resilienz für Entity Framework-Verbindungen/Wiederholungslogik.

Abhören von Befehlen

Wie testen Sie nun, nachdem Sie eine Wiederholungsrichtlinie aktiviert haben, um sicherzustellen, dass sie wie erwartet funktioniert? Es ist nicht so einfach, einen vorübergehenden Fehler zu erzwingen, insbesondere wenn Sie lokal ausgeführt werden, 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 ersetzen Sie die SQL Server Antwort durch einen Ausnahmetyp, der normalerweise vorübergehend ist.

Sie können auch die Abfrageüberwachung 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.

In diesem Abschnitt des Tutorials verwenden Sie die Abhörfunktion von Entity Framework 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 zu tun, indem Sie eine interface Aufrufe oder eine Protokollierungsklasse verwenden, anstatt sie hart zu System.Diagnostics.Trace codieren. Das macht es einfacher, ihren Protokollierungsmechanismus später zu ändern, wenn Sie dies jemals tun müssen. Daher erstellen Sie in diesem Abschnitt die Protokollierungsschnittstelle und eine Klasse, um sie zu implementieren.

Basierend auf dem obigen Verfahren haben Sie die WingtipToys-Beispielanwendung in Visual Studio heruntergeladen und geöffnet.

  1. Erstellen Sie einen Ordner im WingtipToys-Projekt , und nennen Sie ihn Logging.

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

    using System;
     
    namespace WingtipToys.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 Bedeutung von Protokollen anzugeben, und eine, um Latenzinformationen für externe Dienstaufrufe wie Datenbankabfragen bereitzustellen. Die Protokollierungsmethoden verfügen über Überladungen, mit denen Sie eine Ausnahme übergeben können. Dies ist so, dass Ausnahmeinformationen, einschließlich Stapelablaufverfolgung und inneren Ausnahmen, zuverlässig von der Klasse protokolliert werden, die die Schnittstelle implementiert, anstatt sich darauf zu verlassen, dass in jedem Protokollierungsmethodenaufruf in der gesamten Anwendung ausgeführt wird.

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

  3. Erstellen Sie im Ordner Logging eine Klassendatei mit dem Namen Logger.cs , und ersetzen Sie den Standardcode durch den folgenden Code:

    using System;
    using System.Diagnostics;
    using System.Text;
     
    namespace WingtipToys.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)
        {
          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 die Ablaufverfolgung. Dies ist ein integriertes Feature von .NET, das das Generieren und Verwenden von Ablaufverfolgungsinformationen vereinfacht. Es gibt viele "Listener", die Sie mit System.Diagnostics der Ablaufverfolgung verwenden können, z. B. zum Schreiben von Protokollen in Dateien oder zum Schreiben in Blobspeicher in Windows Azure. Weitere Informationen finden Sie unter Problembehandlung für Windows Azure-Websites in Visual Studio unter einigen 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 Ablaufverfolgungsframeworks als System.Diagnosticsverwenden, 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, um vorübergehende Fehler zu simulieren, und eine für die Protokollierung. Diese Interceptorklassen müssen von der DbCommandInterceptor -Klasse abgeleitet werden. In diesen schreiben Sie Methodenüberschreibungen, die automatisch aufgerufen werden, wenn die Abfrage ausgeführt werden soll. Mit diesen Methoden können Sie die Abfrage, die an die Datenbank gesendet wird, untersuchen oder protokollieren, 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 SQL-Abfrage protokolliert, bevor sie an die Datenbank gesendet wird, erstellen Sie im Ordner Logic eine Klassendatei namens InterceptorLogging.cs, und ersetzen Sie den Standardcode 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 WingtipToys.Logging;
    
    namespace WingtipToys.Logic
    {
      public class InterceptorLogging : 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", "Interceptor.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", "Interceptor.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", "Interceptor.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 vorübergehende Dummyfehler generiert, wenn Sie im Textfeld Name auf der Seite adminPage.aspx "Throw" eingeben, erstellen Sie im Ordner Logic eine Klassendatei mit dem Namen InterceptorTransientErrors.cs, und ersetzen Sie den Standardcode 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 WingtipToys.Logging;
     
    namespace WingtipToys.Logic
    {
      public class InterceptorTransientErrors : 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 = "TransientErrorExample";
            command.Parameters[1].Value = "TransientErrorExample";
          }
     
          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.

    Später melden Sie sich als "Admin" an und wählen den link Admin auf der oberen Navigationsleiste aus. Anschließend fügen Sie auf der Seite AdminPage.aspx ein Produkt mit dem Namen "Throw" hinzu. Der Code erstellt eine 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. Das Produkt wird in "TransientErrorExample" umbenannt, was Sie im Code der Datei InterceptorTransientErrors.cs befolgen können.

    Der Code gibt die Ausnahme an Entity Framework zurück, anstatt die Abfrage auszuführen und Ergebnisse zurückzugeben. Die vorübergehende Ausnahme wird viermal zurückgegeben, und dann wird der Code zur normalen 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.

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

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

    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    
        // Initialize the product database.
        Database.SetInitializer(new ProductDatabaseInitializer());
    
        // Create administrator role and user.
        RoleActions roleActions = new RoleActions();
        roleActions.createAdmin();
    
        // Add Routes.
        RegisterRoutes(RouteTable.Routes);
    
        // Logging.
        DbInterception.Add(new InterceptorTransientErrors());
        DbInterception.Add(new InterceptorLogging());
      
    }
    

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 Option, wenn Sie in der Application_Start -Methode keine Interceptors hinzugefügt haben, besteht darin, die Klasse mit dem Namen WingtipToysConfiguration.cs zu aktualisieren oder hinzuzufügen und den obigen Code am Ende des Konstruktors der WingtipToysConfiguration Klasse zu platzieren.

Achten Sie darauf, unabhängig davon, wo Sie diesen Code platzieren, nicht mehr als einmal für denselben Interceptor auszuführen DbInterception.Add , oder Sie erhalten 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 und 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 auf der Benutzeroberfläche eingeben. Alternativ können Sie den Interceptorcode schreiben, um immer die Sequenz vorübergehender Ausnahmen zu generieren, ohne nach einem bestimmten Parameterwert zu suchen. 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 wechselt.

Testen der Protokollierung und Verbindungsresilienz

  1. Drücken Sie in Visual Studio F5, um die Anwendung im Debugmodus auszuführen, und melden Sie sich dann als "Admin" an, indem Sie "Pa$$word" als Kennwort verwenden.

  2. Wählen Sie oben in der Navigationsleiste Admin aus.

  3. Geben Sie ein neues Produkt namens "Throw" mit der entsprechenden Beschreibung, dem preis und der Bilddatei ein.

  4. Klicken Sie auf die Schaltfläche Produkt hinzufügen .
    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, bei dem länger gewartet wird, bevor jeder Wiederholungsversuch ausgeführt wird, wird als exponentielles Backoff bezeichnet.

  5. Warten Sie, bis die Seite nicht mehr versucht zu laden.

  6. Beenden Sie das Projekt, und sehen Sie sich das Visual Studio-Fenster Ausgabe an, um die Ablaufverfolgungsausgabe anzuzeigen. Sie finden das Ausgabefenster , indem Sie Debuggen ->Windows ->Ausgabe auswählen. Möglicherweise müssen Sie an mehreren anderen Protokollen vorbei scrollen, die von Ihrer Protokollierung geschrieben wurden.

    Beachten Sie, dass Sie die tatsächlichen SQL-Abfragen sehen können, die an die Datenbank gesendet werden. Sie sehen einige anfängliche Abfragen und Befehle, die Entity Framework für die ersten Schritte ausführt, und überprüfen die Datenbankversion und die Migrationsverlaufstabelle.
    Ausgabefenster
    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 einzelnen Ausführung der Anwendung testen möchten, können Sie Code schreiben, um den Fehlerindikator in InterceptorTransientErrors zurückzusetzen.

  7. Um den Unterschied zu sehen, den die Ausführungsstrategie (Wiederholungsrichtlinie) macht, kommentieren Sie die Zeile in der SetExecutionStrategy Datei WingtipToysConfiguration.cs im Ordner Logic aus, führen Sie die seite Admin im Debugmodus erneut aus, und fügen Sie das Produkt "Throw" erneut hinzu.

    Diesmal beendet der Debugger die erste generierte Ausnahme sofort, wenn er versucht, die Abfrage zum ersten Mal auszuführen.
    Debuggen – Details anzeigen

  8. Heben Sie die Auskommentierung der SetExecutionStrategy Zeile in der Datei WingtipToysConfiguration.cs auf.

Zusammenfassung

In diesem Tutorial haben Sie erfahren, wie Sie eine Web Forms Beispielanwendung ändern, um Die Verbindungsresilienz und das Abfangen von Befehlen zu unterstützen.

Nächste Schritte

Nachdem Sie die Verbindungsresilienz und das Abfangen von Befehlen in ASP.NET Web Forms überprüft haben, lesen Sie das ASP.NET Web Forms Thema Asynchrone Methoden in ASP.NET 4.5. Das Thema vermittelt Ihnen die Grundlagen der Erstellung einer asynchronen ASP.NET Web Forms-Anwendung mit Visual Studio.