c# - 如何正确使用 ExecutionStrategy?

标签 c# postgresql entity-framework entity-framework-core

我们正在使用 ExecutionStrategy 并在我们的数据库上下文中有这个辅助方法:

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);
        }
    }
}

我们在调用复杂/脆弱的业务逻辑时使用 ExecuteWithinTransactionAsync,这些业务逻辑应该在可序列化事务中可靠地执行。我们正在使用 Postgres,因此我们的事务可能会由于序列化问题而中止。执行策略检测到它并重试操作。这很好用。但是 EF 仍然保留上次执行的旧缓存。这就是我们引入 ClearChangeTracker 的原因,它看起来像这样:

private void ClearChangeTracker()
{
    ChangeTracker.DetectChanges();
    foreach (var entity in ChangeTracker.Entries().ToList())
    {
        entity.State = EntityState.Detached;
    }
}

这似乎工作正常,直到我们发现它不再工作的情况。当我们将新实体添加到导航属性列表时,这些实体不会在下次尝试时被删除。例如

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.SaveChangesAsync() 失败,并且整个操作重新运行,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 中的 Dockerized

最佳答案

在 ef-core 2.0.0 中,这个 new feature DbContext 引入了池化。为了使其正常工作,DbContext 实例现在能够重置其内部状态,因此它们可以作为"new"分发。可以像这样调用重置方法(在您的 DbContext 中):

((IDbContextPoolable)this).ResetState();

因此,如果您可以升级到 ef-core 2.0.0,那就去吧。不仅受益于这项新功能,它在许多方面也更加成熟。

免责声明:此方法仅供内部使用,因此 API 将来可能会发生变化。

关于c# - 如何正确使用 ExecutionStrategy?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46685823/

相关文章:

c# - 如何将 Webapi 项目(与 EF 和 LocalDb 结合使用)部署到 Azure

c# - 延迟过滤列表

c# - 在枚举中扩展功能

c# - 将 selectlistitems 绑定(bind)到 viewmodel 的正确方法

arrays - Hibernate SQLQuery,如何获取数组和行对象?

c# - 在 Asp.Net 方法中,如何使用 EF 从表中删除行?

c# - 用户单击后禁用按钮(在数据列表中)

comparison - 测试 Postgresql 数组字段是空还是空的正确方法是什么

postgresql - 如何获取一个月中的天数?

c# - Entity Framework - 索引 0 处的初始化字符串的格式