使用实体框架使用SQL Azure进行瞬态故障处理

时间:2012-09-05 13:20:51

标签: .net entity-framework enterprise-library

我目前在我的应用程序中使用SQL Azure和Entity SQL。

e.g。

Entities model = new Entities();
db_Item item = model.db_Item.First();

现在我想在企业库中使用瞬态故障处理,但是没有我能找到的示例或解决方案可以让我做一些事情,比如覆盖实体类,所以我不需要更新我的代码在数百个地方。

有人可以提供更多关于如何做到这一点的信息吗?

5 个答案:

答案 0 :(得分:18)

了解我到目前为止的情况。

  1. 实体框架不提供对连接打开的访问以及将SQL发送到服务器的部分,因此目前无法在该区域周围提供重试逻辑。

  2. EF团队意识到了这一不足,并计划将重试逻辑实际集成到EF中,以备可能的版本6.

  3. 根据[1]的案例#3,您可以在OnContextCreated上向数据库发送SQL命令。然而,这意味着对于您对数据库进行的每次单独数据库调用,您必须进行2.我不会在任何情况下推荐这种情况,除非您不关心性能。

  4. 到目前为止,唯一可行的选择是围绕您对数据库进行的每次调用,以企业库瞬态故障处理应用程序块[2]的形式实现重试逻辑。在现有的应用程序中,这非常繁琐。

  5. 当我有时间的时候,我正在寻找EF的源代码,看看是否还有其他事情可以做,而我们等待EF 6.我会关注[3]

    < / LI>
  6. 有些人希望,EF团队目前正在审核。 [4]

  7. 更新: 2013-11-14

    我想我会更新这篇文章,让每个人都知道EF6已经发布并支持开箱即用的连接弹性。 https://www.nuget.org/packages/EntityFramework/

    不再需要解决方法。

    更新:2013-03-23

    EF 6 Alpha 3发布时具有连接弹性 - http://entityframework.codeplex.com/wikipage?title=Connection%20Resiliency%20Spec

    更新: 2012-11-04

    EF团队已正式宣布计划用于EF 6. [4]

    [1] http://blogs.msdn.com/b/appfabriccat/archive/2010/12/11/sql-azure-and-entity-framework-connection-fault-handling.aspx

    [2] http://msdn.microsoft.com/en-us/library/hh680934(v=pandp.50).aspx

    [3] http://entityframework.codeplex.com/wikipage?title=Roadmap

    [4] http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2426525-automatically-perform-retry-logic-for-sql-azure

答案 1 :(得分:11)

值得庆幸的是,新的瞬态故障处理应用程序块非常简单。你需要的一切都可以在这里找到:

http://geekswithblogs.net/ScottKlein/archive/2012/01/27/understanding-sql-azure-throttling-and-implementing-retry-logic.aspx

以视频形式:

http://channel9.msdn.com/Shows/Cloud+Cover/Episode-68-Throttling-in-SQL-Azure-with-Scott-Klein

上述链接的一个例子:

using (NorthwindEntities dc = new NorthwindEntities())
{
    RetryPolicy myPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(3);
    Employee e1 = myPolicy.ExecuteAction<Employee>(() =>
        (from x in dc.Employees
            where x.LastName == "King"
            select x).First());
}

如您所见,您需要做的就是创建一个RetryPolicy并使用包含在操作中的查询调用其ExecuteAction函数。

*的 *修改

示例上下文覆盖:

private RetryPolicy m_RetryPolicy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(....

public override int SaveChanges()
{
    return m_RetryPolicy.ExecuteAction<int>(() =>
    {
        return base.SaveChanges();
    });
}

// Pass anonymous query func in here
public T AutoRetryQuery<T>(Func<T> query)
{
    return m_RetryPolicy.ExecuteAction<T>(query);
}

答案 2 :(得分:10)

由于这似乎是有关Azure瞬态处理的最热门问题之一,我将在此处添加此答案。

实体框架确实内置了弹性代码(根据Adam的回答)

BUT:

1)您必须手动添加代码才能激活它

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        this.SetExecutionStrategy(
            "System.Data.SqlClient",
            () => new SqlAzureExecutionStrategy());

        this.SetTransactionHandler(
            SqlProviderServices.ProviderInvariantName, 
            () => new CommitFailureHandler()); 
    }
...

第一个方法调用激活重试,第二个调用设置处理程序以避免重试时发生重复更新。

注意:将自动找到并实例化此类,如下所述:https://msdn.microsoft.com/en-us/library/jj680699(v=vs.113).aspx。只要确保该类与DbContext类在同一个程序集中,并且有一个没有参数的公共构造函数。

2)内置的SqlAzureExecutionStrategy 不够好。它不包括所有瞬态错误。当您考虑SQL Server团队独立于Entity Framework工作时,这并不奇怪,因此他们不太可能完全同步可能出现的瞬态错误。你自己也很难理解这一点。

