【问题标题】:My custom DbExecutionStrategy is not being called我的自定义 DbExecutionStrategy 没有被调用
【发布时间】:2018-04-10 11:54:55
【问题描述】:

我最初的问题是我在更新我的 SQL 数据库时经常遇到死锁。通过一些研究,我发现我能够定义一个自定义 DbConfiguration 和一个 DbExecutionStrategy,它指示实体框架在 x 毫秒和 y 次后出现某些错误后自动重试。太好了!

因此,按照https://msdn.microsoft.com/en-us/data/jj680699 的指南,我构建了正在使用的自定义 DbConfiguration,但相关的 DbExecutionStrategy 似乎被忽略了。

最初,我的整个 DbConfiguration 都被忽略了,但我发现这是因为我在 app.config 中引用了它,并使用 DbConfigurationType 属性 [DbConfigurationType(typeof(MyConfiguration))] 装饰了我的实体构造函数。现在我只使用了 app.config,至少我的自定义配置正在被调用。

在最简单的形式中,我的自定义配置如下所示:

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        System.Windows.MessageBox.Show("Hey! Here I am!"); //I threw this in just to check that I was calling the constructor. Simple breakpoints don't seem to work here.
        SetExecutionStrategy("System.Data.SqlClient", () => new MyExecutionStrategy(3, TimeSpan.FromMilliseconds(500)));
    }
}

我的自定义 DbConfiguration 在我的 app.config 中被引用,如下所示:

<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
    ...
</entityFramework>

我的自定义 DbExecutionStrategy 是这样构建的:

private class MyExecutionStrategy : DbExecutionStrategy
{
    public MyExecutionStrategy() : this(3, TimeSpan.FromSeconds(2))
    {
        System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through default constructor.");
    }

    public MyExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, maxDelay)
    {
        System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through parametered constructor.");
    }

    protected override bool ShouldRetryOn(Exception ex)
    {
        System.Windows.MessageBox.Show($"Overriding ShouldRetryOn.");

        bool retry = false;

        SqlException sqlException = GetSqlException(ex);

        if (sqlException != null)
        {
            int[] errorsToRetry =
            {
                1205,  //Deadlock
                -2     //Timeout
            };

            if (sqlException.Errors.Cast<SqlError>().Any(x => errorsToRetry.Contains(x.Number)))
            {
                retry = true;
            }
        }

        if (ex is TimeoutException)
        {
            retry = true;
        }

        return retry;
    }
}

我在这段特定的代码中根本没有遇到任何问题。

可能需要注意的一点是,到目前为止我看到的每个示例(例如 http://blog.analystcircle.com/2015/08/01/connection-resiliency-in-entity-framework-6-0-and-above/)都使用将 ShouldRetryOn 中的异常直接转换为 SqlException

SqlException sqlException = ex as SqlException;

我发现使用这种方法总是会导致 SqlException 为空,因为我的程序抛出了无法转换为 SqlException 的 EntityException。我的底层SqlException其实就是EntityException内部异常的内部异常。所以,我整理了一个简短的递归调用来挖掘并找到它。

private SqlException GetSqlException(Exception ex)
{
    SqlException result = ex as SqlException;

    if (result == null && ex.InnerException != null)
        result = GetSqlException(ex.InnerException);

    return result;
}

这可以正常工作,但是当我发现的示例不存在时,我需要这样做,这可能是出问题的线索。 EntityExceptions 不会触发 DbExecutionStrategy 吗?如果不是,为什么这被列为与 EF 6 一起使用的解决方案?任何见解将不胜感激。

编辑: 在深入研究 DbExecutionStrategy (https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Infrastructure/DbExecutionStrategy.cs) 的源代码后,我发现从 EntityException 中找到我的 SqlException 的递归函数是不必要的。 DbExecutionStrategy 有一个函数 UnwrapAndHandleException,它就是这样做的,并将 SqlException 传递给 ShouldRetryOn。所以,看来我又回到了第一格。

编辑 2: 不是一个真正的解决方案,因为它没有解释为什么我的 DbExecutionStrategy 没有按应有的方式被调用,但我发现如果我显式调用执行策略,它就可以工作。

明确使用执行策略的代码是:

var executionStrategy = new MyConfiguration.MyExecutionStrategy();

executionStrategy.Execute(
    () =>
    {
        //build your context and execute db functions here
        using (var context = new Entities())
        {
            ...do stuff
        }
    });

【问题讨论】:

  • 我也有不调用 ShouldRetryOn 的情况,尽管一切都表明它应该被调用 - 但我也无法确定它发生的原因。
  • 自从升级到 EF 6.2.0 后,我遇到了同样的问题。在 EF 6.1.3 和之前完美运行。
  • 我也看到了...
  • 检查是否应用了执行策略 - typeof(MyExecutionStrategy) == DbProviderServices.GetExecutionStrategy(connection).GetType()

标签: c# sql-server entity-framework-6 database-deadlocks


【解决方案1】:

现在可能太旧了,但万一有人遇到同样的问题:

  • exception.GetBaseException() 为您提供任何异常的根本原因。无需递归

  • 我可以使用 EF 6.4.0 实现此功能

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多