Share via


ASP.NET Web Forms 연결 복원력 및 명령 인터셉션

작성자: Erik Reitan

이 자습서에서는 연결 복원력 및 명령 가로채기를 지원하도록 Wingtip Toys 샘플 애플리케이션을 수정합니다. 연결 복원력을 사용하도록 설정하면 Wingtip Toys 샘플 애플리케이션은 클라우드 환경의 일반적인 일시적인 오류가 발생할 때 데이터 호출을 자동으로 다시 시도합니다. 또한 명령 가로채기를 구현하여 Wingtip Toys 샘플 애플리케이션은 로그 또는 변경하기 위해 데이터베이스로 전송된 모든 SQL 쿼리를 catch합니다.

참고

이 Web Forms 자습서는 Tom Dykstra의 다음 MVC 자습서를 기반으로 했습니다.
ASP.NET MVC 애플리케이션에서 Entity Framework와의 연결 복원력 및 명령 가로채기

학습할 내용:

  • 연결 복원력을 제공하는 방법
  • 명령 가로채기를 구현하는 방법

사전 요구 사항

시작하기 전에 컴퓨터에 다음 소프트웨어가 설치되어 있는지 확인합니다.

연결 복원력

Windows Azure에 애플리케이션을 배포하는 것을 고려할 때 고려해야 할 한 가지 옵션은 클라우드 데이터베이스 서비스인 WindowsAzure SQL Database에 데이터베이스를 배포하는 것입니다. 일시적인 연결 오류는 일반적으로 웹 서버와 데이터베이스 서버가 동일한 데이터 센터에서 직접 연결된 경우보다 클라우드 데이터베이스 서비스에 연결할 때 더 자주 발생합니다. 클라우드 웹 서버와 클라우드 데이터베이스 서비스가 동일한 데이터 센터에 호스트되더라도 부하 분산 장치와 같은 문제가 있을 수 있는 네트워크 연결이 더 많이 있습니다.

또한 클라우드 서비스는 일반적으로 다른 사용자가 공유하므로 응답성이 영향을 받을 수 있습니다. 또한 데이터베이스에 대한 액세스는 제한될 수 있습니다. 제한은 데이터베이스 서비스가 SLA( 서비스 수준 계약 )에서 허용되는 것보다 더 자주 액세스하려고 할 때 예외를 throw한다는 것을 의미합니다.

클라우드 서비스에 액세스할 때 발생하는 많은 또는 대부분의 연결 문제는 일시적입니다. 즉, 단기간에 resolve. 따라서 데이터베이스 작업을 시도하고 일반적으로 일시적인 오류 유형을 가져오는 경우 잠시 기다린 후 작업을 다시 시도할 수 있으며 작업이 성공할 수 있습니다. 일시적 오류를 자동으로 다시 시도하여 대부분의 오류를 고객에게 보이지 않게 하여 사용자에게 훨씬 더 나은 환경을 제공할 수 있습니다. Entity Framework 6의 연결 복원력 기능은 실패한 SQL 쿼리를 다시 시도하는 프로세스를 자동화합니다.

특정 데이터베이스 서비스에 대해 연결 복원력 기능을 적절하게 구성해야 합니다.

  1. 일시적일 수 있는 예외를 알아야 합니다. 예를 들어 프로그램 버그로 인한 오류가 아니라 네트워크 연결의 일시적인 손실로 인한 오류를 다시 시도하려고 합니다.
  2. 실패한 작업의 재시도 사이에 적절한 시간을 기다려야 합니다. 사용자가 응답을 기다리는 온라인 웹 페이지의 경우보다 일괄 처리 프로세스에 대한 재시도 사이에 더 오래 기다릴 수 있습니다.
  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 Code-Based 구성을 참조하세요.

  1. Logic 폴더에서 AddProducts.cs 파일을 엽니다.

  2. 노란색으로 using 강조 표시된 대로 에 대한 System.Data.Entity.Infrastructure 문을 추가합니다.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using WingtipToys.Models;
    using System.Data.Entity.Infrastructure;
    
  3. catch 가 노란색으로 강조 표시된 대로 기록되도록 RetryLimitExceededException 메서드에 블록을 AddProduct 추가합니다.

    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 catch하면 일시적일 수 있는 유일한 오류는 이미 여러 번 시도되고 실패했습니다. 반환된 실제 예외는 예외로 RetryLimitExceededException 래핑됩니다. 또한 일반적인 catch 블록도 추가했습니다. 예외에 대한 RetryLimitExceededException 자세한 내용은 Entity Framework 연결 복원력/재시도 논리를 참조하세요.

