Partager via


Résilience des connexions et interception des commandes Web Forms ASP.NET

par Erik Reitan

Dans ce tutoriel, vous allez modifier l’exemple d’application Wingtip Toys pour prendre en charge la résilience des connexions et l’interception des commandes. En activant la résilience des connexions, l’exemple d’application Wingtip Toys retentera automatiquement les appels de données lorsque des erreurs temporaires typiques d’un environnement cloud se produisent. En outre, en implémentant l’interception des commandes, l’exemple d’application Wingtip Toys intercepte toutes les requêtes SQL envoyées à la base de données afin de les consigner ou de les modifier.

Remarque

Ce didacticiel Web Forms était basé sur le didacticiel MVC suivant de Tom Dykstra :
Résilience des connexions et interception de commandes avec Entity Framework dans une application MVC ASP.NET

Ce que vous allez apprendre :

  • Comment fournir une résilience de connexion.
  • Comment implémenter l’interception des commandes.

Prérequis

Avant de commencer, vérifiez que le logiciel suivant est installé sur votre ordinateur :

Résilience de connexion

Lorsque vous envisagez de déployer une application sur Windows Azure, une option à prendre en compte consiste à déployer la base de données sur Windows Azure SQL Database, un service de base de données cloud. Les erreurs de connexion temporaires sont généralement plus fréquentes lorsque vous vous connectez à un service de base de données cloud que lorsque votre serveur web et votre serveur de base de données sont directement connectés dans le même centre de données. Même si un serveur web cloud et un service de base de données cloud sont hébergés dans le même centre de données, il existe davantage de connexions réseau entre elles qui peuvent rencontrer des problèmes, tels que des équilibreurs de charge.

En outre, un service cloud est généralement partagé par d’autres utilisateurs, ce qui signifie que sa réactivité peut être affectée par eux. Et votre accès à la base de données peut être soumis à une limitation. La limitation signifie que le service de base de données lève des exceptions lorsque vous essayez de l’accéder plus fréquemment que ce qui est autorisé dans votre contrat de niveau de service (SLA).

De nombreux problèmes de connexion ou la plupart des problèmes de connexion qui se produisent lorsque vous accédez à un service cloud sont temporaires, c’est-à-dire qu’ils se résolvent dans une courte période de temps. Par conséquent, lorsque vous essayez une opération de base de données et obtenez un type d’erreur généralement temporaire, vous pouvez réessayer après une courte attente et l’opération peut réussir. Vous pouvez offrir une meilleure expérience à vos utilisateurs si vous gérez des erreurs temporaires en réessayant automatiquement, ce qui les rend la plupart invisibles pour le client. La fonctionnalité de résilience de connexion dans Entity Framework 6 automatise ce processus de nouvelle tentative de requêtes SQL ayant échoué.

La fonctionnalité de résilience de connexion doit être configurée de manière appropriée pour un service de base de données particulier :

  1. Il doit savoir quelles exceptions sont susceptibles d’être temporaires. Vous souhaitez réessayer des erreurs provoquées par une perte temporaire de connectivité réseau, et non par des erreurs causées par des bogues de programme, par exemple.
  2. Il doit attendre un délai approprié entre les nouvelles tentatives d’une opération ayant échoué. Vous pouvez attendre plus longtemps entre les nouvelles tentatives d’un processus de traitement par lots que pour une page web en ligne où un utilisateur attend une réponse.
  3. Il doit réessayer un nombre approprié de fois avant de renoncer. Vous souhaiterez peut-être réessayer plus de fois dans un processus de traitement par lots que vous feriez dans une application en ligne.

Vous pouvez configurer ces paramètres manuellement pour n’importe quel environnement de base de données pris en charge par un fournisseur Entity Framework.

Tout ce que vous devez faire pour activer la résilience des connexions est de créer une classe dans votre assembly qui dérive de la DbConfiguration classe, et dans cette classe, définissez la stratégie d’exécution sql Database, qui dans Entity Framework est un autre terme pour la stratégie de nouvelle tentative.

Implémentation de la résilience de connexion

  1. Téléchargez et ouvrez l’exemple d’application Web Forms WingtipToys dans Visual Studio.

  2. Dans le dossier Logique de l’application WingtipToys , ajoutez un fichier de classe nommé WingtipToysConfiguration.cs.

  3. Remplacez le code existant par le code suivant :

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

