Freigeben über


Verbindungsresilienz von ASP.NET Web Forms und Abfangen von Befehlen

von Erik Reitan

In diesem Lernprogramm ändern Sie die Wingtip Toys-Beispielanwendung, um verbindungsresilienz und Befehlsinterception zu unterstützen. Durch aktivieren der Verbindungsresilienz wiederholt die Wingtip Toys-Beispielanwendung automatisch Datenaufrufe, wenn vorübergehende Fehler auftreten, die typisch für eine Cloudumgebung sind. Außerdem erfasst die Wingtip Toys-Beispielanwendung durch die Implementierung der Befehlsinterception alle SQL-Abfragen, die an die Datenbank gesendet werden, um sie zu protokollieren oder zu ändern.

Hinweis

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

Sie lernen Folgendes:

  • Bereitstellen der Verbindungsresilienz
  • Implementieren der Befehlsinterception.

Voraussetzungen

Bevor Sie beginnen, stellen Sie sicher, dass die folgende Software auf Ihrem Computer installiert ist:

Verbindungsstabilität

Wenn Sie eine Anwendung in Windows Azure bereitstellen, empfiehlt es sich, die Datenbank in Windows Azure SQL-Datenbank, einem Clouddatenbankdienst, bereitzustellen. Vorübergehende Verbindungsfehler sind in der Regel häufiger, 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. Lastenausgleichsgeräte.

Auch ein Clouddienst wird in der Regel von anderen Benutzern geteilt, was bedeutet, dass seine Reaktionsfähigkeit von ihnen beeinflusst werden kann. Und Ihr Zugriff auf die Datenbank unterliegt möglicherweise der Einschränkung. Drosselung bedeutet, dass der Datenbankdienst Ausnahmen auslöst, wenn Sie versuchen, häufiger darauf zuzugreifen, als in Ihrem Service Level Agreement (SLA) zulässig ist.

Viele oder die meisten Verbindungsprobleme, die auftreten, wenn Sie auf einen Clouddienst zugreifen, sind vorübergehend, d. h. sie lösen sich in kurzer Zeit selbst. Wenn Sie also einen Datenbankvorgang ausprobieren und einen Fehlertyp 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 wesentlich bessere Benutzererfahrung bieten, wenn Sie vorübergehende Fehler behandeln, indem Sie es automatisch erneut versuchen, die meisten davon für den Kunden unsichtbar zu machen. Das Feature zur Ausfallsicherheit von Verbindungen in Entity Framework 6 automatisiert diesen Prozess des Wiederholens fehlgeschlagener SQL-Abfragen.

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

  1. Es muss wissen, welche Ausnahmen wahrscheinlich vorübergehend sind. Sie möchten Fehler wiederholen, die durch einen temporären Verlust in der Netzwerkkonnektivität verursacht werden, nicht durch Programmfehler verursacht, z. B. fehler.
  2. Es muss eine angemessene Zeitspanne zwischen Wiederholungen eines fehlgeschlagenen Vorgangs warten. Sie können länger zwischen Wiederholungen für einen Batchprozess warten, als für eine Onlinewebseite, auf der ein Benutzer auf eine Antwort wartet.
  3. Es muss eine entsprechende Anzahl von Wiederholungen wiederholen, bevor sie aufgibt. Möglicherweise möchten Sie in einem Batchprozess, den Sie in einer Onlineanwendung verwenden würden, weitere Wiederholungen wiederholen.

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

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

Implementieren der Verbindungsresilienz

  1. Laden Sie die WingtipToys-Beispielwebformular-Anwendung in Visual Studio herunter, und öffnen Sie sie.

  2. Fügen Sie im Ordner "Logik " der WingtipToys-Anwendung eine Klassendatei namens 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 DbConfiguration. Sie können die DbConfiguration Klasse verwenden, um Konfigurationsaufgaben im Code auszuführen, den Sie andernfalls in der Datei "Web.config " ausführen würden. Weitere Informationen finden Sie unter EntityFramework Code-based Configuration.

  1. Öffnen Sie im Ordner "Logik " die AddProducts.cs Datei.

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

    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, sodass die RetryLimitExceededException Protokollierte in Gelb hervorgehoben 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 Vorgang erneut ausprobieren kann. Durch den Abfangen der RetryLimitExceededException Ausnahme wurden die einzigen Fehler, die wahrscheinlich vorübergehend sein, bereits mehrmals ausprobiert und fehlgeschlagen. Die tatsächliche 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 Entity Framework Connection Resiliency /Retry Logic.

