阻止entity framework为导航属性插入值

我正在使用Entity Framework 4.0开发WPF应用程序。 当我尝试保存对象时,我得到了一个主键exception,但主键是一个AutoIncremented字段,我无法理解exception的原因。

所以在尝试了这个和那个,以及一点调试和使用SQL分析器之后,我发现在插入我的对象之前,必须在父表中插入一条记录,因为我设置了该对象的导航属性。

因此,关键是如果尝试插入Employee对象并将其部门设置为Employee.Department = deptObject,则将新记录设置为插入部门对象。

请告诉我某些导航属性对象不会插入数据库,任何属性或任何方法,Anything。

谢谢

如果您错误地使用分离的实体,这就是EF的工作方式。 我想你使用的是这样的东西:

 var employee = new Employee(); employee.Department = GetDepartmentFromSomewhere(departmentId); ... using (var context = new YourContext()) { context.Employees.AddObject(employee); context.SaveChanges(); } 

此代码编写了员工实体,添加了对现有部门的引用并将新员工保存到数据库中。 问题出在哪儿? 问题是AddObject不仅添加员工而是添加整个对象图。 这就是EF的工作方式 – 你不能拥有对象图,其中部分对象连接到上下文而部分不连接到上下文。 AddObject将图中的每个对象添加为新对象(新对象=在数据库中插入)。 因此,您必须更改操作顺序或手动修复实体状态,以便您的上下文知道该部门已存在。

第一个解决方案 – 使用相同的上下文来加载部门和保存员工:

 using (var context = new YourContext()) { var employee = new Employee(); ... context.Employees.AddObject(employee); employee.Department = context.Departments.Single(d => d.Id == departmentId); context.SaveChanges(); } 

第二种解决方案 – 将实体分别连接到上下文,然后在实体之间进行引用:

 var employee = new Employee(); ... var department = GetDepartmentFromSomewhere(departmentId); using (var context = new YourContext()) { context.Employees.AddObject(employee); context.Departments.Attach(department); employee.Department = department; context.SaveChanges(); } 

第三种解决方案 – 手动更正部门的状态,以便上下文不再插入它:

 var employee = new Employee(); employee.Department = GetDepartmentFromSomewhere(departmentId); ... using (var context = new YourContext()) { context.Employees.AddObject(employee); context.ObjectStateManager.ChangeObjectState(employee.Department, EntityState.Unchanged); context.SaveChanges(); } 

除了Ladislavs提供的3个解决方案之外,我想补充第4个解决方案。 事实上它是Naor简短答案的详细版本。 我正在使用entity framework版本6。

将deparment id分配给员工而不是department对象

除了我的模型类中的导航属性之外,我倾向于拥有“外键值”属性。

所以在Employee类中我有一个Department属性,也有一个类型为int的DepartmentId (如果Employee没有Department可能会使int为空):

 public class Employee { public int Id { get; set; } public String EmployeeName { get; set; } #region FK properties public Department Department { get; set; } public int? DepartmentId { get; set; } #endregion } 

你现在可以做的只是设置DepartmentId :所以而不是:

 employee.Department = departmentObject; 

刚设置:

 employee.DepartmentId = departmentObject.Id; 

要么

 employee.DepartmentId = departmentid 

现在,当在添加的员工上调用SaveChanges时,只会保存员工并且不会创建新的部门。 但是,由于分配了部门ID,因此从EmployeeDepartment的引用设置正确。

更多信息

我通常只在读取/处理员工时才访问Employee类的Department对象。 在创建或更新员工时,我会使用Employee类的DepartmentId属性来分配。

不分配给EmployeeDepartment属性有一个缺点:它可能使调试更加困难,因为在调用SaveChanges并重新读取员工之前,将无法查看或使用EmployeeDepartment对象。

修复EF6中的实体状态信息

这是指Ladislavs解决方案编号3。

使用EF6就是这样做的:

 _context.Entry(employee.Department).State = EntityState.Unchanged; 

当您将部门设置为员工时 – 我认为您应该validation部门是从数据库及其附加实体中检索的。
另外,您可以将deprtment的id(外键属性)设置为部门导航属性。