You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity Framework更新丢失求助:插入正常但实体更新偶发失效

解决方案:Entity Framework事务内更新丢失问题

1. 修复代码中的语法错误

你提供的代码存在致命语法问题:new Context().Database.ExecuteSqlCommand(...)返回的是受影响行数(int类型),不能赋值给db变量用于后续的实体操作。正确的写法应该先创建DbContext实例,再执行SQL命令:

using (var scope = new TransactionScope())
{
    using (var db = new Context())
    {
        // 正确执行隔离级别设置
        db.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

        var student = db.Students.SingleOrDefault(x => x.ID == 1);
        if (student == null) return;

        var schoolName = "TestSchool";
        db.Schools.Add(new School { Name = schoolName });
        student.School = schoolName;

        db.SaveChanges(); // 替换错误的db.Save()
    }
    scope.Complete();
}

2. 统一事务隔离级别管理,避免手动修改

手动执行SET TRANSACTION ISOLATION LEVEL会脱离TransactionScope的事务管理,导致隔离级别冲突(TransactionScope默认隔离级别为Serializable)。应通过TransactionOptions指定隔离级别,确保事务行为一致:

var transactionOptions = new TransactionOptions
{
    IsolationLevel = IsolationLevel.ReadCommitted, // 推荐使用ReadCommitted,避免脏读
    Timeout = TransactionManager.DefaultTimeout
};

using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
    using (var db = new Context())
    {
        var student = db.Students.SingleOrDefault(x => x.ID == 1);
        if (student == null) return;

        var schoolName = "TestSchool";
        db.Schools.Add(new School { Name = schoolName });
        student.School = schoolName;

        db.SaveChanges();
    }
    scope.Complete();
}

3. 引入乐观并发控制解决高并发更新丢失

高并发场景下,多个事务同时修改同一Student实体时,会出现更新覆盖问题。给Student实体添加并发校验属性,EF会自动检测数据是否被其他事务修改:

public class Student
{
    public int ID { get; set; }
    public string School { get; set; }
    // 添加时间戳属性用于并发校验
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

然后在保存时处理并发冲突,重试更新操作:

bool saveFailed;
do
{
    saveFailed = false;
    using (var scope = new TransactionScope())
    using (var db = new Context())
    {
        var student = db.Students.SingleOrDefault(x => x.ID == 1);
        if (student == null) break;

        var schoolName = "TestSchool";
        var newSchool = new School { Name = schoolName };
        db.Schools.Add(newSchool);

        student.School = schoolName;

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            saveFailed = true;
            // 重新加载最新的实体数据,准备重试
            ex.Entries.Single().Reload();
        }
    }
} while (saveFailed);

4. 优化实体关联逻辑(若为外键关联场景)

如果Student.School是外键关联属性,直接赋值名称存在可靠性问题(比如School名称重复)。建议通过School的ID关联:

using (var scope = new TransactionScope())
using (var db = new Context())
{
    var student = db.Students.SingleOrDefault(x => x.ID == 1);
    if (student == null) return;

    var schoolName = "TestSchool";
    var newSchool = new School { Name = schoolName };
    db.Schools.Add(newSchool);
    // 先保存School获取自动生成的ID
    db.SaveChanges();

    // 关联外键ID而非名称
    student.SchoolId = newSchool.ID;
    db.SaveChanges();
}
scope.Complete();

5. 避免使用Read Uncommitted隔离级别

Read Uncommitted会允许读取未提交的脏数据,在高并发场景下极易导致读取到过时的Student数据,修改后覆盖其他事务的合法变更。优先使用ReadCommitted或RepeatableRead隔离级别。

内容的提问来源于stack exchange,提问作者dotNaki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 03:41:29