问题描述
我最初的问题是我在更新我的 SQL 数据库时经常遇到死锁.通过一些研究,我发现我能够定义一个自定义 DbConfiguration 和一个 DbExecutionStrategy,它指示实体框架在 x 毫秒和 y 次后出现某些错误后自动重试.太好了!
My original problem was that I was experiencing deadlocks often when updating my SQL database. Through a little bit of research, I found that I'm able to define a custom DbConfiguration and with it a DbExecutionStrategy which instructs Entity Framework to automatically retry after getting certain errors after x milliseconds and y number of times. Great!
因此,请遵循 https://msdn.microsoft.com/en- 上的指南us/data/jj680699,我构建了我的自定义 DbConfiguration,正在使用,但关联的 DbExecutionStrategy 似乎被忽略了.
So, following the guide at https://msdn.microsoft.com/en-us/data/jj680699, I build my custom DbConfiguration, which is being used, but the associated DbExecutionStrategy seems to be ignored.
最初,我的整个 DbConfiguration 都被忽略了,但我发现这是因为我在 app.config 中引用了它,并使用 DbConfigurationType 属性 [DbConfigurationType(typeof(MyConfiguration))] 装饰了我的实体构造函数.现在我只使用了 app.config,至少我的自定义配置正在被调用.
Originally, my entire DbConfiguration was being ignored, but I found that was because I was referencing it in my app.config as well as decorating my entity constructor with the DbConfigurationType attribute [DbConfigurationType(typeof(MyConfiguration))]. Now that I'm only using the app.config, at least my custom configuration is being called.
在最简单的形式中,我的自定义配置如下所示:
In its simplest form, my custom config looks like this:
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 中被引用,如下所示:
My custom DbConfiguration is referenced in my app.config like so:
<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
...
</entityFramework>
我的自定义 DbExecutionStrategy 是这样构建的:
My custom DbExecutionStrategy is built like so:
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;
}
}
我在这段特定的代码中根本没有遇到任何问题.
I'm not hitting anything at all in this particular piece of the code.
可能需要注意的一点是,到目前为止我看到的每个示例(例如 http://blog.analystcircle.com/2015/08/01/connection-resiliency-in-entity-framework-6-0-and-above/) 已使用
One thing that may be of note, is that every example I've seen so far (for example http://blog.analystcircle.com/2015/08/01/connection-resiliency-in-entity-framework-6-0-and-above/) has casted the exception in ShouldRetryOn directly to a SqlException using
SqlException sqlException = ex as SqlException;
我发现使用这种方法总是会导致 null SqlException,因为我的程序抛出了一个无法转换为 SqlException 的 EntityException.我的底层SqlException其实就是EntityException内部异常的内部异常.所以,我整理了一个简短的递归调用来挖掘并找到它.
I found that using this method always resulted in a null SqlException because my program is throwing an EntityException which can't be cast into a SqlException. My underlying SqlException is actually the inner exception of the inner exception of the EntityException. So, I put together a short recursive call to dig in and find it.
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 一起使用的解决方案?任何见解将不胜感激.
This works properly, but the fact that I need to do it when the examples I've found don't is probably a clue as to what's going wrong. Do EntityExceptions not trigger a DbExecutionStrategy? If not, why is this listed as a solution to be used with EF 6? Any insight would be much appreciated.
进一步挖掘 DbExecutionStrategy 的源代码 (https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Infrastructure/DbExecutionStrategy.cs),我发现从EntityException中找到我的SqlException的递归函数是不必要的.DbExecutionStrategy 有一个函数 UnwrapAndHandleException,它就是这样做的,并将 SqlException 传递给 ShouldRetryOn.所以,看来我又回到了第一格.
Doing some more digging into the source for DbExecutionStrategy (https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Infrastructure/DbExecutionStrategy.cs), I've found that my recursive function to find my SqlException from the EntityException is unnecessary. DbExecutionStrategy has a function UnwrapAndHandleException which does just that and passes the SqlException on to ShouldRetryOn. So, it seems I'm right back at square one.
编辑 2:不是真正的解决方案,因为它没有解释为什么我的 DbExecutionStrategy 没有按应有的方式被调用,但我发现如果我明确调用执行策略,它就可以工作.
EDIT 2: Not really a solution, because it doesn't explain why my DbExecutionStrategy isn't being called as it should, but I have found that if I explicitly call the execution strategy, it works.
明确使用执行策略的代码是:
The code to use the execution strategy explicitly is:
var executionStrategy = new MyConfiguration.MyExecutionStrategy();
executionStrategy.Execute(
() =>
{
//build your context and execute db functions here
using (var context = new Entities())
{
...do stuff
}
});
推荐答案
现在可能太老了,但万一有人遇到同样的问题:
Probably way too old by now but in case anyone is having the same issues:
exception.GetBaseException() 让您了解任何异常的根本原因.无需递归
exception.GetBaseException() gets you the root cause of any exception. No need for recursion
我可以使用 EF 6.4.0 实现此功能
I'm able to get this to work using EF 6.4.0
这篇关于我的自定义 DbExecutionStrategy 没有被调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!