EF 6中如何仅禁用单个实体的插入与更新操作
解决方案:禁用单个实体的EF插入/更新并同步实体值
一、核心问题解决:同步手动更新后的实体值
你当前的问题是手动通过ADO.NET更新数据库后,EF的本地实体实例未同步新值。这是因为你仅将实体状态设为Unchanged,但未将数据库返回的新值同步到实体对象中。可以通过以下两种方式处理:
1. 手动同步变更值(推荐,性能更高)
在执行ADO.NET的插入/更新操作后,将数据库返回的新值(比如自增主键、数据库计算字段)直接赋值给实体对应的属性:
private void SaveEmployeeEntitiesIfAny() { var employeeEntries = this.ChangeTracker .Entries<EmployeeEntity>() .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added) .ToList(); // 转为List避免多次枚举 foreach (var entry in employeeEntries) { var employee = entry.Entity; if (entry.State == EntityState.Added) { // 执行ADO插入并获取数据库生成的主键 int newId = InsertEmployeeViaAdoNet(employee); // 同步主键到实体 employee.Id = newId; // 同步其他数据库生成字段(如创建时间) employee.CreateTime = DateTime.Now; // 或从数据库返回值获取 } else if (entry.State == EntityState.Modified) { // 执行ADO更新并获取数据库更新后的字段(如最后修改时间) DateTime updatedTime = UpdateEmployeeViaAdoNet(employee); // 同步更新后的字段到实体 employee.LastUpdateTime = updatedTime; // 同步所有修改属性的原值与当前值,避免EF后续检测到变更 foreach (var prop in entry.OriginalValues.PropertyNames) { if (entry.Property(prop).IsModified) { entry.Property(prop).OriginalValue = entry.Property(prop).CurrentValue; } } } // 重置状态为Unchanged,避免EF自动处理 entry.State = EntityState.Unchanged; } } // ADO插入示例方法 private int InsertEmployeeViaAdoNet(EmployeeEntity employee) { using (var conn = new SqlConnection(this.Database.Connection.ConnectionString)) { conn.Open(); var cmd = new SqlCommand( "INSERT INTO Employees (Name, Age) OUTPUT INSERTED.Id VALUES (@Name, @Age)", conn); cmd.Parameters.AddWithValue("@Name", employee.Name); cmd.Parameters.AddWithValue("@Age", employee.Age); return (int)cmd.ExecuteScalar(); } } // ADO更新示例方法 private DateTime UpdateEmployeeViaAdoNet(EmployeeEntity employee) { using (var conn = new SqlConnection(this.Database.Connection.ConnectionString)) { conn.Open(); var cmd = new SqlCommand( "UPDATE Employees SET Name = @Name, Age = @Age, LastUpdateTime = GETDATE() OUTPUT INSERTED.LastUpdateTime WHERE Id = @Id", conn); cmd.Parameters.AddWithValue("@Id", employee.Id); cmd.Parameters.AddWithValue("@Name", employee.Name); cmd.Parameters.AddWithValue("@Age", employee.Age); return (DateTime)cmd.ExecuteScalar(); } }
2. 重新加载实体(简单但性能稍低)
如果不想手动同步字段,可以在执行ADO操作后调用entry.Reload(),让EF从数据库重新获取最新值:
private void SaveEmployeeEntitiesIfAny() { var employeeEntries = this.ChangeTracker .Entries<EmployeeEntity>() .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added) .ToList(); foreach (var entry in employeeEntries) { if (entry.State == EntityState.Added) { InsertEmployeeViaAdoNet(entry.Entity); } else { UpdateEmployeeViaAdoNet(entry.Entity); } // 重新加载实体,同步数据库最新值 entry.Reload(); // Reload后状态自动变为Unchanged,无需手动设置 } }
二、更优雅地禁用EF对该实体的插入/更新
在SaveChanges中对EmployeeEntity的新增/修改状态做特殊处理,确保EF不会自动生成对应的SQL:
public override int SaveChanges() { // 先手动处理Employee的增改操作 SaveEmployeeEntitiesIfAny(); // 调用父类方法保存其他实体的变更 return base.SaveChanges(); } private void SaveEmployeeEntitiesIfAny() { var employeeEntries = this.ChangeTracker .Entries<EmployeeEntity>() .Where(x => x.State == EntityState.Modified || x.State == EntityState.Added) .ToList(); foreach (var entry in employeeEntries) { // 执行ADO操作并同步值(参考上述方法) // ... // 设置状态为Unchanged,EF会忽略这些实体的变更 entry.State = EntityState.Unchanged; } }
三、验证删除和查询功能
- 删除操作:EF会正常生成DELETE语句,因为你未修改
EntityState.Deleted状态的实体; - 查询操作:完全不受影响,EF的查询逻辑与
SaveChanges的重写无关。
内容的提问来源于stack exchange,提问作者Hem
相关产品推荐
相关产品推荐

