简体   繁体   English

阻止实体框架为导航属性插入值

[英]Prevent Entity Framework to Insert Values for Navigational Properties

I am working on a WPF application using Entity Framework 4.0. 我正在使用Entity Framework 4.0处理WPF应用程序。 When I tried to save the object, I got a primary key exception, but the primary key is an AutoIncremented field and I cannot understand the reason for the exception. 当我尝试保存对象时,我得到了一个主键异常,但主键是一个AutoIncremented字段,我无法理解异常的原因。

So after trying this and that, and a little debugging and using the SQL profiler, I found out that prior to inserting my object, a record must be inserted in the parent table, as I set the navigation property of that object. 所以在尝试了这个和那个,以及一些调试和使用SQL分析器后,我发现在插入我的对象之前,必须在父表中插入一条记录,因为我设置了该对象的导航属性。

So the crux is if an attempt to insert Employee object and set its department as Employee.Department = deptObject, then a new record is set to be inserted on department object. 所以关键是如果尝试插入Employee对象并将其部门设置为Employee.Department = deptObject,则将新记录设置为插入部门对象。

Kindly suggest me someway by which navigational property objects won't be inserted in the database, any property or any method, Anything. 请告诉我某些导航属性对象不会插入数据库,任何属性或任何方法,Anything。

Thanks 谢谢

This is the way how EF works if you incorrectly use detached entities. 如果您错误地使用分离的实体,这就是EF的工作方式。 I suppose you are using something like this: 我想你使用的是这样的东西:

var employee = new Employee();
employee.Department = GetDepartmentFromSomewhere(departmentId);

...

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

This code prepared employee entity, added reference to existing department and saved new employee to the database. 此代码编写了员工实体,添加了对现有部门的引用并将新员工保存到数据库中。 Where is the problem? 问题出在哪儿? The problem is that AddObject doesn't add only employee but whole object graph. 问题是AddObject不仅添加员工而是添加整个对象图。 That is how EF works - you cannot have object graph where part of objects are connected to context and part of not. 这就是EF的工作方式 - 你不能拥有对象图,其中部分对象连接到上下文而部分不连接到上下文。 AddObject adds every object in the graph as a new one (new one = insert in database). AddObject将图中的每个对象添加为新对象(新对象=在数据库中插入)。 So you must either change sequence of your operations or fix state of entities manually so that your context knows that department already exists. 因此,您必须更改操作顺序或手动修复实体状态,以便上下文知道该部门已存在。

First solution - use the same context for loading department and saving employee: 第一个解决方案 - 使用相同的上下文来加载部门和保存员工:

using (var context = new YourContext())
{
    var employee = new Employee();
    ...
    context.Employees.AddObject(employee);

    employee.Department = context.Departments.Single(d => d.Id == departmentId);
    context.SaveChanges();
}

Second solution - connect entities to the context separately and after that make reference between entities: 第二种解决方案 - 将实体分别连接到上下文,然后在实体之间进行引用:

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();
}

Third solution - correct state of the department manually so that context doesn't insert it again: 第三种解决方案 - 手动更正部门的状态,以便上下文不再插入它:

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();
}

I would like to add a 4th solution to in addition to the 3 solutions already provided in Ladislavs great answer. 除了Ladislavs提供的3个解决方案之外,我想补充第4个解决方案。 In facts its a detailed version of the short answer from Naor. 事实上它是Naor简短答案的详细版本。 I am working with entity framework version 6. 我正在使用实体框架版本6。


Assign the deparment id to the employee instead of department object 将deparment id分配给员工而不是department对象

I tend to have a "foreign key value" property in addition to the navigation property in my model classes. 除了我的模型类中的导航属性之外,我倾向于拥有“外键值”属性。

So on the Employee class I have a Department property and also an DepartmentId of type int (make the int nullable if its possible that an Employee has no 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
}

Would you could do now is just setting the DepartmentId : So instead of: 你现在可以做的只是设置DepartmentId :所以而不是:

employee.Department = departmentObject;

just set: 刚设置:

employee.DepartmentId = departmentObject.Id;

or 要么

employee.DepartmentId = departmentid

Now when calling SaveChanges on the added employee, only the employee gets saved and no new department is created. 现在,在添加的员工上调用SaveChanges时,只会保存员工并且不会创建新的部门。 But the reference from Employee to Department is set correctly because of the assigned department id. 但是,由于分配了部门ID,因此从EmployeeDepartment的引用设置正确。


More info 更多信息

I usually would access the Department object of the Employee class only when reading / processing employees. 我通常只在读取/处理员工时才访问Employee类的Department对象。 When creating or updating employees, I would use the DepartmentId property of the Employee class to assign to. 在创建或更新员工时,我会使用Employee类的DepartmentId属性来分配。

Not assigning to the Department property of the Employee has one downside: It could make debugging more difficult, because before calling SaveChanges and re-reading the employees it would not be possible to see or use the Department object of the Employee . 不分配给EmployeeDepartment属性有一个缺点:它可能使调试更加困难,因为在调用SaveChanges并重新读取员工之前,将无法查看或使用EmployeeDepartment对象。


Fixing entity state info in EF6 修复EF6中的实体状态信息

This refers to Ladislavs solution number 3. 这是指Ladislavs解决方案编号3。

With EF6 it is done that way: 使用EF6就是这样做的:

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

In my case I had collections that were manually populated from a different context (different database). 在我的例子中,我有从不同的上下文(不同的数据库)手动填充的集合。 In order to prevent my main context from trying to save these collections I ended up adding 为了防止我的主要上下文试图保存这些集合,我最终添加了

[NotMapped, NotNavigable]

annotations to the property definitions. 属性定义的注释。

When you set the department to employee - I think that you should verify the department was retrieved from the db and it attached entity. 当您将部门设置为员工时 - 我认为您应该验证部门是从数据库及其附加实体中检索的。
In addition, you can put the id of deprtment (the foreign key property) instead of set the department navigation property. 此外,您可以输入deprtment的id(外键属性),而不是设置department导航属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM