与实体框架和瞬态故障处理块的连接?

时间:2013-01-17 20:56:17

标签: sql-server azure entity-framework-4 azure-sql-database

我们正在将SQL迁移到Azure。我们的DAL是基于实体框架4.x。我们希望使用Transient Fault Handling Block为SQL Azure添加重试逻辑。

总的来说,我们正在寻找最好的80/20规则(或者更多的是95/5,但你明白了) - 我们不打算花几周重构/重写代码(有很多它)。我很好地重新实现了我们的DAL框架,但不是所有编写和生成的代码都不再需要了,因为这已经只是针对少数情况。缓解>>>为我们消除这种边缘情况。

查看here at MSDN解释的可能选项,似乎案例#3 实施“最快”,但只是乍一看。稍微思考一下这个解决方案,让我感到震惊的是我们可能会遇到连接管理问题,因为这可以解决实体框架内置的管理连接的过程(即总是关闭它们)。在我看来,“解决方案”是确保我们实例化的100%的上下文使用块,但是使用我们的架构,这将是困难的。

所以我的问题是:从那个链接开始使用案例#3,是否存在连接问题,或者某些地方正在发生一些我不知道的魔法?

2 个答案:

答案 0 :(得分:1)

我已经做了一些实验,结果证明这让我们回到过去习惯的旧“管理连接”状态,只是这次连接被抽象了我们一点而且我们必须现在“同样管理上下文”。

假设我们有以下OnContextCreated实现:

private void OnContextCreated()
{
    const int maxRetries = 4;
    const int initialDelayInMilliseconds = 100;
    const int maxDelayInMilliseconds = 5000;
    const int deltaBackoffInMilliseconds = initialDelayInMilliseconds;

    var policy = new RetryPolicy<SqlAzureTransientErrorDetectionStrategy>(maxRetries,
                                                                            TimeSpan.FromMilliseconds(initialDelayInMilliseconds),
                                                                            TimeSpan.FromMilliseconds(maxDelayInMilliseconds),
                                                                            TimeSpan.FromMilliseconds(deltaBackoffInMilliseconds));
    policy.ExecuteAction(() =>
            {
                try
                {
                    Connection.Open();
                    var storeConnection = (SqlConnection) ((EntityConnection) Connection).StoreConnection;
                    new SqlCommand("declare @i int", storeConnection).ExecuteNonQuery();
                    //Connection.Close();
                    // throw new ApplicationException("Test only");
                }
                catch (Exception e)
                {
                    Connection.Close();

                    Trace.TraceWarning("Attempted to open connection but failed: " + e.Message);

                    throw;
                }
            }
        );
}

在这种情况下,我们强行打开Connection(这是此处的目标)。因此,Context在许多调用中保持开放。因此,我们必须告诉Context何时关闭连接。我们这样做的主要机制是在Context上调用Dispose方法。因此,如果我们只是允许垃圾收集来清理我们的上下文,那么我们允许连接保持悬空。

我通过切换Connection.Close()块中try的注释并对我们的数据库运行一系列单元测试来测试它。在不调用Close的情况下,我们跳到了~275-300个活动连接(从SQL Server的角度来看)。通过致电Close,这个数字徘徊在~12。然后我用少量单元测试再现了有和没有using块的上下文并重现相同的结果(不同的数字 - 我忘了他们是什么)。

我使用以下查询来计算我的连接数:

SELECT s.session_id, s.login_name, e.connection_id,
      s.last_request_end_time, s.cpu_time, 
      e.connect_time
FROM sys.dm_exec_sessions AS s
INNER JOIN sys.dm_exec_connections AS e
ON s.session_id = e.session_id
WHERE login_name='myuser'
ORDER BY s.login_name

结论:如果您使用此解决方法调用Connection.Open()来启用瞬态错误处理块,那么您必须使用using块来处理与您合作的所有上下文,否则你会遇到问题(使用SQL Azure会导致数据库被“限制”并最终脱机几个小时!)。

答案 1 :(得分:1)

这种方法的问题在于它只处理连接重试而不是命令重试。

如果您使用Entity Framework 6(目前处于alpha版本),那么对Azure SQL数据库的瞬态重试有一些新的内置支持(稍加配置):http://entityframework.codeplex.com/wikipage?title=Connection%20Resiliency%20Spec

我创建了一个库,允许您配置实体框架以使用故障处理块重试,而无需更改每个数据库调用 - 通常您只需要更改配置文件,可能需要更改一行或两行代码。

这允许您将它用于Entity Framework或Linq To Sql。

https://github.com/robdmoore/ReliableDbProvider