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

Ada*_*ley 16 .net entity-framework enterprise-library

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

例如

Entities model = new Entities();
db_Item item = model.db_Item.First();
Run Code Online (Sandbox Code Playgroud)

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

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

Ada*_*ley 18

了解到目前为止我所拥有的.

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

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

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

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

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

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

更新: 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


Jos*_*ith 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());
}
Run Code Online (Sandbox Code Playgroud)

如您所见,您需要做的就是创建一个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);
}
Run Code Online (Sandbox Code Playgroud)

  • 如果应用于现有项目,则需要大量代码重写,这是需要在EF中的实际连接开放点应用重试的地方.虽然这个例子是+1. (5认同)

O'R*_*ney 11

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

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

但:

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

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

        this.SetTransactionHandler(
            SqlProviderServices.ProviderInvariantName, 
            () => new CommitFailureHandler()); 
    }
...
Run Code Online (Sandbox Code Playgroud)

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

注意:此类将自动找到并实例化,如下所述:https://msdn.microsoft.com/en-us/library/jj680699(v = vs133).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;
    }       
}
Run Code Online (Sandbox Code Playgroud)

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

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

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