Compartilhar via


Tutorial: Usar resiliência de conexão e interceptação de comando com o Entity Framework em um aplicativo MVC ASP.NET

Até agora, o aplicativo foi executado localmente em IIS Express em seu computador de desenvolvimento. Para disponibilizar um aplicativo real para outras pessoas usarem pela Internet, você precisa implantá-lo em um provedor de hospedagem na Web e implantar o banco de dados em um servidor de banco de dados.

Neste tutorial, você aprenderá a usar a resiliência de conexão e a interceptação de comandos. São dois recursos importantes do Entity Framework 6 que são especialmente valiosos quando você está implantando no ambiente de nuvem: resiliência de conexão (repetições automáticas para erros transitórios) e interceptação de comando (capturar todas as consultas SQL enviadas ao banco de dados para registrá-las ou alterá-las).

Esse tutorial de resiliência de conexão e interceptação de comando é opcional. Se você ignorar este tutorial, alguns pequenos ajustes precisarão ser feitos nos tutoriais subsequentes.

Neste tutorial, você:

  • Habilitar resiliência de conexão
  • Habilitar interceptação de comando
  • Testar a nova configuração

Pré-requisitos

Habilitar resiliência de conexão

Ao implantar o aplicativo no Windows Azure, você implantará o banco de dados no Windows SQL do Azure Database, um serviço de banco de dados de nuvem. Erros de conexão transitória normalmente são mais frequentes quando você se conecta a um serviço de banco de dados de nuvem do que quando o servidor Web e o servidor de banco de dados estão conectados diretamente no mesmo data center. Mesmo que um servidor Web de nuvem e um serviço de banco de dados de nuvem estejam hospedados no mesmo data center, há mais conexões de rede entre eles que podem ter problemas, como balanceadores de carga.

Além disso, um serviço de nuvem normalmente é compartilhado por outros usuários, o que significa que sua capacidade de resposta pode ser afetada por eles. E seu acesso ao banco de dados pode estar sujeito à limitação. Limitação significa que o serviço de banco de dados gera exceções quando você tenta acessá-lo com mais frequência do que o permitido no SLA (Contrato de Nível de Serviço).

Muitos ou mais problemas de conexão quando você está acessando um serviço de nuvem são transitórios, ou seja, eles resolve a si mesmos em um curto período de tempo. Portanto, quando você tenta uma operação de banco de dados e recebe um tipo de erro que normalmente é transitório, você pode tentar a operação novamente após uma breve espera e a operação pode ser bem-sucedida. Você pode fornecer uma experiência muito melhor para seus usuários se você lidar com erros transitórios tentando automaticamente novamente, tornando a maioria deles invisível para o cliente. O recurso de resiliência de conexão no Entity Framework 6 automatiza esse processo de repetição de consultas SQL com falha.

O recurso de resiliência de conexão deve ser configurado adequadamente para um serviço de banco de dados específico:

  • Ele precisa saber quais exceções provavelmente serão transitórias. Você deseja repetir erros causados por uma perda temporária na conectividade de rede, não por erros causados por bugs do programa, por exemplo.
  • Ele precisa aguardar uma quantidade apropriada de tempo entre as repetições de uma operação com falha. Você pode esperar mais tempo entre as tentativas de um processo em lotes do que pode para uma página da Web online em que um usuário está aguardando uma resposta.
  • Ele precisa repetir um número apropriado de vezes antes de desistir. Talvez você queira repetir mais vezes em um processo em lotes que faria em um aplicativo online.

Você pode definir essas configurações manualmente para qualquer ambiente de banco de dados compatível com um provedor do Entity Framework, mas os valores padrão que normalmente funcionam bem para um aplicativo online que usa o Banco de Dados do Windows SQL do Azure já foram configurados para você, e essas são as configurações que você implementará para o aplicativo da Contoso University.