Abfangen von Befehlen

Nachdem Sie eine Wiederholungsrichtlinie aktiviert haben, wie überprüfen Sie, ob 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. Zum Testen des Verbindungsresilienzfeatures benötigen Sie eine Möglichkeit, Abfragen abzufangen, die Entity Framework an SQL Server sendet, und die SQL Server-Antwort durch einen Ausnahmetyp ersetzen, der in der Regel vorübergehend ist.

Sie können die Abfrageinterception auch verwenden, um eine bewährte Methode für Cloudanwendungen zu implementieren: Protokollieren der Latenz und des Erfolgs oder Fehlers aller Aufrufe an externe Dienste wie Datenbankdienste.

In diesem Abschnitt des Lernprogramms verwenden Sie das Abfangen-Feature 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 ist die Verwendung von interface aufrufenden Aufrufen System.Diagnostics.Trace oder einer Protokollierungsklasse. Dadurch wird es einfacher, Ihren 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.

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 "Protokollierung " 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 Wichtigkeit von Protokollen anzugeben, und eine, die Latenzinformationen für externe Dienstaufrufe wie Datenbankabfragen bereitstellt. 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 darauf zu vertrauen, dass dies in jedem Protokollierungsmethodeaufruf in der gesamten Anwendung erfolgt.

    Mit TraceApi den Methoden können Sie die Latenz jedes Aufrufs an einen externen Dienst wie SQL-Datenbank nachverfolgen.

  3. Erstellen Sie im Ordner "Protokollierung " 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 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 Blob-Speicher in Windows Azure zu schreiben. Unter "Problembehandlung für Windows Azure-Websites in Visual Studio" finden Sie einige der Optionen und Links zu anderen Ressourcen. In diesem Lernprogramm sehen Sie sich nur Protokolle im Visual Studio-Ausgabefenster an.

In einer Produktionsanwendung sollten Sie die Verwendung von Ablaufverfolgungsframeworks System.Diagnosticsin 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 eine Abfrage an die Datenbank gesendet wird, eine, um vorübergehende Fehler zu simulieren und eine Protokollierung auszuführen. Diese Interceptorklassen müssen von der DbCommandInterceptor Klasse abgeleitet werden. In ihnen schreiben Sie Methodenüberschreibungen, die automatisch aufgerufen werden, wenn die Abfrage ausgeführt werden soll. In 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 etwas 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 eine Klassendatei namens InterceptorLogging.cs im Ordner "Logic ", 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);
        }
      }
    }
    

    Bei erfolgreichen Abfragen oder Befehlen schreibt dieser Code ein Informationsprotokoll mit Latenzinformationen. Für Ausnahmen wird ein Fehlerprotokoll erstellt.

  2. Um die Interceptor-Klasse zu erstellen, die dumme vorübergehende Fehler generiert, wenn Sie "Throw" in das Textfeld "Name " auf der Seite mit dem Namen AdminPage.aspx eingeben, erstellen Sie eine Klassendatei mit dem Namen InterceptorTransientErrors.cs im Ordner "Logic ", 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 setzt nur die Methode außer Kraft, die ReaderExecuting 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 ScalarExecuting außer Kraft setzen, wie der Protokollierungs-Interceptor vornimmt.

    Später melden Sie sich als "Administrator" an, und wählen Sie den Administratorlink auf der oberen Navigationsleiste aus. Anschließend fügen Sie auf der seite AdminPage.aspx ein Produkt namens "Throw" hinzu. Der Code erstellt eine Dummy SQL-Datenbank Ausnahme für Fehlernummer 20, einen Typ, der normalerweise 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 in neuen Versionen von SQL-Datenbank geändert werden. Das Produkt wird in "TransientErrorExample" umbenannt, dem Sie im Code der InterceptorTransientErrors.cs Datei folgen können.

    Der Code gibt die Ausnahme von Entity Framework zurück, anstatt die Abfrage auszuführen und Ergebnisse zurückgibt. Die vorübergehende Ausnahme wird viermal zurückgegeben, und anschließend wird der Code auf die normale Prozedur zurückgesetzt, in der die Abfrage an die Datenbank übergeben wird.

    Da alles protokolliert wird, können Sie sehen, dass Entity Framework viermal versucht, die Abfrage auszuführen, bevor sie erfolgreich ist, und der einzige Unterschied in der Anwendung besteht darin, dass es länger dauert, eine Seite mit Abfrageergebnissen zu rendern.

    Die Anzahl der Wiederholungen des Entity Frameworks 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 auch den Code hier, 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 hervorgehobenen Zeilen hinzu Application_Start :

    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 führen dazu, dass der Interceptorcode ausgeführt wird, wenn Entity Framework Abfragen an die Datenbank sendet. Beachten Sie, dass Sie, da Sie separate Interceptorklassen für vorübergehende Fehlersimulation und Protokollierung erstellt haben, unabhängig voneinander aktivieren und deaktivieren können.

