我的自定义DbExecutionStrategy没有被调用

Tro*_*ier 5 c# sql-server database-deadlocks entity-framework-6

我最初的问题是更新SQL数据库时经常遇到死锁。通过一些研究,我发现我可以定义一个自定义DbConfiguration并使用它来定义一个DbExecutionStrategy,它指示Entity Framework在x毫秒和y次重复出现某些错误后自动重试。大!

因此,按照https://msdn.microsoft.com/zh-cn/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)));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
    ...
</entityFramework>
Run Code Online (Sandbox Code Playgroud)

我的自定义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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在这段特定代码中,我什么都没有打。

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

SqlException sqlException = ex as SqlException;
Run Code Online (Sandbox Code Playgroud)

我发现使用此方法始终会导致SqlException为空,因为我的程序抛出了一个EntityException,而该EntityException无法转换为SqlException。我的基础SqlException实际上是EntityException内部异常的内部异常。因此,我整理了一个简短的递归调用以进行挖掘并找到它。

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

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

    return result;
}
Run Code Online (Sandbox Code Playgroud)

这可以正常工作,但是当我发现的示例不存在时,我需要这样做的事实可能是出了什么问题的线索。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
        }
    });
Run Code Online (Sandbox Code Playgroud)

Tun*_*dey 2

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

  • exception.GetBaseException() 可以帮助您找到任何异常的根本原因。不需要递归

  • 我可以使用 EF 6.4.0 让它工作