Entity Framework exécute automatiquement le code qu’il trouve dans une classe qui dérive de DbConfiguration. Vous pouvez utiliser la DbConfiguration classe pour effectuer des tâches de configuration dans le code que vous feriez autrement dans le fichier Web.config . Pour plus d’informations, consultez Configuration basée sur le code EntityFramework.

  1. Dans le dossier Logique , ouvrez le fichier AddProducts.cs .

  2. Ajoutez une using instruction comme System.Data.Entity.Infrastructure indiqué en jaune :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using System.Data.Entity.Infrastructure;
    
  3. Ajoutez un catch bloc à la AddProduct méthode afin que le RetryLimitExceededException journal soit enregistré comme mis en surbrillance en jaune :

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

En ajoutant l’exception RetryLimitExceededException , vous pouvez fournir une meilleure journalisation ou afficher un message d’erreur à l’utilisateur où il peut choisir de réessayer le processus. En interceptant l’exception RetryLimitExceededException , les seules erreurs susceptibles d’être temporaires ont déjà été tentées et ont échoué plusieurs fois. L’exception réelle retournée est encapsulée dans l’exception RetryLimitExceededException . En outre, vous avez également ajouté un bloc catch général. Pour plus d’informations sur l’exception RetryLimitExceededException , consultez Entity Framework Connection Resiliency /Retry Logic.

Interception des commandes

Maintenant que vous avez activé une stratégie de nouvelle tentative, comment tester pour vérifier qu’elle fonctionne comme prévu ? Il n’est pas si facile de forcer une erreur temporaire à se produire, en particulier lorsque vous exécutez localement, et il serait particulièrement difficile d’intégrer des erreurs temporaires réelles dans un test unitaire automatisé. Pour tester la fonctionnalité de résilience de connexion, vous avez besoin d’un moyen d’intercepter les requêtes envoyées par Entity Framework à SQL Server et de remplacer la réponse SQL Server par un type d’exception généralement temporaire.

Vous pouvez également utiliser l’interception des requêtes pour implémenter une bonne pratique pour les applications cloud : journaliser la latence et la réussite ou l’échec de tous les appels à des services externes tels que les services de base de données.

Dans cette section du didacticiel, vous allez utiliser la fonctionnalité d’interception d’Entity Framework à la fois pour la journalisation et la simulation d’erreurs temporaires.

Créer une interface et une classe de journalisation

Une bonne pratique pour la journalisation consiste à le faire à l’aide d’un interface appel plutôt que d’appels codés en dur vers ou à System.Diagnostics.Trace une classe de journalisation. Cela facilite la modification de votre mécanisme de journalisation ultérieurement si vous avez besoin de le faire. Dans cette section, vous allez créer l’interface de journalisation et une classe pour l’implémenter.

En fonction de la procédure ci-dessus, vous avez téléchargé et ouvert l’exemple d’application WingtipToys dans Visual Studio.

  1. Créez un dossier dans le projet WingtipToys et nommez-le Journalisation.

  2. Dans le dossier Journalisation , créez un fichier de classe nommé ILogger.cs et remplacez le code par défaut par le code suivant :

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

    L’interface fournit trois niveaux de suivi pour indiquer l’importance relative des journaux d’activité et l’une conçue pour fournir des informations de latence pour les appels de service externes, tels que les requêtes de base de données. Les méthodes de journalisation ont des surcharges qui vous permettent de passer une exception. Ainsi, les informations d’exception, y compris la trace de pile et les exceptions internes, sont enregistrées de manière fiable par la classe qui implémente l’interface, au lieu de s’appuyer sur celle effectuée dans chaque appel de méthode de journalisation dans l’ensemble de l’application.

    Les TraceApi méthodes vous permettent de suivre la latence de chaque appel à un service externe tel que SQL Database.

  3. Dans le dossier Journalisation , créez un fichier de classe nommé Logger.cs et remplacez le code par défaut par le code suivant :

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

L’implémentation utilise System.Diagnostics pour effectuer le suivi. Il s’agit d’une fonctionnalité intégrée de .NET qui facilite la génération et l’utilisation des informations de suivi. Il existe de nombreux « écouteurs » que vous pouvez utiliser avec System.Diagnostics le suivi, pour écrire des journaux dans des fichiers, par exemple, ou pour les écrire dans le stockage d’objets blob dans Windows Azure. Consultez certaines des options et des liens vers d’autres ressources pour plus d’informations, dans la résolution des problèmes liés aux sites web Windows Azure dans Visual Studio. Pour ce tutoriel, vous allez uniquement examiner les journaux d’activité dans la fenêtre Sortie de Visual Studio.

Dans une application de production, vous pouvez envisager d’utiliser des frameworks de suivi autres que System.Diagnostics, et l’interface ILogger permet de basculer relativement facilement vers un autre mécanisme de suivi si vous décidez de le faire.

