チュートリアル: ASP.NET MVC アプリで Entity Framework で接続の回復性とコマンド インターセプトを使用する

これまで、アプリケーションは開発用コンピューター上の IIS Express でローカルに実行されています。 実際のアプリケーションを他のユーザーがインターネット経由で使用できるようにするには、それを Web ホスティング プロバイダーにデプロイする必要があり、データベース サーバーにデータベースをデプロイする必要があります。

このチュートリアルでは、接続の回復性とコマンド インターセプトを使用する方法について説明します。 これらは Entity Framework 6 の 2 つの重要な機能であり、クラウド環境にデプロイする場合に特に重要です。接続の回復性 (一時的なエラーの自動再試行) とコマンド インターセプト (ログ記録または変更のためにデータベースに送信されたすべての SQL クエリをキャッチする) です。

この接続の回復性とコマンド インターセプトのチュートリアルは省略可能です。 このチュートリアルをスキップする場合は、後続のチュートリアルでいくつかの小さな調整を行う必要があります。

このチュートリアルでは、次の作業を行いました。

  • 接続の回復性を有効にする
  • コマンドインターセプトを有効にする
  • 新しい構成をテストする

前提条件

接続の回復性を有効にする

アプリケーションを Windows Azure にデプロイするときは、クラウド データベース サービスである Windows Azure SQL Database にデータベースをデプロイします。 一時的な接続エラーは、通常、クラウド データベース サービスに接続する場合、Web サーバーとデータベース サーバーが同じデータ センターに直接接続されている場合よりも頻繁に発生します。 クラウド Web サーバーとクラウド データベース サービスが同じデータ センターでホストされている場合でも、それらの間にロード バランサーなどの問題が発生する可能性のあるネットワーク接続が増えています。

また、クラウド サービスは通常、他のユーザーによって共有されます。つまり、その応答性が影響を受ける可能性があります。 また、データベースへのアクセスは調整の影響を受ける可能性があります。 調整とは、サービス レベル アグリーメント (SLA) で許可されているよりも頻繁にアクセスしようとすると、データベース サービスが例外をスローすることを意味します。

クラウド サービスにアクセスするときの接続の問題の多くまたはほとんどは一時的なもので、短時間で解決されます。 そのため、データベース操作を試して、通常は一時的なエラーの種類を取得する場合は、しばらく待ってから操作を再試行すると、操作が成功する可能性があります。 一時的なエラーを処理する場合は、ユーザーが自動的に再試行し、そのほとんどを顧客に見えないようにすることで、ユーザーにはるかに優れたエクスペリエンスを提供できます。 Entity Framework 6 の接続の回復性機能により、失敗した SQL クエリを再試行するプロセスが自動化されます。

接続の回復性機能は、特定のデータベース サービスに対して適切に構成する必要があります。

  • 一時的である可能性が高い例外を把握する必要があります。 たとえば、プログラムのバグによるエラーではなく、ネットワーク接続の一時的な損失によって発生したエラーを再試行する必要があります。
  • 失敗した操作の再試行の間に適切な時間待機する必要があります。 バッチ プロセスの再試行の間は、ユーザーが応答を待機しているオンライン Web ページよりも長く待機できます。
  • それは、それが放棄する前に、適切な回数を再試行する必要があります。 オンライン アプリケーションで行うバッチ プロセスで再試行回数を増やしたい場合があります。

これらの設定は、Entity Framework プロバイダーでサポートされている任意のデータベース環境に対して手動で構成できますが、Windows Azure SQL Database を使用するオンライン アプリケーションで通常適切に機能する既定値は既に構成されており、これらは Contoso University アプリケーションに実装する設定です。

