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

发布于 2024-12-11 21:07:31 字数 317 浏览 0 评论 0原文

我正在使用 Entity Framework 4.0 开发 WPF 应用程序。当我尝试保存对象时,出现主键异常,但主键是自动增量字段,我无法理解异常的原因。

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

所以关键是如果尝试插入 Employee 对象并将其部门设置为 Employee.Department = deptObject,则将在部门对象上插入一条新记录。

请建议我以某种方式将导航属性对象插入到数据库中,任何属性或任何方法,任何东西。

谢谢

I am working on a WPF application using Entity Framework 4.0. 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.

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.

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.

Kindly suggest me someway by which navigational property objects won't be inserted in the database, any property or any method, Anything.

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

千と千尋 2024-12-18 21:07:31

如果您错误地使用分离实体,这就是 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();
}

This is the way how EF works if you incorrectly use detached entities. 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. That is how EF works - you cannot have object graph where part of objects are connected to context and part of not. AddObject adds every object in the graph as a new one (new one = insert in database). 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();
}
荒芜了季节 2024-12-18 21:07:31

除了 Ladislavs 很好的答案中已经提供的 3 个解决方案之外,我还想添加第四个解决方案。事实上,这是 Naor 简短回答的详细版本。我正在使用实体框架版本 6。

将部门 ID 分配给员工而不是部门对象

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

因此,在 Employee 类上,我有一个 Department 属性,还有一个 int 类型的 DepartmentId (如果可能的话,请使 int 为空) >Employee 没有 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

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

更多信息

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

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

修复 EF6 中的实体状态信息

这指的是 Ladislavs 解决方案 3。

在 EF6 中,它是这样完成的:

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

I would like to add a 4th solution to in addition to the 3 solutions already provided in Ladislavs great answer. In facts its a detailed version of the short answer from Naor. I am working with entity framework version 6.

Assign the deparment id to the employee instead of department object

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):

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:

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. But the reference from Employee to Department is set correctly because of the assigned department id.

More info

I usually would access the Department object of the Employee class only when reading / processing employees.
When creating or updating employees, I would use the DepartmentId property of the Employee class to assign to.

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.

Fixing entity state info in EF6

This refers to Ladislavs solution number 3.

With EF6 it is done that way:

_context.Entry(employee.Department).State = EntityState.Unchanged;
花间憩 2024-12-18 21:07:31

当您将部门设置为员工时 - 我认为您应该验证该部门是从数据库检索的并且它附加了实体。
另外,您可以输入部门的 id(外键属性)来代替设置部门导航属性。

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.

玻璃人 2024-12-18 21:07:31

就我而言,我有从不同上下文(不同数据库)手动填充的集合。为了防止我的主上下文尝试保存这些集合,我最终

[NotMapped, NotNavigable]

向属性定义添加了注释。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文