Créer des classes d’intercepteur

Ensuite, vous allez créer les classes que Entity Framework appelle à chaque fois qu’elle va envoyer une requête à la base de données, une pour simuler des erreurs temporaires et une pour effectuer la journalisation. Ces classes d’intercepteur doivent dériver de la DbCommandInterceptor classe. Dans ceux-ci, vous écrivez des remplacements de méthode qui sont automatiquement appelés lorsque la requête est sur le point d’être exécutée. Dans ces méthodes, vous pouvez examiner ou consigner la requête envoyée à la base de données, et vous pouvez modifier la requête avant qu’elle ne soit envoyée à la base de données ou retourner quelque chose à Entity Framework vous-même sans même passer la requête à la base de données.

  1. Pour créer la classe d’intercepteur qui journalisera chaque requête SQL avant son envoi à la base de données, créez un fichier de classe nommé InterceptorLogging.cs dans le dossier Logique et remplacez le code par défaut par le code suivant :

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

    Pour les requêtes ou commandes réussies, ce code écrit un journal d’informations avec des informations de latence. Pour les exceptions, il crée un journal des erreurs.

  2. Pour créer la classe d’intercepteur qui génère des erreurs temporaires factices lorsque vous entrez « Throw » dans la zone de texte Name de la page nommée AdminPage.aspx, créez un fichier de classe nommé InterceptorTransientErrors.cs dans le dossier Logique et remplacez le code par défaut par le code suivant :

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

    Ce code remplace uniquement la ReaderExecuting méthode, qui est appelée pour les requêtes qui peuvent retourner plusieurs lignes de données. Si vous souhaitez vérifier la résilience de connexion pour d’autres types de requêtes, vous pouvez également remplacer les méthodes et ScalarExecuting les NonQueryExecuting méthodes, comme le fait l’intercepteur de journalisation.

    Plus tard, vous vous connecterez en tant que « Administrateur », puis sélectionnez le lien Administrateur dans la barre de navigation supérieure. Ensuite, dans la page AdminPage.aspx , vous allez ajouter un produit nommé « Throw ». Le code crée une exception de base de données SQL factice pour le numéro d’erreur 20, un type connu pour être généralement temporaire. D’autres numéros d’erreur actuellement reconnus comme temporaires sont 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 et 40613, mais ils sont susceptibles de changer dans les nouvelles versions de SQL Database. Le produit est renommé en « TransientErrorExample », que vous pouvez suivre dans le code du fichier InterceptorTransientErrors.cs .

    Le code retourne l’exception à Entity Framework au lieu d’exécuter la requête et de renvoyer les résultats. L’exception temporaire est retournée quatre fois, puis le code revient à la procédure normale de passage de la requête à la base de données.

    Étant donné que tout est journalisé, vous serez en mesure de voir que Entity Framework tente d’exécuter la requête quatre fois avant de réussir, et la seule différence dans l’application est qu’il faut plus de temps pour afficher une page avec les résultats de la requête.

    Le nombre de nouvelles tentatives d’Entity Framework est configurable ; le code spécifie quatre fois, car il s’agit de la valeur par défaut de la stratégie d’exécution sql Database. Si vous modifiez la stratégie d’exécution, vous devez également modifier le code ici qui spécifie le nombre d’erreurs temporaires générées. Vous pouvez également modifier le code pour générer davantage d’exceptions afin que Entity Framework lève l’exception RetryLimitExceededException .

  3. Dans Global.asax, ajoutez les instructions using suivantes :

    using System.Data.Entity.Infrastructure.Interception;
    
  4. Ensuite, ajoutez les lignes en surbrillance à la Application_Start méthode :

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

Ces lignes de code sont les causes de l’exécution de votre code d’intercepteur lorsque Entity Framework envoie des requêtes à la base de données. Notez que, étant donné que vous avez créé des classes d’intercepteur distinctes pour la simulation d’erreur temporaire et la journalisation, vous pouvez activer et désactiver ces classes indépendamment.

Vous pouvez ajouter des intercepteurs à l’aide de la DbInterception.Add méthode n’importe où dans votre code ; il n’est pas obligé d’être dans la Application_Start méthode. Une autre option, si vous n’avez pas ajouté d’intercepteurs dans la Application_Start méthode, consiste à mettre à jour ou à ajouter la classe nommée WingtipToysConfiguration.cs et à placer le code ci-dessus à la fin du constructeur de la WingtipToysConfiguration classe.