명령 가로채기

다시 시도 정책을 설정했으므로 예상대로 작동하는지 확인하기 위해 어떻게 테스트합니까? 특히 로컬에서 실행할 때 일시적인 오류가 발생하도록 강제하는 것은 쉽지 않으며, 실제 일시적인 오류를 자동화된 단위 테스트에 통합하는 것은 특히 어려울 수 있습니다. 연결 복원력 기능을 테스트하려면 Entity Framework가 SQL Server 보내는 쿼리를 가로채고 SQL Server 응답을 일반적으로 일시적인 예외 형식으로 바꾸는 방법이 필요합니다.

또한 쿼리 가로채기를 사용하여 클라우드 애플리케이션에 대한 모범 사례를 구현할 수 있습니다. 데이터베이스 서비스와 같은 외부 서비스에 대한 모든 호출의 대기 시간 및 성공 또는 실패를 기록합니다.

자습서의 이 섹션에서는 로깅 및 일시적인 오류 시뮬레이션에 Entity Framework의 가로채기 기능을 사용합니다.

로깅 인터페이스 및 클래스 만들기

로깅에 대한 모범 사례는 또는 로깅 클래스에 대한 하드 코딩 호출 System.Diagnostics.Trace 대신 을 사용하여 interface 수행하는 것입니다. 이렇게 하면 나중에 로깅 메커니즘을 더 쉽게 변경할 수 있습니다. 따라서 이 섹션에서는 로깅 인터페이스와 이를 구현하는 클래스를 만듭니다.

위의 절차에 따라 Visual Studio에서 WingtipToys 샘플 애플리케이션을 다운로드하고 열었습니다.

  1. WingtipToys 프로젝트에 폴더를 만들고 이름을 로깅으로 지정합니다.

  2. 로깅 폴더에서 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);
    
        }
    }
    

    인터페이스는 로그의 상대적 중요도를 나타내는 세 가지 추적 수준과 데이터베이스 쿼리와 같은 외부 서비스 호출에 대한 대기 시간 정보를 제공하도록 설계된 세 가지 추적 수준을 제공합니다. 로깅 메서드에는 예외를 전달할 수 있는 오버로드가 있습니다. 따라서 스택 추적 및 내부 예외를 포함한 예외 정보는 애플리케이션 전체의 각 로깅 메서드 호출에서 수행되는 것에 의존하는 대신 인터페이스를 구현하는 클래스에 의해 안정적으로 기록됩니다.

    메서드를 TraceApi 사용하면 SQL Database 같은 외부 서비스에 대한 각 호출의 대기 시간을 추적할 수 있습니다.

  3. 로깅 폴더에서 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의 기본 제공 기능입니다. 추적과 함께 System.Diagnostics 사용할 수 있는 많은 "수신기"가 있습니다. 예를 들어 파일에 로그를 쓰거나 Windows Azure의 Blob Storage에 쓸 수 있습니다. 자세한 내용은 Visual Studio의 Windows Azure 웹 사이트 문제 해결에서 몇 가지 옵션 및 다른 리소스에 대한 링크를 참조하세요. 이 자습서에서는 Visual Studio 출력 창의 로그만 살펴봅니다.