Tudo o que você precisa fazer para habilitar a resiliência de conexão é criar uma classe no assembly derivada da classe DbConfiguration e, nessa classe, definir a estratégia de execução Banco de Dados SQL, que no EF é outro termo para política de repetição.

  1. Na pasta DAL, adicione um arquivo de classe chamado SchoolConfiguration.cs.

  2. Substitua o código do modelo pelo seguinte código:

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

    O Entity Framework executa automaticamente o código encontrado em uma classe derivada de DbConfiguration. Você pode usar a DbConfiguration classe para realizar tarefas de configuração no código que, de outra forma, faria no arquivo Web.config . Para obter mais informações, consulte EntityFramework Code-Based Configuration.

  3. Em StudentController.cs, adicione uma using instrução para System.Data.Entity.Infrastructure.

    using System.Data.Entity.Infrastructure;
    
  4. Altere todos os catch blocos que capturam DataException exceções para que eles capturem RetryLimitExceededException exceções. Por exemplo:

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

    Você estava usando DataException para tentar identificar erros que podem ser transitórios para dar uma mensagem amigável de "tente novamente". Mas agora que você ativou uma política de repetição, os únicos erros provavelmente transitórios já foram tentados e falharam várias vezes e a exceção real retornada será encapsulada na RetryLimitExceededException exceção.

Para obter mais informações, consulte Entity Framework Connection Resiliency/Retry Logic.

Habilitar interceptação de comando

Agora que você ativou uma política de repetição, como testar para verificar se ela está funcionando conforme o esperado? Não é tão fácil forçar um erro transitório a acontecer, especialmente quando você está executando localmente, e seria especialmente difícil integrar erros transitórios reais em um teste de unidade automatizado. Para testar o recurso de resiliência de conexão, você precisa de uma maneira de interceptar consultas que o Entity Framework envia para SQL Server e substituir a resposta SQL Server por um tipo de exceção que normalmente é transitório.

Você também pode usar a interceptação de consulta para implementar uma prática recomendada para aplicativos de nuvem: registrar a latência e o sucesso ou a falha de todas as chamadas para serviços externos , como serviços de banco de dados. O EF6 fornece uma API de log dedicada que pode facilitar o registro em log, mas nesta seção do tutorial, você aprenderá a usar o recurso de interceptação do Entity Framework diretamente, tanto para registro em log quanto para simular erros transitórios.

Criar uma interface e uma classe de registro em log

Uma prática recomendada para registro em log é fazer isso usando uma interface em vez de fazer chamadas hard-coding para System.Diagnostics.Trace ou uma classe de registro em log. Isso facilita a alteração do mecanismo de registro em log posteriormente se você precisar fazer isso. Portanto, nesta seção, você criará a interface de registro em log e uma classe para implementá-la./p>

  1. Crie uma pasta no projeto e nomeie-a como Log.

  2. Na pasta Log , crie um arquivo de classe chamado ILogger.cs e substitua o código do modelo pelo seguinte código:

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

    A interface fornece três níveis de rastreamento para indicar a importância relativa dos logs e um projetado para fornecer informações de latência para chamadas de serviço externo, como consultas de banco de dados. Os métodos de registro em log têm sobrecargas que permitem passar uma exceção. Isso é para que as informações de exceção, incluindo rastreamento de pilha e exceções internas, sejam registradas de forma confiável pela classe que implementa a interface, em vez de dependerem disso sendo feita em cada chamada de método de registro em log em todo o aplicativo.

    Os métodos TraceApi permitem que você acompanhe a latência de cada chamada para um serviço externo, como Banco de Dados SQL.

  3. Na pasta Log , crie um arquivo de classe chamado Logger.cs e substitua o código do modelo pelo seguinte código:

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

    A implementação usa System.Diagnostics para fazer o rastreamento. Esse é um recurso interno do .NET que facilita a geração e o uso de informações de rastreamento. Há muitos "ouvintes" que você pode usar com o rastreamento System.Diagnostics, para gravar logs em arquivos, por exemplo, ou para gravá-los no armazenamento de blobs no Azure. Confira algumas das opções e links para outros recursos para obter mais informações em Solução de problemas de sites do Azure no Visual Studio. Para este tutorial, você examinará apenas os logs na janela Saída do Visual Studio.

    Em um aplicativo de produção, talvez você queira considerar o rastreamento de pacotes diferentes de System.Diagnostics, e a interface ILogger torna relativamente fácil alternar para um mecanismo de rastreamento diferente se você decidir fazer isso.

Criar classes de interceptador