Partout où vous placez ce code, veillez à ne pas exécuter DbInterception.Add pour le même intercepteur plusieurs fois, ou vous obtiendrez des instances d’intercepteur supplémentaires. Par exemple, si vous ajoutez l’intercepteur de journalisation deux fois, vous verrez deux journaux pour chaque requête SQL.

Les intercepteurs sont exécutés dans l’ordre d’inscription (l’ordre dans lequel la DbInterception.Add méthode est appelée). L’ordre peut être important en fonction de ce que vous faites dans l’intercepteur. Par exemple, un intercepteur peut modifier la commande SQL qu’il obtient dans la CommandText propriété. S’il modifie la commande SQL, l’intercepteur suivant obtient la commande SQL modifiée, et non la commande SQL d’origine.

Vous avez écrit le code de simulation d’erreur temporaire d’une manière qui vous permet d’entraîner des erreurs temporaires en entrant une valeur différente dans l’interface utilisateur. En guise d’alternative, vous pouvez écrire le code d’intercepteur pour générer toujours la séquence d’exceptions temporaires sans vérifier une valeur de paramètre particulière. Vous pouvez ensuite ajouter l’intercepteur uniquement lorsque vous souhaitez générer des erreurs temporaires. Toutefois, si vous effectuez cette opération, n’ajoutez pas l’intercepteur tant qu’une fois l’initialisation de la base de données terminée. En d’autres termes, effectuez au moins une opération de base de données telle qu’une requête sur l’un de vos jeux d’entités avant de commencer à générer des erreurs temporaires. Entity Framework exécute plusieurs requêtes lors de l’initialisation de la base de données, et elles ne sont pas exécutées dans une transaction, de sorte que les erreurs lors de l’initialisation peuvent entraîner l’entrée du contexte dans un état incohérent.

Tester la journalisation et la résilience des connexions

  1. Dans Visual Studio, appuyez sur F5 pour exécuter l’application en mode débogage, puis connectez-vous en tant qu'« Administrateur » à l’aide de « Pa$$word » comme mot de passe.

  2. Sélectionnez Admin dans la barre de navigation en haut.

  3. Entrez un nouveau produit nommé « Throw » avec la description, le prix et le fichier image appropriés.

  4. Appuyez sur le bouton Ajouter un produit .
    Vous remarquerez que le navigateur semble se bloquer pendant plusieurs secondes pendant que Entity Framework tente plusieurs fois la requête. La première nouvelle tentative se produit très rapidement, puis l’attente augmente avant chaque nouvelle tentative supplémentaire. Ce processus d’attente plus long avant chaque nouvelle tentative est appelé interruption exponentielle.

  5. Attendez que la page ne tente plus de charger.

  6. Arrêtez le projet et examinez la fenêtre Sortie de Visual Studio pour afficher la sortie de suivi. Vous trouverez la fenêtre Sortie en sélectionnant Debug ->Windows ->Output. Vous devrez peut-être faire défiler plusieurs autres journaux écrits par votre enregistreur d’événements.

    Notez que vous pouvez voir les requêtes SQL réelles envoyées à la base de données. Vous voyez quelques requêtes et commandes initiales que Entity Framework effectue pour commencer, en vérifiant la version de la base de données et la table d’historique de migration.
    Fenêtre Sortie
    Notez que vous ne pouvez pas répéter ce test, sauf si vous arrêtez l’application et redémarrez-la. Si vous souhaitez pouvoir tester la résilience de connexion plusieurs fois dans une seule exécution de l’application, vous pouvez écrire du code pour réinitialiser le compteur d’erreur dans InterceptorTransientErrors .

  7. Pour voir la différence que fait la stratégie d’exécution (stratégie de nouvelle tentative), commentez la SetExecutionStrategy ligne dans WingtipToysConfiguration.cs fichier dans le dossier Logique , réexécutez la page Administrateur en mode débogage et ajoutez à nouveau le produit nommé « Lever ».

    Cette fois que le débogueur s’arrête sur la première exception générée immédiatement lorsqu’il tente d’exécuter la requête la première fois.
    Débogage - Afficher les détails

  8. Supprimez les marques de commentaire de la SetExecutionStrategy ligne dans le fichier WingtipToysConfiguration.cs .

Résumé

Dans ce tutoriel, vous avez vu comment modifier un exemple d’application Web Forms pour prendre en charge la résilience des connexions et l’interception des commandes.

Étapes suivantes

Une fois que vous avez passé en revue la résilience des connexions et l’interception de commandes dans ASP.NET Web Forms, consultez la rubrique ASP.NET Méthodes asynchrones dans ASP.NET 4.5. La rubrique vous apprend les principes de base de la création d’une application Web Forms asynchrone ASP.NET à l’aide de Visual Studio.