如何在EF Core中放弃对上下文的更改

时间:2017-03-19 09:36:57

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

我有一个很大的"扁平的" 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块中?我希望在失败的交易中发生的一切都留在那里而不是泄漏。

1 个答案:

答案 0 :(得分:5)

以下代码对我有用。但是,如果有人发布了一个更清洁的解决方案(我仍然期望有一些开箱即用的EF),我会接受它。

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