Em seguida, você criará as classes que o Entity Framework chamará sempre que enviar uma consulta para o banco de dados, uma para simular erros transitórios e outra para fazer o registro em log. Essas classes de interceptador devem derivar da DbCommandInterceptor classe . Neles, você escreve substituições de método que são chamadas automaticamente quando a consulta está prestes a ser executada. Nesses métodos, você pode examinar ou registrar a consulta que está sendo enviada para o banco de dados e pode alterar a consulta antes que ela seja enviada para o banco de dados ou retornar algo para o Entity Framework por conta própria sem nem mesmo passar a consulta para o banco de dados.

  1. Para criar a classe interceptor que registrará todas as consultas SQL enviadas ao banco de dados, crie um arquivo de classe chamado SchoolInterceptorLogging.cs na pasta DAL e substitua o código de modelo pelo seguinte código:

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

    Para consultas ou comandos bem-sucedidos, esse código grava um log de informações com informações de latência. Para exceções, ele cria um log de erros.

  2. Para criar a classe interceptor que gerará erros transitórios fictícios quando você inserir "Throw" na caixa Pesquisar , crie um arquivo de classe chamado SchoolInterceptorTransientErrors.cs na pasta DAL e substitua o código de modelo pelo seguinte código:

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

    Esse código substitui apenas o ReaderExecuting método , que é chamado para consultas que podem retornar várias linhas de dados. Se você quisesse marcar resiliência de conexão para outros tipos de consultas, também poderia substituir os NonQueryExecuting métodos e ScalarExecuting , como faz o interceptador de log.

    Quando você executa a página Aluno e insere "Gerar" como a cadeia de caracteres de pesquisa, esse código cria uma exceção de Banco de Dados SQL fictícia para o número de erro 20, um tipo conhecido como normalmente transitório. Outros números de erro atualmente reconhecidos como transitórios são 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 e 40613, mas estão sujeitos a alterações em novas versões do Banco de Dados SQL.

    O código retorna a exceção ao Entity Framework em vez de executar a consulta e transmitir os resultados da consulta. A exceção transitória é retornada quatro vezes e, em seguida, o código é revertido para o procedimento normal de passar a consulta para o banco de dados.

    Como tudo está registrado, você poderá ver que o Entity Framework tenta executar a consulta quatro vezes antes de finalmente ser bem-sucedido, e a única diferença no aplicativo é que leva mais tempo para renderizar uma página com resultados de consulta.

    O número de vezes que o Entity Framework tentará novamente é configurável; o código especifica quatro vezes porque esse é o valor padrão para a política de execução Banco de Dados SQL. Se você alterar a política de execução, também alterará o código aqui que especifica quantas vezes erros transitórios são gerados. Você também pode alterar o código para gerar mais exceções para que o Entity Framework gere a RetryLimitExceededException exceção.

    O valor que você inserir na caixa Pesquisar estará em command.Parameters[0] e command.Parameters[1] (um é usado para o nome e outro para o sobrenome). Quando o valor "%Throw%" é encontrado, "Throw" é substituído nesses parâmetros por "um" para que alguns alunos sejam encontrados e retornados.

    Essa é apenas uma maneira conveniente de testar a resiliência da conexão com base na alteração de alguma entrada para a interface do usuário do aplicativo. Você também pode escrever código que gera erros transitórios para todas as consultas ou atualizações, conforme explicado posteriormente nos comentários sobre o método DbInterception.Add .

  3. Em Global.asax, adicione as seguintes using instruções:

    using ContosoUniversity.DAL;
    using System.Data.Entity.Infrastructure.Interception;
    
  4. Adicione as linhas realçadas ao Application_Start método :

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

    Essas linhas de código são o que faz com que o código do interceptador seja executado quando o Entity Framework envia consultas para o banco de dados. Observe que, como você criou classes interceptadoras separadas para simulação de erro transitória e registro em log, você pode habilitá-las e desabilitá-las de forma independente.

    Você pode adicionar interceptadores usando o DbInterception.Add método em qualquer lugar do código; ele não precisa estar no Application_Start método . Outra opção é colocar esse código na classe DbConfiguration que você criou anteriormente para configurar a política de execução.

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

    Onde quer que você coloque esse código, tenha cuidado para não executar DbInterception.Add para o mesmo interceptador mais de uma vez ou obterá instâncias de interceptador adicionais. Por exemplo, se você adicionar o interceptador de log duas vezes, verá dois logs para cada consulta SQL.

    Os interceptadores são executados na ordem de registro (a ordem na qual o DbInterception.Add método é chamado). A ordem pode importar dependendo do que você está fazendo no interceptador. Por exemplo, um interceptador pode alterar o comando SQL que ele obtém na CommandText propriedade . Se ele alterar o comando SQL, o próximo interceptador obterá o comando SQL alterado, não o comando SQL original.

    Você escreveu o código de simulação de erro transitório de uma maneira que permite causar erros transitórios inserindo um valor diferente na interface do usuário. Como alternativa, você pode escrever o código interceptador para sempre gerar a sequência de exceções transitórias sem verificar se há um valor de parâmetro específico. Em seguida, você pode adicionar o interceptador somente quando quiser gerar erros transitórios. No entanto, se você fizer isso, não adicione o interceptador até que a inicialização do banco de dados seja concluída. Em outras palavras, faça pelo menos uma operação de banco de dados, como uma consulta em um de seus conjuntos de entidades antes de começar a gerar erros transitórios. O Entity Framework executa várias consultas durante a inicialização do banco de dados e elas não são executadas em uma transação, portanto, erros durante a inicialização podem fazer com que o contexto entre em um estado inconsistente.

