【问题标题】:How to discard changes to context in EF Core如何在 EF Core 中放弃对上下文的更改
【发布时间】:2017-08-10 15:13:30
【问题描述】:

我有大量 json 格式的“扁平化”对象列表,以及一个有点复杂的关系数据库模式(大约 20 个表对应于扁平化对象)。我正在尝试在我的新关系数据库中自动插入这些扁平对象:

foreach (var flattenedObject in flattenedObjects)
{
    _repository.Insert(flattenedObject).Wait();
    //some time logging, etc
}

Insert() 方法针对不同表中的多个相关对象调用AddRangeAsync()AddAsync()

由于展平对象是遗留的,我想说其中大约 0.001% 的对象格式不正确并且会违反数据库约束 - 例如尝试在其中一个表中插入重复的复合主键。

我预计会出现这些罕见的错误,因此我的想法是 - 将整个 Insert() 操作包装在一个事务中 - 如果任何部分操作无效,请不要插入任何内容并记录错误,这样我就可以修改在重试之前手动展平对象。因此我的代码看起来有点类似于:

public async Task Insert(FlattenedObject fo)
{
    using (var transaction = _context.Database.BeginTransaction())
    {
        try
        {
            //magical code that calls AddAsync for multiple tables
        }
        catch (Exception ex)
        {
            transaction.Rollback()
            //logging
        }
    }
}

但是,如果在我的 try 块中某处发生错误(我尝试插入违反复合主键的对象),我的整个上下文对象就会损坏。

导致异常的对象仍保留在我的 DbContext 中,并且在不同事务中对 AddAsync() 的任何后续调用都会触发新异常。

我尝试为上面foreach 循环中的每个新对象重新创建我的 DbContext 和 repo - 但即便如此,如果我查询:

_context.ChangeTracker.Entries().Where(e => e.State != EntityState.Unchanged);

我看到我的旧对象仍在 dbContext 的新实例中。

是否有任何(优雅的)方法可以告诉我的上下文重置所有待处理的更改 - 这样我就可以在发生错误时将其放入 catch 块中?我希望我失败的交易中发生的所有事情都留在那里而不是泄漏。

【问题讨论】:

    标签: c# .net entity-framework entity-framework-core


    【解决方案1】:

    下面的代码对我有用。但是,如果有人发布了更简洁的解决方案(我仍然希望 EF 有现成的东西),我会接受。

    private void ResetContextState() => _context.ChangeTracker.Entries()
        .Where(e => e.Entity != null).ToList()
        .ForEach(e => e.State = EntityState.Detached);
    

    【讨论】:

    • 在分离之前是否应该在 .where(e => e.Entity!=null && e.state == EntityState.Added) 中包含以下条件
    • 这对于我当前插入导致问题的场景来说是正确的。但是,有人可能会在更新时遇到类似问题,因此如果其他人遇到此类问题,我将保留此答案,因为它是用于复制+粘贴。
    • 将上下文中存在的每个实体与 context.Reload() 分离,下次使用上下文时。这将有一个巨大的数据库命中。因为它与再次创建上下文对象相同
    • context.Reload() 正是我正在寻找的这个问题。虽然,它似乎不是 Core 标准 EF 包的选项。
    • 在 EF Core 3.1 上,我本身找不到重新加载选项;但是,在使用上面的代码分离 ChangeTracker 中的所有条目之后,我可以调用 context.SaveChanges() 来有效地重置它(解决并丢弃所有那些分离的条目,使更改计数为零,并且有效保存零东西,因此对数据库没有影响)。然后继续使用上下文。
    【解决方案2】:

    解决办法:

    多年后,也许你们中的一些人仍在寻找,并且奇迹般地您正在使用 EF Core 5.0。有一个清除 ChangeTracker 的新功能:

    dbContext.ChangeTracker.Clear();
    

    只要更新失败就调用它。
    但不要忘记这根本不是最佳实践。Microsoft 建议为每个请求创建新的 dbContext,因为它旨在有那么短的寿命。
    更多信息在这里:Microsoft EF Core 5.0: ChangeTracker.Clear Method

    更好的解决方案

    但是稍微复杂一点,就是使用DbContextFactory。这在应用程序代码需要手动创建和释放上下文实例时很有用。

    更多信息在这里:Microsoft EF Core 5.0 : DbContextFactory
    享受

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 2022-06-28
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      相关资源
      最近更新 更多