创建和修改关系

对于拥有外键属性的关系,修改关系是非常简单的,如下:

course.DepartmentID = newCourse.DepartmentID;

下面的代码通过将外键设置为 null 删除了关系。请注意,外键属性必须可以为 Null。

course.DepartmentID = null;

注意:如果引用处于已添加状态(在本例中为 course 对象),在调用 SaveChanges 之前,引用导航属性将不与新对象的键值同步。由于对象上下文在键值保存前不包含已添加对象的永久键,因此不发生同步。

通过将一个新对象分配给导航属性。下面的代码在 course 和department 之间创建关系。如果对象附加到上下文,course 也会添加到 department.Courses 集合中,course 对象的相应的外键属性设置为 department 的键属性值。

course.Department = department;

要删除该关系,请将导航属性设置为 null。如果使用的是基于 .NET 4.0 的实体框架,则需要先加载相关端,然后再将其设置为 Null。例如:

context.Entry(course).Reference(c = >c.Department).Load();
course.Department = null;

从实体框架5.0(它基于 .NET 4.5)开始,不必加载相关端就可以将关系设置为 Null。也可以使用以下方法将当前值设置为 Null。

context.Entry(course).Reference(c = >c.Department).CurrentValue = null;

通过在实体集合中删除或添加对象。例如,可以将 Course 类型的对象添加到 department.Courses 集合中。此操作将在特定 course 和特定 department 之间创建关系。如果对象附加到上下文,course 对象的 department 引用和外键属性将设置为相应的 department。

department.Courses.Add(newCourse);

此处,如果course的departmentid不能为空,则可能会出现错误,对department.Courses集合不能有删除course的操作,否则会出现错误。因为如果从集合中移除了course,在SaveChanges过程中把该过程识别为更新关系,而那些被删除的course的departmentid又不能为空,所以save不会成功。

通过使用 ChangeRelationshipState方法更改两个实体对象间指定关系的状态。此方法是处理 N 层应用程序和独立关联 时最常用的方法(不能用于外键关联)。此外,要使用此方法,必须下拉到 ObjectContext,如下例所示。

在下面的示例中,Instructor和 Course 之间存在多对多关系。调用 ChangeRelationshipState 方法并传递 EntityState.Added 参数,使 SchoolContext 知道在这两个对象间添加了关系。

((IObjectContextAdapter)context).ObjectContext.ObjectStateManager.

                 ChangeRelationshipState(course, instructor, c => c.Instructor,EntityState.Added);

请注意,如果是更新(而不仅是添加)关系,添加新关系后必须删除旧关系:

((IObjectContextAdapter) context).ObjectContext.ObjectStateManager.

ChangeRelationshipState(course, oldInstructor, c = >c.Instructor, EntityState.Deleted);

同步FK 和导航属性之间的更改

使用上述方法中的一种更改附加到上下文的对象的关系时,实体框架需要保持外键、引用和集合同步。实体框架使用代理自动管理 POCO 实体的这种同步(也称为关系修复)。

如果不通过代理使用POCO 实体,则必须确保调用 DetectChanges 方法同步上下文中的相关对象。请注意,下面的 API 会自动触发 DetectChanges 调用。

· DbSet.Add

· DbSet.Find

· DbSet.Remove

· DbSet.Local

· DbContext.SaveChanges

· DbSet.Attach

· DbContext.GetValidationErrors

· DbContext.Entry

· DbChangeTracker.Entries

· 对 DbSet 执行 LINQ 查询

如果context中有很多实体,而且你正在多次调用上述方法,那么就会造成很大的性能影响。可以使用下面的代码来的代码禁用自动检测:

using(var context = newBloggingContext())
{
  try
  {

   context.Configuration.AutoDetectChangesEnabled = false;

 

    // Make manycalls in a loop

    foreach (var blog inaLotOfBlogs)

    {

      context.Blogs.Add(blog);

    }

  }

  finally
  {

   context.Configuration.AutoDetectChangesEnabled = true;

  }

}

除了以上方法,还可以调用context.ChangeTracker.DetectChanges方法来显式检测变化。需要小心使用这些高级方法,否则很容易在你的程序里引入微妙的bug。