【问题标题】:Connections with Entity Framework and Transient Fault Handling Block?与实体框架和瞬态故障处理块的连接?
【发布时间】:2013-01-17 20:56:16
【问题描述】:

我们正在将 SQL 迁移到 Azure。我们的 DAL 是基于 Entity Framework 4.x 的。我们希望使用瞬态故障处理块为 SQL Azure 添加重试逻辑。

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

查看here at MSDN 解释的可能选项,似乎案例#3有“最快”的实施方式,但只是乍一看。稍微考虑一下这个解决方案后,我突然想到我们可能在连接管理方面遇到问题,因为这个规避了实体框架用于管理连接的内置流程(即总是关闭它们)。在我看来,“解决方案”是确保我们实例化的 Context 100% 使用 Using 块,但对于我们的架构,这将是困难的。

所以我的问题是:使用该链接中的案例 #3,是挂起连接有问题,还是在某个我不知道的地方发生了什么神奇的事情?

【问题讨论】:

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


    【解决方案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;
                    }
                }
            );
    }
    

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

    我通过在try 块中切换Connection.Close() 上的cmets 并针对我们的数据库运行一堆单元测试来对此进行测试。在不调用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 会导致您的数据库被“限制”并最终离线数小时!)。

    【讨论】:

      【解决方案2】:

      这种方法的问题是它只负责连接重试而不是命令重试。

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

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

      这允许您将它用于实体框架或 Linq To Sql。

      https://github.com/robdmoore/ReliableDbProvider

      【讨论】:

        猜你喜欢
        • 2016-07-26
        • 1970-01-01
        • 2015-03-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多