接続の回復性を有効にするために必要なのは、DbConfiguration クラスから派生したクラスをアセンブリに作成することであり、そのクラスではSQL Database実行戦略を設定します。EF では再試行ポリシーのもう 1 つの用語です。

  1. DAL フォルダーに SchoolConfiguration.cs という名前のクラス ファイルを追加します。

  2. テンプレート コードを次のコードに置き換えます。

    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 は、 から DbConfiguration派生したクラスで見つけたコードを自動的に実行します。 クラスを DbConfiguration 使用すると、 Web.config ファイルで行うコードで構成タスクを実行できます。 詳細については、「 EntityFramework Code-Based 構成」を参照してください。

  3. StudentController.cs で、 の System.Data.Entity.Infrastructureステートメントをusing追加します。

    using System.Data.Entity.Infrastructure;
    
  4. 代わりに例外をcatchキャッチするように、例外をキャッチDataExceptionRetryLimitExceededExceptionするすべてのブロックを変更します。 次に例を示します。

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

    を使用 DataException して、わかりやすい "再試行" メッセージを表示するために、一時的なエラーを特定しようとしていました。 ただし、再試行ポリシーを有効にしたので、一時的である可能性が高い唯一のエラーは既に試行され、複数回失敗し、返された実際の例外は例外に RetryLimitExceededException ラップされます。

詳細については、「 Entity Framework の接続の回復性/再試行ロジック」を参照してください。

コマンドインターセプトを有効にする

再試行ポリシーを有効にしたので、期待どおりに動作していることをテストして確認するにはどうすればよいですか? 特にローカルで実行している場合は、一時的なエラーを強制的に発生させるのは簡単ではなく、実際の一時的なエラーを自動単体テストに統合することは特に困難です。 接続の回復性機能をテストするには、Entity Framework がSQL Serverに送信するクエリをインターセプトし、SQL Server応答を通常は一時的な例外の種類に置き換える方法が必要です。

また、クエリインターセプトを使用して、クラウド アプリケーションのベスト プラクティスを実装することもできます。データベース サービスなどの外部サービスに対するすべての呼び出しの待機時間と成功または失敗をログに記録 します。 EF6 には、ログ記録を簡単に実行できる 専用のログ記録 API が用意されていますが、チュートリアルのこのセクションでは、ログ記録と一時的なエラーのシミュレートの両方のために、Entity Framework の インターセプト機能 を直接使用する方法について説明します。

ログ インターフェイスとクラスを作成する

ログ記録のベスト プラクティスは、System.Diagnostics.Trace またはログ クラスへの呼び出しをハードコーディングするのではなく、インターフェイスを使用して実行することです。 これにより、後でログメカニズムを変更する必要がある場合に簡単に変更できます。 そのため、このセクションでは、ログ インターフェイスとそれを実装するクラスを作成します。/p>

  1. プロジェクトにフォルダーを作成し、Logging という名前 を付けます

  2. Logging フォルダーで、ILogger.cs という名前のクラス ファイルを作成し、テンプレート コードを次のコードに置き換えます。

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

    インターフェイスには、ログの相対的な重要度を示す 3 つのトレース レベルと、データベース クエリなどの外部サービス呼び出しの待機時間情報を提供するように設計されたトレース レベルが用意されています。 ログ メソッドには、例外を渡すオーバーロードがあります。 これは、スタック トレースや内部例外を含む例外情報が、アプリケーション全体の各ログ メソッド呼び出しで実行されるのではなく、インターフェイスを実装するクラスによって確実にログに記録されるようにするためです。

    TraceApi メソッドを使用すると、SQL Databaseなどの外部サービスへの各呼び出しの待機時間を追跡できます。

  3. Logging フォルダーで Logger.cs という名前のクラス ファイルを作成し、テンプレート コードを次のコードに置き換えます。

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

    この実装では、System.Diagnostics を使用してトレースを実行します。 これは.NET の組み込み機能であり、トレース情報を簡単に生成して使用できます。 System.Diagnostics トレースで使用できる "リスナー" は多数あります。ログをファイルに書き込む場合や、Azure の BLOB ストレージに書き込む場合などです。 詳細については、「 Visual Studio での Azure Web サイトのトラブルシューティング」のオプションと他のリソースへのリンクを参照してください。 このチュートリアルでは、Visual Studio の [出力] ウィンドウのログのみを確認します。

    運用アプリケーションでは、System.Diagnostics 以外のトレース パッケージを検討することをお勧めします。また、ILogger インターフェイスを使用すると、これを行う場合に別のトレース メカニズムに比較的簡単に切り替えることができます。

インターセプター クラスを作成する

