Tutoriel : Utiliser la résilience de connexion et l’interception de commandes avec Entity Framework dans une application MVC ASP.NET

Jusqu’à présent, l’application s’exécute localement dans IIS Express sur votre ordinateur de développement. Pour mettre une application réelle à la disposition d’autres personnes sur Internet, vous devez la déployer sur un fournisseur d’hébergement web et déployer la base de données sur un serveur de base de données.

Dans ce tutoriel, vous allez apprendre à utiliser la résilience de connexion et l’interception de commandes. Il s’agit de deux fonctionnalités importantes d’Entity Framework 6 qui sont particulièrement utiles lorsque vous effectuez un déploiement dans l’environnement cloud : la résilience de connexion (nouvelles tentatives automatiques pour les erreurs temporaires) et l’interception des commandes (interceptez toutes les requêtes SQL envoyées à la base de données pour les enregistrer ou les modifier).

Ce tutoriel sur la résilience de connexion et l’interception des commandes est facultatif. Si vous ignorez ce didacticiel, quelques ajustements mineurs devront être effectués dans les didacticiels suivants.

Dans ce tutoriel, vous allez :

  • Activer la résilience des connexions
  • Activer l’interception de commandes
  • Tester la nouvelle configuration

Prérequis

Activer la résilience des connexions

Lorsque vous déployez l’application sur Windows Azure, vous déployez 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 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).

