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 de connexion et l’interception de commandes. En activant la résilience de connexion, 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 de commandes, l’exemple d’application Wingtip Toys intercepte toutes les requêtes SQL envoyées à la base de données afin de les journaliser ou de les modifier.

Notes

Ce didacticiel Web Forms était basé sur le tutoriel 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 de commandes.

Prérequis

Avant de commencer, assurez-vous que les logiciels suivants sont installés sur votre ordinateur :

Résilience de connexion

Lorsque vous envisagez de déployer une application sur Windows Azure, une option à envisager est de déployer la base de données sur WindowsAzure 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 ensemble 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 eux qui peuvent avoir des problèmes, tels que les é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 ceux-ci. 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 d’y accéder plus fréquemment que ce qui est autorisé dans votre contrat de niveau de service (SLA).

Un grand nombre 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 d’eux-mêmes dans un court laps 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 l’opération après une courte attente, et l’opération peut réussir. Vous pouvez offrir une bien meilleure expérience à vos utilisateurs si vous gérez les erreurs temporaires en réessayant automatiquement, ce qui rend la plupart d’entre elles 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 bogues du programme, par exemple.
  2. Il doit attendre un laps de temps approprié entre les nouvelles tentatives d’une opération ayant échoué. Vous pouvez attendre plus longtemps entre les nouvelles tentatives pour 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 d’abandonner. Vous souhaiterez peut-être réessayer plus de fois dans un processus de traitement par lots que 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 avez à faire pour activer la résilience de connexion est de créer une classe dans votre assembly qui dérive de la DbConfiguration classe et, dans cette classe, de définir la stratégie d’exécution SQL Database, ce qui, dans Entity Framework, est un autre terme pour la stratégie de nouvelle tentative.

Implémentation de la résilience des connexions

  1. Téléchargez et ouvrez l’exemple d’application WingtipToys Web Forms 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 du code que vous feriez autrement dans le fichier Web.config . Pour plus d’informations, consultez Configuration Code-Based EntityFramework.

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

  2. Ajoutez une using instruction pour System.Data.Entity.Infrastructure comme 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 soit journalisé 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 auront déjà été essayées et ont échoué plusieurs fois. L’exception réelle retournée sera 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 Résilience de connexion /Logique de nouvelle tentative d’Entity Framework.

Interception de 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 qu’Entity Framework envoie à 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 afin d’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 tutoriel, 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 de journalisation et une classe

Une meilleure pratique pour la journalisation consiste à le faire à l’aide d’un interface appel à ou d’une System.Diagnostics.Trace classe de journalisation au lieu de coder en dur. Cela facilite la modification de votre mécanisme de journalisation ultérieurement si vous avez besoin de le faire. Ainsi, 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, et un autre 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 consignées de manière fiable par la classe qui implémente l’interface, au lieu de s’appuyer sur cette opération 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 d’informations de suivi. Il existe de nombreux « écouteurs » que vous pouvez utiliser avec System.Diagnostics le traçage, pour écrire des journaux dans des fichiers, par exemple, ou pour les écrire dans le stockage d’objets blob dans Windows Azure. Pour plus d’informations, consultez certaines options et des liens vers d’autres ressources dans Résolution des problèmes liés aux sites web Windows Azure dans Visual Studio. Pour ce tutoriel, vous allez uniquement examiner les journaux dans la fenêtre Sortie de Visual Studio.

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

Créer des classes d’intercepteurs

Ensuite, vous allez créer les classes dans lesquelles Entity Framework appelle chaque fois qu’il va envoyer une requête à la base de données, une pour simuler des erreurs temporaires et une autre pour effectuer la journalisation. Ces classes d’intercepteurs 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 journaliser 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 transmettre la requête à la base de données.

  1. Pour créer la classe intercepteur qui journalise 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 intercepteur qui générera des erreurs temporaires factices lorsque vous entrez « Throw » dans la zone de texte Nom 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 case activée la résilience de connexion pour d’autres types de requêtes, vous pouvez également remplacer les NonQueryExecuting méthodes etScalarExecuting, comme le fait l’intercepteur de journalisation.

    Plus tard, vous vous connecterez en tant que « Administration » et sélectionnez le lien Administration 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 factice SQL Database pour le numéro d’erreur 20, un type connu pour être généralement temporaire. Les 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 sera renommé « 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 transmission de la requête à la base de données.

    Étant donné que tout est journalisé, vous pouvez voir qu’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 modifiez également le code qui spécifie le nombre de fois où des erreurs temporaires sont générées. Vous pouvez également modifier le code pour générer davantage d’exceptions afin qu’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 à l’origine de l’exécution de votre code d’intercepteur quand Entity Framework envoie des requêtes à la base de données. Notez que, comme vous avez créé des classes d’intercepteurs distinctes pour la simulation et la journalisation des erreurs temporaires, vous pouvez les activer et les désactiver 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 nécessaire qu’elle se trouve 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.

Où que vous ayez placé ce code, veillez à ne pas s’exécuter DbInterception.Add pour le même intercepteur plusieurs fois, sinon vous obtiendrez des instances d’intercepteur supplémentaires. Par exemple, si vous ajoutez deux fois l’intercepteur de journalisation, 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). La commande peut être importante en fonction de ce que vous effectuez 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 de provoquer des erreurs temporaires en entrant une valeur différente dans l’interface utilisateur. Vous pouvez également écrire le code de l’intercepteur pour générer toujours la séquence d’exceptions temporaires sans rechercher 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 pendant l’initialisation de la base de données, et elles ne sont pas exécutées dans une transaction, de sorte que des erreurs lors de l’initialisation peuvent entraîner l’état incohérent du contexte.

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 que « Administration » à l’aide de « Pa$$word » comme mot de passe.

  2. Sélectionnez Administration 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 qu’Entity Framework réessaye la requête plusieurs fois. 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é backoff exponentiel .

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

  6. Arrêtez le projet et examinez la fenêtre Sortie de Visual Studio pour voir la sortie de suivi. Vous pouvez trouver la fenêtre Sortie en sélectionnant Déboguer ->Windows ->Sortie. 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 certaines requêtes et commandes initiales effectuées par Entity Framework 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 la redémarrez. Si vous souhaitez pouvoir tester la résilience de connexion plusieurs fois en une seule exécution de l’application, vous pouvez écrire du code pour réinitialiser le compteur d’erreurs dans InterceptorTransientErrors .

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

    Cette fois, le débogueur s’arrête immédiatement sur la première exception générée 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 de connexion et l’interception de commandes.

Étapes suivantes

Après avoir passé en revue la résilience de connexion et l’interception de commandes dans ASP.NET Web Forms, consultez la rubrique ASP.NET Web Forms méthodes asynchrones dans ASP.NET 4.5. La rubrique vous apprendra les principes de base de la création d’une application ASP.NET Web Forms asynchrone à l’aide de Visual Studio.