Sie können Interceptors mithilfe 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 einzufügen.

Wenn Sie diesen Code platzieren, achten Sie darauf, 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 zwei Protokolle für jede SQL-Abfrage angezeigt.

Interceptors werden in der Reihenfolge der Registrierung ausgeführt (die Reihenfolge, in der die DbInterception.Add Methode aufgerufen wird). Die Reihenfolge hängt möglicherweise davon ab, was Sie im Interceptor tun. 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 Kurzfehlersimulationscode 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 nach einem bestimmten Parameterwert zu suchen. Anschließend können Sie den Interceptor nur dann hinzufügen, wenn Sie vorübergehende Fehler generieren möchten. Wenn Sie dies tun, fügen Sie den Interceptor jedoch erst hinzu, nachdem die Datenbankinitialisierung abgeschlossen wurde. Mit anderen Worten: Führen Sie mindestens einen Datenbankvorgang aus, z. B. eine Abfrage für einen 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, und sie werden nicht in einer Transaktion ausgeführt, 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 "Administrator" mit "Pa$$word" als Kennwort an.

  2. Wählen Sie "Administrator " in der Navigationsleiste oben aus.

  3. Geben Sie ein neues Produkt namens "Throw" mit entsprechender Beschreibung, Preis- und Bilddatei ein.

  4. Drücken Sie die Schaltfläche "Produkt hinzufügen".
    Sie werden feststellen, dass der Browser mehrere Sekunden lang hängen scheint, während Entity Framework mehrmals die Abfrage wiederholt. Der erste Wiederholungstest erfolgt sehr schnell, dann steigt die Wartezeit, bevor jeder zusätzliche Wiederholungsversuche erneut gestartet wird. Dieser Vorgang, der länger wartet, bevor jeder Wiederholungsvorgang als exponentieller Backoff bezeichnet wird.

  5. Warten Sie, bis die Seite nicht mehr geladen werden soll.

  6. Beenden Sie das Projekt, und sehen Sie sich das Visual Studio-Ausgabefenster an, um die Ablaufverfolgungsausgabe anzuzeigen. Sie finden das Ausgabefenster, indem Sie "Debug ->Windows ->Output" auswählen. Möglicherweise müssen Sie nach mehreren anderen Protokollen scrollen, die von Ihrem Logger geschrieben wurden.

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

  7. Um den Unterschied der Ausführungsstrategie (Wiederholungsrichtlinie) anzuzeigen, kommentieren Sie die SetExecutionStrategy Zeile in WingtipToysConfiguration.cs Datei im Ordner "Logik ", führen Sie die Administratorseite erneut im Debugmodus aus, und fügen Sie das Produkt mit dem Namen "Throw" erneut hinzu.

    Dieses Mal beendet der Debugger die erste generierte Ausnahme sofort, wenn sie versucht, die Abfrage zum ersten Mal auszuführen.
    Debuggen – Detail anzeigen

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

Zusammenfassung

In diesem Lernprogramm haben Sie erfahren, wie Sie eine Web Forms-Beispielanwendung ändern, um verbindungsresilienz und Befehlsinterception zu unterstützen.

Nächste Schritte

Nachdem Sie die Verbindungsresilienz und die Befehlsinterception in ASP.NET Web Forms überprüft haben, lesen Sie das Thema ASP.NET Web Forms asynchrone Methoden in ASP.NET 4.5. Das Thema vermittelt Ihnen die Grundlagen des Erstellens einer asynchronen ASP.NET Webanwendung mit Visual Studio.