次に、Entity Framework がデータベースにクエリを送信するたびに呼び出すクラスを作成します。1 つは一時的なエラーをシミュレートし、もう 1 つはログ記録を実行します。 これらのインターセプター クラスは、 クラスから派生する DbCommandInterceptor 必要があります。 その中で、クエリの実行時に自動的に呼び出されるメソッドオーバーライドを記述します。 これらのメソッドでは、データベースに送信されるクエリを調べたりログに記録したりできます。また、データベースに送信される前にクエリを変更したり、クエリをデータベースに渡さずに Entity Framework に何かを返したりできます。

  1. データベースに送信されるすべての SQL クエリをログに記録するインターセプター クラスを作成するには、DAL フォルダーに SchoolInterceptorLogging.cs という名前のクラス ファイルを作成し、テンプレート コードを次のコードに置き換えます。

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

    クエリまたはコマンドを正常に実行するために、このコードは待機時間情報を含む情報ログを書き込みます。 例外の場合は、エラー ログが作成されます。

  2. 検索ボックスに「Throw」と入力したときにダミーの一時的なエラーを生成するインターセプター クラスを作成するには、DAL フォルダーに SchoolInterceptorTransientErrors.cs という名前のクラス ファイルを作成し、テンプレート コードを次のコードに置き換えます。

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

    このコードは、 メソッドのみをオーバーライド ReaderExecuting します。このメソッドは、複数のデータ行を返すことができるクエリに対して呼び出されます。 他の種類のクエリに対して接続の回復性をチェックする場合は、ログ インターセプターと同様にNonQueryExecuting、 メソッドと ScalarExecuting メソッドをオーバーライドすることもできます。

    Student ページを実行し、検索文字列として「Throw」と入力すると、このコードでは、エラー番号 20 のダミー SQL Database例外が作成されます。これは、通常は一時的であることが知られている型です。 現在、一時的と認識されているその他のエラー番号は 64、233、10053、10054、10060、10928、10929、40197、40501、40613 ですが、これらは新しいバージョンの SQL Database で変更される可能性があります。

    このコードは、クエリを実行してクエリ結果を返す代わりに、Entity Framework に例外を返します。 一時的な例外が 4 回返された後、コードはクエリをデータベースに渡す通常のプロシージャに戻ります。

    すべてがログに記録されるため、Entity Framework が最終的に成功する前にクエリを 4 回実行しようとしていることがわかります。アプリケーションの唯一の違いは、クエリ結果を含むページのレンダリングに時間がかかるということです。

    Entity Framework が再試行する回数は構成可能です。コードは、SQL Database実行ポリシーの既定値であるため、4 回指定します。 実行ポリシーを変更する場合は、ここで、一時的なエラーが生成される回数を指定するコードも変更します。 コードを変更して、Entity Framework が例外をスローするように、より多くの例外を RetryLimitExceededException 生成することもできます。

    [検索] ボックスに入力する値は command.Parameters[0] 、 と command.Parameters[1] になります (1 つは名、もう 1 つは姓に使用されます)。 値 "%Throw%" が見つかると、それらのパラメーターの "Throw" が "an" に置き換えられ、一部の学生が見つけて返されます。

    これは、アプリケーション UI への入力の変更に基づいて接続の回復性をテストするための便利な方法です。 DbInterception.Add メソッドに関するコメントで後述するように、すべてのクエリまたは更新に対して一時的なエラーを生成するコードを記述することもできます。

  3. Global.asax で、次usingのステートメントを追加します。

    using ContosoUniversity.DAL;
    using System.Data.Entity.Infrastructure.Interception;
    
  4. 強調表示された行を メソッドに Application_Start 追加します。

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

    これらのコード行によって、Entity Framework がデータベースにクエリを送信するときにインターセプター コードが実行されます。 一時的なエラー シミュレーションとログ記録用に個別のインターセプター クラスを作成したため、それらを個別に有効または無効にできることに注意してください。

    インターセプターは、コード内の DbInterception.Add 任意の場所で メソッドを使用して追加できます。メソッド内にある Application_Start 必要はありません。 もう 1 つのオプションは、実行ポリシーを構成するために前に作成した DbConfiguration クラスにこのコードを配置することです。

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

    このコードを配置する場合は、同じインターセプターに対して複数回実行 DbInterception.Add しないように注意してください。または、追加のインターセプター インスタンスが取得されます。 たとえば、ログ インターセプターを 2 回追加すると、SQL クエリごとに 2 つのログが表示されます。

    インターセプターは、登録の順序 (メソッドが呼び出される DbInterception.Add 順序) で実行されます。 インターセプターで何を行っているかによって、順序が重要になる場合があります。 たとえば、インターセプターは、 プロパティで取得する SQL コマンドを CommandText 変更する場合があります。 SQL コマンドが変更された場合、次のインターセプターは、元の SQL コマンドではなく、変更された SQL コマンドを取得します。

    UI に別の値を入力して一時的なエラーを発生させる方法で、一時的なエラー シミュレーション コードを記述しました。 別の方法として、特定のパラメーター値を確認せずに常に一時的な例外のシーケンスを生成するインターセプター コードを記述することもできます。 その後、一時的なエラーを生成する場合にのみインターセプターを追加できます。 ただし、これを行う場合は、データベースの初期化が完了するまでインターセプターを追加しないでください。 つまり、一時的なエラーの生成を開始する前に、エンティティ セットの 1 つに対するクエリなど、少なくとも 1 つのデータベース操作を実行します。 Entity Framework では、データベースの初期化中に複数のクエリが実行され、トランザクションでは実行されないため、初期化中にエラーが発生すると、コンテキストが不整合な状態になる可能性があります。