프로덕션 애플리케이션에서는 이외의 추적 프레임워크 System.Diagnostics를 사용하는 것이 좋습니다. ILogger 인터페이스를 사용하면 다른 추적 메커니즘으로 비교적 쉽게 전환할 수 있습니다.

인터셉터 클래스 만들기

다음으로, 엔터티 프레임워크가 데이터베이스에 쿼리를 보낼 때마다 호출할 클래스를 만듭니다. 하나는 일시적인 오류를 시뮬레이션하고 다른 하나는 로깅을 수행합니다. 이러한 인터셉터 클래스는 클래스에서 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 반환할 수 있는 쿼리에 대해 호출되는 메서드만 재정의합니다. 다른 유형의 쿼리에 대한 연결 복원력을 검사 하려는 경우 로깅 인터셉터처럼 및 ScalarExecuting 메서드를 재정 NonQueryExecuting 의할 수도 있습니다.

    나중에 "관리"로 로그인하고 위쪽 탐색 모음에서 관리 링크를 선택합니다. 그런 다음 AdminPage.aspx 페이지에서 "Throw"라는 제품을 추가합니다. 이 코드는 오류 번호 20에 대한 더미 SQL Database 예외를 만듭니다. 일반적으로 일시적인 형식입니다. 현재 일시적인 것으로 인식되는 기타 오류 번호는 64, 233, 10053, 10054, 10060, 10928, 10929, 40197, 40501 및 40613이지만 새 버전의 SQL Database 변경될 수 있습니다. 제품의 이름이 "TransientErrorExample"으로 변경되며 InterceptorTransientErrors.cs 파일의 코드에서 따를 수 있습니다.

    이 코드는 쿼리를 실행하고 결과를 다시 전달하는 대신 Entity Framework에 예외를 반환합니다. 일시적인 예외는 번 반환된 다음, 코드는 쿼리를 데이터베이스에 전달하는 일반적인 프로시저로 되돌아갑니다.

    모든 항목이 기록되므로 Entity Framework가 마지막으로 성공하기 전에 쿼리를 네 번 실행하려고 하는 것을 볼 수 있으며, 애플리케이션의 유일한 차이점은 쿼리 결과가 있는 페이지를 렌더링하는 데 시간이 오래 걸린다는 것입니다.

    Entity Framework가 다시 시도할 횟수를 구성할 수 있습니다. 코드는 SQL Database 실행 정책의 기본값이므로 네 번 지정합니다. 실행 정책을 변경하는 경우 일시적 오류가 생성되는 횟수를 지정하는 코드도 여기에 변경합니다. Entity Framework에서 예외를 throw하도록 코드를 변경하여 더 많은 예외를 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 있을 필요는 없습니다. 메서드에 Application_Start 인터셉터를 추가하지 않은 경우 다른 옵션은 WingtipToysConfiguration.cs 라는 클래스를 업데이트하거나 추가하고 위의 코드를 클래스의 WingtipToysConfiguration 생성자 끝에 배치하는 것입니다.

이 코드를 넣을 때마다 동일한 인터셉터에 대해 두 번 이상 실행 DbInterception.Add 하지 않도록 주의하거나 추가 인터셉터 인스턴스를 가져옵니다. 예를 들어 로깅 인터셉터를 두 번 추가하면 모든 SQL 쿼리에 대해 두 개의 로그가 표시됩니다.

인터셉터는 등록 순서(메서드가 호출되는 DbInterception.Add 순서)로 실행됩니다. 순서는 인터셉터에서 수행하는 일에 따라 중요할 수 있습니다. 예를 들어 인터셉터 속성에 가져오는 CommandText SQL 명령을 변경할 수 있습니다. SQL 명령을 변경하면 다음 인터셉터에서 원래 SQL 명령이 아닌 변경된 SQL 명령을 가져옵니다.

