17

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

例如

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

现在我想使用企业库之外的瞬态故障处理,但是我找不到可以让我执行诸如覆盖实体类之类的示例或解决方案,因此我不必更新数百个代码的地方。

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

4

5 回答 5

19

经历我到目前为止所拥有的。

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

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

  3. 根据 [1] 的案例 #3,您可以将 SQL 命令发送到 OnContextCreated 上的数据库。然而,这意味着对于您对数据库进行的每一次数据库调用,您都必须进行 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=路线图

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

于 2012-10-05T02:58:25.377 回答
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);
}
于 2012-10-27T00:41:25.063 回答
10

由于这似乎是关于 Azure 瞬态处理的 SO 上最受欢迎的问题之一,因此我将在此处添加此答案。

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

但:

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 团队独立于实体框架工作时,这并不奇怪,因此他们不太可能完全同步可能出现的瞬时错误。自己也很难弄清楚。

在另一家软件公司的建议的支持下,我们使用的解决方案是创建我们自己的执行策略,它会重试每个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) There used to be a kind of bug where EF would run the retries N^2 times instead of N, which made for much longer delays than you'd expect. (It's supposed to take up to about 26 seconds, but the bug made it take minutes.) However, this isn't so bad because in reality SQL Azure regularly has unavailability for more than a minute :( https://entityframework.codeplex.com/workitem/2849

4) If you haven't been doing so already, you really need to dispose of your DbContext after it's used. It seems this is the point that the CommitFailureHandler runs it's purging to tidy up the __TransactionHistory table; if you don't dispose, this table will grow forever (although see next point).

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

于 2015-11-17T02:14:24.433 回答
1

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

使用存储库模式抽象 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;
  }
 }

我也将尝试这种方法,希望它会奏效

于 2012-10-04T08:17:10.367 回答
0

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

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

"

于 2012-09-05T21:38:33.487 回答