ASP.NET Web フォームの接続回復性とコマンド傍受

作成者: Erik Reitan

このチュートリアルでは、接続の回復性とコマンド インターセプトをサポートするように Wingtip Toys サンプル アプリケーションを変更します。 接続の回復性を有効にすると、Wingtip Toys サンプル アプリケーションは、クラウド環境で一般的な一時的なエラーが発生したときにデータ呼び出しを自動的に再試行します。 また、コマンド インターセプトを実装することで、Wingtip Toys サンプル アプリケーションは、ログに記録したり、変更したりするために、データベースに送信されたすべての SQL クエリをキャッチします。

Note

この Web Forms チュートリアルは、Tom Dykstra の次の MVC チュートリアルに基づいています。
ASP.NET MVC アプリケーションの Entity Framework での接続の回復性とコマンド インターセプト

ここでは、次の内容について学習します。

  • 接続の回復性を提供する方法。
  • コマンド インターセプトを実装する方法。

前提条件

開始する前に、コンピューターに次のソフトウェアがインストールされていることを確認します。

接続の弾力性

Windows Azure へのアプリケーションのデプロイを検討する際に考慮できる 1 つのオプションは、クラウド データベース サービスである WindowsAzure SQL Database にデータベースをデプロイすることです。 一時的な接続エラーは、通常、Web サーバーとデータベース サーバーが同じデータ センターで直接接続されている場合よりも、クラウド データベース サービスに接続しているときの方が頻繁に発生します。 クラウド Web サーバーとクラウド データベース サービスが同じデータ センターでホストされていても、ロード バランサーなどの問題が発生する可能性のあるネットワーク接続で接続されていることが多く見受けられます。

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

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

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

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

これらの設定は、Entity Framework プロバイダーによってサポートされる任意のデータベース環境に対して手動で構成できます。

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

接続の回復性の実装

  1. Visual Studio で WingtipToys サンプル Web Forms アプリケーションをダウンロードして開きます。

  2. WingtipToys アプリケーションの Logic フォルダーに、WingtipToysConfiguration.cs という名前のクラス ファイルを追加します。

  3. 既存のコードを次のコードに置き換えます。

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

  1. Logic フォルダーで、AddProducts.cs ファイルを開きます。

  2. 黄色で強調表示されている System.Data.Entity.Infrastructureusing ステートメントを追加します。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using System.Data.Entity.Infrastructure;
    
  3. catch ブロックを AddProduct メソッドに追加して、黄色で強調表示されている RetryLimitExceededException がログに記録されるようにします。

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

例外 RetryLimitExceededException を追加することで、より適切なログ記録が行えるようになったり、プロセスをもう一度試すことができることを示すエラー メッセージをユーザーに表示できたりします。 例外 RetryLimitExceededException をキャッチすることで、既に再試行され、複数回失敗した一時的と思われるエラーのみが返されます。 実際に返される例外は、RetryLimitExceededException 例外でラップされます。 さらに、一般的な catch ブロックも追加しました。 RetryLimitExceededException 例外の詳細については、Entity Framework の接続の回復性と再試行ロジックに関するページを参照してください。

コマンド インターセプト

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

また、クエリ インターセプトを使用することで、データベース サービスなどの外部サービスに対するすべての呼び出しの待機時間および成功または失敗をログに記録するというクラウド アプリケーションのベスト プラクティスを実装することもできます。

チュートリアルのこのセクションでは、Entity Framework のインターセプト機能をログ記録と一時的なエラーのシミュレーションの両方に使用します。

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

ログ記録は、System.Diagnostics.Trace やログ記録クラスへの呼び出しをハード コーディングするよりも、interfaceを使用して行うことの方が適しています。 これにより、ログ記録メカニズムを後で変更する必要が生じた場合に、簡単に変更できます。 そのため、このセクションでは、ログ インターフェイスとそれを実装するクラスを作成します。

上記の手順に従って、既に WingtipToys サンプル アプリケーションをダウンロードして、Visual Studio で開きました。

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

  2. Logging フォルダーで、ILogger.cs という名前のクラス ファイルを作成し、既定のコードを次のコードに変更します。

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

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

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

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

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

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