La plupart ou la plupart des problèmes de connexion lorsque vous accédez à un service cloud sont temporaires, c’est-à-dire qu’ils se résolvent d’eux-mêmes en peu 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 :

  • Elle doit savoir quelles exceptions sont susceptibles d’être temporaires. Vous souhaitez réessayer les erreurs provoquées par une perte temporaire de connectivité réseau, et non par des erreurs provoquées par des bogues du programme, par exemple.
  • 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 par lots que pour une page web en ligne où un utilisateur attend une réponse.
  • 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, mais les valeurs par défaut qui fonctionnent généralement bien pour une application en ligne qui utilise Windows Azure SQL Database ont déjà été configurées pour vous, et ce sont les paramètres que vous allez implémenter pour l’application Contoso University.

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 classe DbConfiguration et, dans cette classe, de définir la stratégie d’exécution SQL Database, qui dans EF est un autre terme de stratégie de nouvelle tentative.

  1. Dans le dossier DAL, ajoutez un fichier de classe nommé SchoolConfiguration.cs.

  2. Remplacez le code du modèle par le code suivant :

    using System.Data.Entity;
    using System.Data.Entity.SqlServer;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolConfiguration : DbConfiguration
        {
            public SchoolConfiguration()
            {
                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, ce que vous feriez autrement dans le fichier Web.config . Pour plus d’informations, consultez EntityFramework Code-Based Configuration.

  3. Dans StudentController.cs, ajoutez une using instruction pour System.Data.Entity.Infrastructure.

    using System.Data.Entity.Infrastructure;
    
  4. Modifiez tous les catch blocs qui interceptent DataException les exceptions afin qu’ils interceptent RetryLimitExceededException les exceptions à la place. Par exemple :

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

    Vous utilisiez DataException pour essayer d’identifier les erreurs qui peuvent être temporaires afin d’envoyer un message convivial « réessayer ». Mais maintenant que vous avez activé une stratégie de nouvelle tentative, les seules erreurs susceptibles d’être temporaires auront déjà été essayées et échouées plusieurs fois et l’exception réelle retournée sera encapsulée dans l’exception RetryLimitExceededException .

Pour plus d’informations, consultez Résilience de connexion/logique de nouvelle tentative d’Entity Framework.

Activer l’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 de 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. EF6 fournit une API de journalisation dédiée qui peut faciliter la journalisation, mais dans cette section du tutoriel, vous allez apprendre à utiliser directement la fonctionnalité d’interception d’Entity Framework, à la fois pour la journalisation et pour la simulation d’erreurs temporaires.

Créer une interface de journalisation et une classe

Une meilleure pratique pour la journalisation consiste à utiliser une interface plutôt qu’à coder en dur des appels à System.Diagnostics.Trace ou à une classe de journalisation. 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./p>

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

  2. Dans le dossier Journalisation , créez un fichier de classe nommé ILogger.cs, puis remplacez le code du modèle par le code suivant :

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

    L’interface fournit trois niveaux de suivi pour indiquer l’importance relative des journaux, et un autre conçu 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 ce qui est effectué dans chaque appel de méthode de journalisation dans toute l’application.

    Les méthodes TraceApi 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 du modèle par le code suivant :

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

    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 le suivi System.Diagnostics, pour écrire des journaux dans des fichiers, par exemple, ou pour les écrire dans le stockage blob dans Azure. Consultez certaines des options et des liens vers d’autres ressources pour plus d’informations dans Résolution des problèmes de sites web 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 de suivre des packages 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 appellera chaque fois qu’il 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’intercepteurs doivent dériver de la DbCommandInterceptor classe . Dans ceux-ci, vous écrivez des substitutions de méthode qui sont automatiquement appelées 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 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 intercepteur qui journalise chaque requête SQL envoyée à la base de données, créez un fichier de classe nommé SchoolInterceptorLogging.cs dans le dossier DAL et remplacez le code de modèle 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 ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorLogging : DbCommandInterceptor
        {
            private ILogger _logger = new Logger();
            private readonly Stopwatch _stopwatch = new Stopwatch();
    
            public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                base.ScalarExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void ScalarExecuted(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ScalarExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ScalarExecuted(command, interceptionContext);
            }
    
            public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                base.NonQueryExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
    
            public override void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.NonQueryExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.NonQueryExecuted(command, interceptionContext);
            }
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                base.ReaderExecuting(command, interceptionContext);
                _stopwatch.Restart();
            }
            public override void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                _stopwatch.Stop();
                if (interceptionContext.Exception != null)
                {
                    _logger.Error(interceptionContext.Exception, "Error executing command: {0}", command.CommandText);
                }
                else
                {
                    _logger.TraceApi("SQL Database", "SchoolInterceptor.ReaderExecuted", _stopwatch.Elapsed, "Command: {0}: ", command.CommandText);
                }
                base.ReaderExecuted(command, interceptionContext);
            }
        }
    }
    

    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 recherche , créez un fichier de classe nommé SchoolInterceptorTransientErrors.cs dans le dossier DAL , puis remplacez le code de modèle 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 ContosoUniversity.Logging;
    
    namespace ContosoUniversity.DAL
    {
        public class SchoolInterceptorTransientErrors : DbCommandInterceptor
        {
            private int _counter = 0;
            private ILogger _logger = new Logger();
    
            public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
            {
                bool throwTransientErrors = false;
                if (command.Parameters.Count > 0 && command.Parameters[0].Value.ToString() == "%Throw%")
                {
                    throwTransientErrors = true;
                    command.Parameters[0].Value = "%an%";
                    command.Parameters[1].Value = "%an%";
                }
    
                if (throwTransientErrors && _counter < 4)
                {
                    _logger.Information("Returning transient error for command: {0}", command.CommandText);
                    _counter++;
                    interceptionContext.Exception = CreateDummySqlException();
                }
            }
    
            private SqlException CreateDummySqlException()
            {
                // The instance of SQL Server you attempted to connect to does not support encryption
                var sqlErrorNumber = 20;
    
                var sqlErrorCtor = typeof(SqlError).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 7).Single();
                var sqlError = sqlErrorCtor.Invoke(new object[] { sqlErrorNumber, (byte)0, (byte)0, "", "", "", 1 });
    
                var errorCollection = Activator.CreateInstance(typeof(SqlErrorCollection), true);
                var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.Instance | BindingFlags.NonPublic);
                addMethod.Invoke(errorCollection, new[] { sqlError });
    
                var sqlExceptionCtor = typeof(SqlException).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic).Where(c => c.GetParameters().Count() == 4).Single();
                var sqlException = (SqlException)sqlExceptionCtor.Invoke(new object[] { "Dummy", errorCollection, null, Guid.NewGuid() });
    
                return sqlException;
            }
        }
    }
    

    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 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.

    Lorsque vous exécutez la page Étudiant et que vous entrez « Throw » comme chaîne de recherche, ce code crée un factice SQL Database exception 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 peuvent être modifiés dans les nouvelles versions de SQL Database.

    Le code retourne l’exception à Entity Framework au lieu d’exécuter la requête et de renvoyer les résultats de la requête. 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 .

    La valeur que vous entrez dans la zone De recherche se trouve dans command.Parameters[0] et command.Parameters[1] (l’une est utilisée pour le prénom et l’autre pour le nom). Lorsque la valeur « %Throw% » est trouvée, « Throw » est remplacé dans ces paramètres par « un » afin que certains étudiants soient trouvés et retournés.

    Il s’agit simplement d’un moyen pratique de tester la résilience de la connexion en fonction de la modification d’une entrée dans l’interface utilisateur de l’application. Vous pouvez également écrire du code qui génère des erreurs temporaires pour toutes les requêtes ou mises à jour, comme expliqué plus loin dans les commentaires sur la méthode DbInterception.Add .

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

    using ContosoUniversity.DAL;
    using System.Data.Entity.Infrastructure.Interception;
    
  4. Ajoutez les lignes en surbrillance à la Application_Start méthode :

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

    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 consiste à placer ce code dans la classe DbConfiguration que vous avez créée précédemment pour configurer la stratégie d’exécution.

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

    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 nouvelle configuration

  1. Appuyez sur F5 pour exécuter l’application en mode débogage, puis cliquez sur l’onglet Étudiants .

  2. Examinez la fenêtre Sortie de Visual Studio pour voir la sortie de suivi. Vous devrez peut-être faire défiler certaines erreurs JavaScript pour accéder aux 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 des migrations (vous en apprendrez plus sur les migrations dans le tutoriel suivant). Et vous voyez une requête pour la pagination, pour savoir combien il y a d’étudiants, et enfin vous voyez la requête qui obtient les données de l’étudiant.

    Journalisation pour une requête normale

  3. Dans la page Étudiants , entrez « Throw » comme chaîne de recherche, puis cliquez sur Rechercher.

    Lever la chaîne de recherche

    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 avant augmente avant chaque nouvelle tentative supplémentaire. Ce processus d’attente plus long avant chaque nouvelle tentative est appelé backoff exponentiel.

    Lorsque la page s’affiche, montrant les étudiants qui ont « un » dans leur nom, examinez la fenêtre de sortie et vous verrez que la même requête a été tentée cinq fois, les quatre premières fois renvoyant des exceptions temporaires. Pour chaque erreur temporaire, vous verrez le journal que vous écrivez lors de la génération de l’erreur temporaire dans la classe (« Erreur temporaire de retour pour la SchoolInterceptorTransientErrors commande... ») et vous verrez le journal écrit lorsque SchoolInterceptorLogging l’exception obtient.

    Sortie de journalisation montrant les nouvelles tentatives

    Étant donné que vous avez entré une chaîne de recherche, la requête qui retourne les données de l’étudiant est paramétrée :

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

    Vous ne journaliserez pas la valeur des paramètres, mais vous pouvez le faire. Si vous souhaitez voir les valeurs des paramètres, vous pouvez écrire du code de journalisation pour obtenir des valeurs de paramètres à partir de la Parameters propriété de l’objet DbCommand que vous obtenez dans les méthodes interceptrices.

    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 SchoolInterceptorTransientErrors.

  4. Pour voir la différence que la stratégie d’exécution (stratégie de nouvelle tentative) fait, commentez la SetExecutionStrategy ligne dans SchoolConfiguration.cs, réexécutez la page Étudiants en mode débogage et recherchez à nouveau « Lancer ».

    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.

    Exception factice

  5. Supprimez les marques de commentaire de la ligne SetExecutionStrategy dans SchoolConfiguration.cs.

Obtenir le code

Télécharger le projet terminé

Ressources supplémentaires

Vous trouverez des liens vers d’autres ressources Entity Framework dans ASP.NET Accès aux données - Ressources recommandées.

Étapes suivantes

Dans ce tutoriel, vous allez :

  • Résilience de connexion activée
  • Interception de commande activée
  • Test de la nouvelle configuration

Passez à l’article suivant pour en savoir plus sur les migrations Code First et le déploiement Azure.