C# 如何正确使用执行策略?
我们正在使用ExecutionStrategy,并在db上下文中使用此helper方法:C# 如何正确使用执行策略?,c#,postgresql,entity-framework,entity-framework-core,C#,Postgresql,Entity Framework,Entity Framework Core,我们正在使用ExecutionStrategy,并在db上下文中使用此helper方法: public Task<T> ExecuteWithinTransactionAsync<T>(Func<IDbContextTransaction, Task<T>> operation, string operationInfo) { int counter = 0; return Database.CreateExecutionStrat
public Task<T> ExecuteWithinTransactionAsync<T>(Func<IDbContextTransaction, Task<T>> operation, string operationInfo)
{
int counter = 0;
return Database.CreateExecutionStrategy().ExecuteAsync(RunOperationWithinTransaction);
async Task<T> RunOperationWithinTransaction()
{
counter++;
if (counter > 1)
{
Logger.Log(LogLevel.Warn, $"Executing ({counter}. time) transaction for {operationInfo}.");
ClearChangeTracker();
}
using (var transaction = await Database.BeginTransactionAsync(IsolationLevel.Serializable))
{
return await operation.Invoke(transaction);
}
}
}
这似乎很有效,直到我们发现一个不再有效的案例。当我们向导航属性列表添加新实体时,这些实体不会在下次尝试时被删除。比如说
var parent = context.Parents.FirstOrDefault(p => p.Id == 1);
if (parent.Children.Any())
{
throw new Exception("Parent already has a child"); // This exception is thrown on the second try
}
parent.Children.Add(new Child());
context.SaveChangesAsync();
因此,如果最后一行context.saveChangesSync()
失败,并且整个操作重新运行,parent.Children
已经包含在parent.Children.Add(new child())中添加的新子级代码>我没有找到任何方法从EF中删除该项
但是,如果我们删除检查(if(parent.Children.Any())
),如果该项已经存在或不存在,并且只尝试第二次添加它,那么它在数据库中只存储一次
我试图找出如何正确地清除DbContext,但大多数时候,答案只是创建一个新的DbContext。但是,这不是一个选项,因为ExecutionStrategy需要DbContext。这就是为什么我想知道,使用ExecutionStrategy并在每次重试时使用干净的DbContext的建议方法是什么
进一步的技术细节
- EF核心版本:1.1.2
- 数据库提供程序:Npgsql.EntityFrameworkCore.PostgreSQL(1.1.1)
- 操作系统:Windows 10,在Linux中停靠
在ef core 2.0.0中,引入了这种
DbContext
池。为了使其正常工作,DbContext
实例现在可以重置其内部状态,因此可以将其作为“新”分发。可以这样调用reset方法(在DbContext
中):
所以,如果你能升级到ef core 2.0.0,那就去吧。不仅受益于这一新功能,它在许多方面都更加成熟
免责声明:此方法仅供内部使用,因此API将来可能会更改。thx最终成功更新到ef core 2(由于2.0.0中的错误,不得不等待2.0.1版本),是的,它似乎正在工作。对于尝试此操作的任何其他人,ResetState
实际上会处理上下文,因此您需要再次取消处理:((IDbContextPoolable)this.Resurrect(((IDbContextPoolable)this.SnapshotConfiguration())代码>
var parent = context.Parents.FirstOrDefault(p => p.Id == 1);
if (parent.Children.Any())
{
throw new Exception("Parent already has a child"); // This exception is thrown on the second try
}
parent.Children.Add(new Child());
context.SaveChangesAsync();
((IDbContextPoolable)this).ResetState();