我最初的问题是我在更新SQL数据库时经常遇到死锁。通过一些研究,我发现我能够定义一个自定义DbConfiguration并使用它来指示实体框架在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)));
}
}
我的app.config中引用了我的自定义DbConfiguration,如下所示:
<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;
我发现使用此方法总是导致null 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
}
});
答案 0 :(得分:0)
现在可能已经太老了,但是如果有人遇到相同的问题:
exception.GetBaseException()获取任何异常的根本原因。无需递归
我可以使用EF 6.4.0使其正常工作