運用アプリケーションでは、System.Diagnostics 以外のトレース フレームワークの使用を検討する場合があるかもしれません。そうする場合、ILogger インターフェイスを使用すると、別のトレース メカニズムに比較的簡単に切り替えることができます。

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

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

  1. データベースに送信される前にすべての SQL クエリをログに記録するインターセプター クラスを作成するには、Logic フォルダーに InterceptorLogging.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 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);
        }
      }
    }
    

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

  2. AdminPage.aspx という名前のページで [名前] テキストボックスに "Throw" と入力したときにダミーの一時的なエラーを生成するインターセプター クラスを作成するには、Logic フォルダーに InterceptorTransientErrors.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 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;
        }
      }
    }
    

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

    後で、"管理者" としてログインし、上部のナビゲーション バーの [管理者] リンクを選択します。 次に、AdminPage.aspx ページで、"Throw" という名前の製品を追加します。 このコードでは、エラー番号 20 (これは通常、一時的であると認識されます) に対してダミーの SQL Database 例外が作成されます。 現在一時的と認識されているその他のエラー番号は、64、233、10053、10054、10060、10928、10929、40197、40501、40613 ですが、これらは新しいバージョンの SQL Database で変更される可能性があります。 製品の名前が "TransientErrorExample" に変更され、InterceptorTransientErrors.cs ファイルのコードに従うことができます。

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

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

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

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

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

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

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

コード内の任意の場所で DbInterception.Add メソッドを使用して、インターセプターを追加できます。とはいえ、必ずしも Application_Start メソッド内である必要はありません。 もう 1 つのオプションとして、Application_Start メソッドにインターセプターを追加しなかった場合は、WingtipToysConfiguration.cs という名前のクラスを更新または追加し、上記のコードを WingtipToysConfiguration クラスのコンストラクターの末尾に配置します。

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

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

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

ログ記録と接続の回復性をテストする

  1. Visual Studio で F5 キーを押してデバッグ モードでアプリケーションを実行し、パスワードとして "Pa$$word" を使用して "管理者" としてログインします。

  2. 上部のナビゲーション バーから [管理者] を選択します。

  3. "Throw" という名前の新しい製品を入力し、適切な説明、価格、イメージ ファイルも指定します。

  4. [製品の追加] ボタンを押します。
    Entity Framework がクエリを複数回再試行している間、ブラウザーが数秒間ハングしているように見えます。 最初の再試行は非常に迅速に行われますが、その後、追加の再試行が行われるごとに、待機時間が増えます。 再試行ごとにより長く待機するこのプロセスは、エクスポネンシャル バックオフと呼ばれます。

  5. ページが読み込もうとしなくなるまで待ちます。

  6. プロジェクトを停止し、Visual Studio の [出力] ウィンドウを見てトレース出力を確認します。 [デバッグ]>[Windows]>[出力] を選択すると、[出力] ウィンドウが表示されます。 ロガーによって書き込まれた他のいくつかのログをスクロールしなければならない場合があります。

    データベースに送信された実際の SQL クエリを確認できることに注目してください。 Entity Framework が開始するために実行する最初のクエリとコマンドがいくつか表示されます。データベースのバージョンと移行履歴テーブルを確認します。
    Output Window
    アプリケーションを停止して再起動しない限り、このテストを繰り返すことはできません。 アプリケーションの 1 回の実行で接続の回復性を複数回テストできるようにする場合は、InterceptorTransientErrors でエラー カウンターをリセットするコードを記述できます。

  7. 実行戦略 (再試行ポリシー) の違いを確認するには、Logic フォルダー内で WingtipToysConfiguration.cs ファイルの SetExecutionStrategy 行をコメント アウトし、デバッグ モードで [管理者] ページをもう一度実行し、"Throw" という名前の製品をもう一度追加します。

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

  8. WingtipToysConfiguration.cs ファイルの SetExecutionStrategy 行をコメント解除します。

まとめ

このチュートリアルでは、接続の回復性とコマンド インターセプトをサポートするように Web Forms サンプル アプリケーションを変更する方法を見てきました。

次のステップ

ASP.NET Web Forms で接続の回復性とコマンド インターセプトを確認したら、ASP.NET 4.5 の非同期メソッドに関する ASP.NET Web Forms のトピックをご覧ください。 このトピックでは、Visual Studio を使用した非同期 ASP.NET Web Forms アプリケーションの構築に関する基本となる事柄について説明します。