Entity Framework 5的SaveChanges()方法未能回滚尝试的事务

3
我有以下代码(EntityContext.Current 是一个 DbContext):
try {
    Setting s = new Setting
    {
        id = 1,
        user_id = 0
    };

    EntityContext.Current.Settings.Add(s);
    EntityContext.Current.SaveChanges(); //this violates a foreign key constraint and therefore throws an exception
} catch (Exception ex) {
    ErrorContext.Log(ex);
}

根据此回答,我期望未提交的更改在失败时会被撤回。然而,在我的错误日志记录方法中,我看到它们没有被撤回。

Error e = new Error
{
    message = ex.Message
};

EntityContext.Current.Errors.Add(e);

var pending = ((IObjectContextAdapter)EntityContext.Current).ObjectContext.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added);
//this contains both my new error entity and my old setting entity which shouldn't be here

EntityContext.Current.SaveChanges();

当我的应用程序尝试在数据库中记录错误时,它还会尝试(再次)保存具有无效外键约束的设置,从而导致添加错误日志记录失败。 这是我第一次遇到EF的这种行为。我尝试将第一个SaveChanges()包装在事务中,但仍然出现了同样的问题。 根据这个答案,我的连接字符串也不包含Enlist=false。我很困惑。这是预期的行为吗?我该如何防止这种情况发生?
1个回答

3
这是预期行为。 SaveChanges 尝试在单个数据库事务中提交所有更改。如果失败,则回滚数据库事务并且不会写入到数据库中。
但是失败的事务不会改变上下文中实体的状态。这就是为什么您的 Setting 实体仍处于 Added 状态的原因。
将您的 Error 实体附加到同一上下文是有问题的,因为您通常无法将其存储到数据库中,就像在此示例中一样。我建议始终在新上下文中编写错误日志,并使用专用方法打开新上下文,向该上下文添加 Error 实体并保存更改。然后立即处理。

我明白了,非常有趣。我想我也可以在catch块中简单地删除该实体?这样当我尝试编写错误(使用相同的上下文)时,它将不再尝试写入该设置了? - Mansfield
1
@Mansfield:是的,您可以分离实体。对于这个简单的示例来说很容易。但是想象一下,如果您有一个更复杂的场景,并且上下文中包含了许多处于Added、Deleted、Modified状态的实体,其中一个实体导致了FK约束违规。在大多数情况下,在catch块中找到有问题的实体并从错误中恢复将会很困难。 - Slauma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接