新しい構成をテストする

  1. F5 キーを押してデバッグ モードでアプリケーションを実行し、[学生] タブをクリックします。

  2. Visual Studio の [出力] ウィンドウを見て、トレース出力を確認します。 ロガーによって書き込まれたログを取得するには、いくつかの JavaScript エラーを超えて上にスクロールする必要がある場合があります。

    データベースに送信された実際の SQL クエリを確認できることに注意してください。 Entity Framework が開始するために行う最初のクエリとコマンドが表示され、データベースのバージョンと移行履歴テーブルが確認されます (次のチュートリアルで移行について学習します)。 また、ページングのクエリが表示され、学生の数が確認され、最後に学生データを取得するクエリが表示されます。

    通常のクエリのログ記録

  3. [ 学生 ] ページで、検索文字列として「スロー」と入力し、[ 検索] をクリックします。

    検索文字列をスローする

    Entity Framework がクエリを数回再試行している間、ブラウザーが数秒ハングしていることがわかります。 最初の再試行は非常に迅速に行われます。その後、追加の再試行のたびに前の待機が増えます。 このプロセスは、各再試行が 指数バックオフと呼ばれる前に長く待機します。

    ページが表示され、名前に "an" が含まれている学生が出力ウィンドウを確認すると、同じクエリが 5 回試行され、最初の 4 回が一時的な例外を返していることがわかります。 一時的なエラーごとに、 クラスで SchoolInterceptorTransientErrors 一時的なエラーを生成するときに書き込むログが表示されます ("command..." の一時的なエラーを返します)。例外を取得すると、ログが SchoolInterceptorLogging 書き込まれます。

    再試行を示すログ出力

    検索文字列を入力したので、学生データを返すクエリはパラメーター化されます。

    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:
    

    パラメーターの値をログに記録していませんが、これを行うことができます。 パラメーター値を表示する場合は、インターセプター メソッドで取得するオブジェクトの DbCommand プロパティからパラメーター値をParameters取得するログ コードを記述できます。

    アプリケーションを停止して再起動しない限り、このテストを繰り返すことはできません。 アプリケーションの 1 回の実行で接続の回復性を複数回テストできるようにする場合は、 で SchoolInterceptorTransientErrorsエラー カウンターをリセットするコードを記述できます。

  4. 実行戦略 (再試行ポリシー) の違いを確認するには、SchoolConfiguration.cs の行をSetExecutionStrategyコメントアウトし、デバッグ モードで [学生] ページをもう一度実行し、もう一度 "スロー" を検索します。

    今回は、デバッガーが初めてクエリを実行しようとしたときに、最初に生成された例外ですぐに停止します。

    ダミー例外

  5. SchoolConfiguration.csSetExecutionStrategy 行のコメントを解除します。

コードを取得する

完了したプロジェクトのダウンロード

その他のリソース

他の Entity Framework リソースへのリンクは、「 ASP.NET データ アクセス - 推奨リソース」にあります

次の手順

このチュートリアルでは、次の作業を行いました。

  • 有効な接続の回復性
  • コマンド インターセプトを有効にしました
  • 新しい構成をテストしました

次の記事に進み、Code First の移行と Azure デプロイについて学習します。