我正在使用Entity Framework 4.0开发WPF应用程序。当我尝试保存对象时,我遇到了主键异常,但主键是AutoIncremented字段,我无法理解异常的原因。
因此,在尝试了这个和那个,以及一些调试和使用SQL分析器后,我发现在插入我的对象之前,必须在父表中插入一条记录,因为我设置了该对象的导航属性。
所以关键是如果尝试插入Employee对象并将其部门设置为Employee.Department = deptObject,则会将新记录设置为插入部门对象。
请告诉我某些导航属性对象不会插入数据库,任何属性或任何方法,Anything。
由于
答案 0 :(得分:42)
如果您错误地使用分离的实体,这就是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();
}
答案 1 :(得分:3)
除了Ladislavs提供的3个解决方案之外,我想补充第4个解决方案。事实上它是Naor简短答案的详细版本。我正在使用实体框架版本6。
将deparment ID分配给员工而不是部门对象
除了我的模型类中的导航属性外,我倾向于拥有“外键值”属性。
所以在Employee
类上我有一个Department
属性,还有一个类型为int的DepartmentId
(如果Employee
可能没有{可能使int成为可空{1}}):
Department
您现在可以做的只是设置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
时,只会保存员工并且不会创建新的部门。但是,由于指定的部门ID,SaveChanges
到Employee
的引用设置正确。
更多信息
我通常只在阅读/处理员工时访问Department
类的Department
对象。
在创建或更新员工时,我会使用Employee
类的DepartmentId
属性来分配。
不分配Employee
的{{1}}属性有一个缺点:它可能使调试更加困难,因为在调用Department
并重新阅读员工之前,这是不可能的查看或使用Employee
的{{1}}对象。
修复EF6中的实体状态信息
这是指Ladislavs解决方案编号3。
使用EF6可以这样做:
SaveChanges
答案 2 :(得分:0)
当您将部门设置为员工时 - 我认为您应该验证部门是从数据库及其附加实体中检索的。
另外,您可以输入deprtment的id(外键属性),而不是设置部门导航属性。
答案 3 :(得分:0)
就我而言,我有从不同上下文(不同数据库)手动填充的集合。为了防止我的主要背景试图保存这些收藏,我最终添加了
[NotMapped, NotNavigable]
属性定义的注释。