我们使用的解决方案是在另一家软件公司的建议的支持下,创建了我们自己的执行策略,重试每个 SqlException和TimeoutException,除了一些我们白名单不值得重试的情况(如许可被拒绝)。

public class WhiteListSqlAzureExecutionStrategy : DbExecutionStrategy
{
    public WhiteListSqlAzureExecutionStrategy()
    {
    }

    protected override bool ShouldRetryOn(Exception exception)
    {  
        var sqlException = exception as SqlException;

        // If this is an SqlException then we want to always retry
        // Unless the all the exception types are in the white list. 
        // With those errors there is no point in retrying.
        if (sqlException != null)
        {
            var retry = false;
            foreach (SqlError err in sqlException.Errors)
            {
                // Exception white list.
                switch (err.Number)
                {
                    // Primary Key violation
                    // https://msdn.microsoft.com/en-us/library/ms151757(v=sql.100).aspx
                    case 2627:

                    // Constraint violation
                    case 547:

                    // Invalid column name, We have seen this happen when the Snapshot helper runs for a column 'CreatedOn'
                    // This is not one of our columns and it appears to be using our execution strategy.
                    // An invalid column is also something that probably doesn't get resolved by retries.
                    case 207:
                        break;

                    // The server principal "username" is not able to access the database "dbname" under the current security context
                    // May occur when using restricted user - Entity Framework wants to access master for something
                    // probably not transient
                    case 916:
                        break;

                    // XXX permission denied on object. (XXX = select, etc)
                    // Should not occur if db access is correct, but occurred when using restricted user - EF accessing __MigrationHistory
                    case 229:
                        break;

                    // Invalid object name 'xxx'.
                    // Occurs at startup because Entity Framework looks for EdmMetadata, an old table
                    // (Perhaps only if it can't access __MigrationHistory?)
                    case 208:
                        break;

                    default:
                        retry = true;
                        break;
                }
            }
            return retry;
        }

        if (exception is TimeoutException)
        {
            return true;
        }

        return false;
    }       
}

3)有一种错误,其中EF运行重试N ^ 2次而不是N,这使得延迟比你预期的要长得多。它应该需要大约26秒,但这个错误需要几分钟。但是,这并不是那么糟糕,因为实际上SQL Azure通常不可用超过一分钟:( https://entityframework.codeplex.com/workitem/2849

4)如果你还没有这样做,你真的需要在使用后处理你的DbContext。似乎这就是CommitFailureHandler运行它清除整理__TransactionHistory表的要点;如果你不处理,这个表将永远增长(虽然见下一点)。

5)您应该在启动时或后台线程中的某处调用ClearTransactionHistory,以清除__TransactionHistory中的任何剩余部分。

答案 3 :(得分:1)

我也有同样的问题,在我的情况下我使用LINQ-To-SQL(我相信同样适用于实体框架),这就是我打算做的。

使用repository pattern摘要Linq-to-sql查询并包装Linq-to-sql代码,该代码使用重试机制生成数据库请求。

为此,我建议创建一个名为ExecuteWithRetry(Action action)的方法,然后像这样ExecuteWithRetry(()=> { db_Item item = model.db_Item.First(); });

调用此方法

该方法可以这种方式实现

private void  ExecuteWithRetry(Action action)
{
  var retryStrategy = new Incremental(5, TimeSpan.FromSeconds(1), 
  TimeSpan.FromSeconds(2));

  // Define your retry policy using the retry strategy and the Windows Azure storage
  // transient fault detection strategy.
  var retryPolicy =
  new RetryPolicy<StorageTransientErrorDetectionStrategy>(retryStrategy);

  // Receive notifications about retries.
  retryPolicy.Retrying += (sender, args) =>
  {
    // Log details of the retry.
    var msg = String.Format("Retry - Count:{0}, Delay:{1}, Exception:{2}",
        args.CurrentRetryCount, args.Delay, args.LastException);
    Trace.WriteLine(msg, "Information");
  };

  try
  {
    this.retryPolicy.ExecuteAction(action);
  }
  catch (Exception ex)
  {
     Trace.TraceError(ex.TraceInformation());
    throw;
  }
 }

我也将尝试这种方法,希望它能够正常工作

答案 4 :(得分:0)

实际上你想使用MS提供的扩展方法。参考:http://msdn.microsoft.com/en-us/library/hh680906(v=pandp.50).aspx

“此外, SqlCommandExtensions SqlConnectionExtensions 类提供了一组扩展方法,使.NET开发人员能够打开SQL Azure数据库连接并调用SQL命令。如果您无法调整代码以利用ReliableSqlConnection类,则扩展方法很有用。例如,您可能正在使用内部使用SqlConnection实例的企业库数据访问应用程序块或实体框架。 In在这种情况下,扩展方法可以帮助您将瞬态故障处理应用程序块提供的重试功能添加到现有代码中,而无需重大工作。