UI에 다른 값을 입력하여 일시적인 오류를 발생시킬 수 있는 방식으로 일시적인 오류 시뮬레이션 코드를 작성했습니다. 또는 특정 매개 변수 값을 확인하지 않고 항상 일시적인 예외 시퀀스를 생성하는 인터셉터 코드를 작성할 수 있습니다. 그런 다음 일시적인 오류를 생성하려는 경우에만 인터셉터를 추가할 수 있습니다. 그러나 이 작업을 수행하는 경우 데이터베이스 초기화가 완료될 때까지 인터셉터를 추가하지 마세요. 즉, 일시적인 오류를 생성하기 전에 엔터티 집합 중 하나에 대한 쿼리와 같은 데이터베이스 작업을 하나 이상 수행합니다. Entity Framework는 데이터베이스 초기화 중에 여러 쿼리를 실행하며 트랜잭션에서 실행되지 않으므로 초기화 중 오류가 발생하면 컨텍스트가 일관되지 않은 상태가 될 수 있습니다.

로깅 및 연결 복원력 테스트

  1. Visual Studio에서 F5 키를 눌러 디버그 모드에서 애플리케이션을 실행한 다음 암호로 "Pa$$word"을 사용하여 "관리"로 로그인합니다.

  2. 위쪽의 탐색 모음에서 관리 선택합니다.

  3. 적절한 설명, 가격 및 이미지 파일을 사용하여 "Throw"라는 새 제품을 입력합니다.

  4. 제품 추가 단추를 누릅니다.
    Entity Framework가 쿼리를 여러 번 다시 시도하는 동안 브라우저가 몇 초 동안 중단되는 것처럼 보입니다. 첫 번째 다시 시도는 매우 빠르게 수행된 다음 각 추가 재시도 전에 대기가 증가합니다. 각 재시도를 기하급수적 백오프 라고 하려면 이 프로세스를 더 오래 대기합니다.

  5. 페이지가 더 이상 로드되지 않을 때까지 기다립니다.

  6. 프로젝트를 중지하고 Visual Studio 출력 창을 확인하여 추적 출력을 확인합니다. 디버그 -Windows ->>출력을 선택하여 출력 창을 찾을 수 있습니다. 로거가 작성한 다른 여러 로그를 스크롤해야 할 수 있습니다.

    데이터베이스로 전송된 실제 SQL 쿼리를 볼 수 있습니다. Entity Framework가 시작하기 위해 수행하는 몇 가지 초기 쿼리 및 명령이 표시되어 데이터베이스 버전 및 마이그레이션 기록 테이블을 확인합니다.
    출력 창
    애플리케이션을 중지하고 다시 시작하지 않으면 이 테스트를 반복할 수 없습니다. 애플리케이션의 단일 실행에서 연결 복원력을 여러 번 테스트하려면 에서 InterceptorTransientErrors 오류 카운터를 다시 설정하는 코드를 작성할 수 있습니다.

  7. 실행 전략(재시도 정책)의 차이를 확인하려면 Logic 폴더의 WingtipToysConfiguration.cs 파일에서 줄을 주석 SetExecutionStrategy 으로 처리하고, 디버그 모드에서 관리 페이지를 다시 실행하고, "Throw"라는 제품을 다시 추가합니다.

    이번에는 처음 쿼리를 실행하려고 할 때 디버거가 처음 생성된 예외에서 즉시 중지됩니다.
    디버깅 - 세부 정보 보기

  8. WingtipToysConfiguration.cs 파일에서 줄의 주석 처리를 SetExecutionStrategy 제거합니다.

요약

이 자습서에서는 연결 복원력 및 명령 가로채기를 지원하도록 Web Forms 샘플 애플리케이션을 수정하는 방법을 살펴보았습니다.

다음 단계

ASP.NET Web Forms 연결 복원력 및 명령 가로채기를 검토한 후 ASP.NET 4.5의 ASP.NET Web Forms 항목 비동기 메서드를 검토합니다. 이 항목에서는 Visual Studio를 사용하여 비동기 ASP.NET Web Forms 애플리케이션을 빌드하는 기본 사항을 설명합니다.