Testar a nova configuração

  1. Pressione F5 para executar o aplicativo no modo de depuração e clique na guia Alunos .

  2. Examine a janela Saída do Visual Studio para ver a saída de rastreamento. Talvez seja necessário rolar para cima após alguns erros do JavaScript para acessar os logs gravados pelo agente.

    Observe que você pode ver as consultas SQL reais enviadas para o banco de dados. Você verá algumas consultas e comandos iniciais que o Entity Framework faz para começar, verificando a versão do banco de dados e a tabela de histórico de migração (você aprenderá sobre migrações no próximo tutorial). E você vê uma consulta para paginação, para descobrir quantos alunos existem e, por fim, vê a consulta que obtém os dados do aluno.

    Registro em log para consulta normal

  3. Na página Alunos , insira "Gerar" como a cadeia de caracteres de pesquisa e clique em Pesquisar.

    Gerar cadeia de caracteres de pesquisa

    Você observará que o navegador parece travar por vários segundos enquanto o Entity Framework está repetindo a consulta várias vezes. A primeira repetição ocorre muito rapidamente e, em seguida, a espera antes de aumentar antes de cada repetição adicional. Esse processo de aguardar mais tempo antes de cada repetição ser chamado de retirada exponencial.

    Quando a página for exibida, mostrando os alunos que têm "um" em seus nomes, examine a janela de saída e você verá que a mesma consulta foi tentada cinco vezes, as primeiras quatro vezes retornando exceções transitórias. Para cada erro transitório, você verá o log que você escreve ao gerar o erro transitório na SchoolInterceptorTransientErrors classe ("Retornando um erro transitório para o comando...") e verá o log gravado quando SchoolInterceptorLogging obtiver a exceção.

    Saída de registro em log mostrando novas tentativas

    Como você inseriu uma cadeia de caracteres de pesquisa, a consulta que retorna os dados do aluno é parametrizada:

    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:
    

    Você não está registrando o valor dos parâmetros, mas pode fazer isso. Se você quiser ver os valores de parâmetro, poderá gravar o código de log para obter valores de parâmetro da Parameters propriedade do DbCommand objeto que você obtém nos métodos do interceptador.

    Observe que você não pode repetir esse teste, a menos que interrompa o aplicativo e reinicie-o. Se você quisesse testar a resiliência da conexão várias vezes em uma única execução do aplicativo, poderia escrever código para redefinir o contador de erros em SchoolInterceptorTransientErrors.

  4. Para ver a diferença que a estratégia de execução (política de repetição) faz, comente a SetExecutionStrategy linha em SchoolConfiguration.cs, execute a página Alunos no modo de depuração novamente e pesquise "Gerar" novamente.

    Desta vez, o depurador para na primeira exceção gerada imediatamente quando tenta executar a consulta pela primeira vez.

    Exceção fictícia

  5. Descompacte a linha SetExecutionStrategy em SchoolConfiguration.cs.

Obter o código

Baixar Projeto Concluído

Recursos adicionais

Links para outros recursos do Entity Framework podem ser encontrados em ASP.NET Acesso a Dados – Recursos recomendados.

Próximas etapas

Neste tutorial, você:

  • Resiliência de conexão habilitada
  • Interceptação de comando habilitada
  • Testou a nova configuração

Avance para o próximo artigo para saber mais sobre as migrações do Code First e a implantação do Azure.