关系与导航属性

本主题概述实体框架如何管理实体间的关系。还对如何映射和操作关系提供了一些指南。

关系、导航属性和外键

在关系数据库中,表之间的关系(也称为关联)是通过外键定义的。外键 (FK) 是用于在两个表的数据之间建立并强制链接的一列或列组合。有三种关系类型:一对一、一对多和多对多。在一对多关系中,外键是在表示关系多端的表上定义的。 多对多关系涉及定义第三个表(也称为接合或联接表),主键由来自两个相关表的外键组成。在一对一关系中,主键还用作外键,两个表都没有单独的外键列。

下图显示的两个表存在一对多关系。Course 表为依赖表,因为它包含 DepartmentID 列,该列链接到 Department 表。

在实体框架中,实体可以通过关联(关系)与其他实体相关。每个关系都包含两端,它们描述关系中两个实体的实体类型以及类型的多重性(一、零 或一、多)。关系可由引用约束控制,该引用约束描述了关系中的哪端为 Principal Role 以及哪端为 Dependent Role。

导航属性为在两个实体类型间导航关联提供了一种方式。针对对象参与到其中的每个关系,各对象均可以具有导航属性。使用导航属性,可以在两个 方向上导航和管理关系,返回引用对象(如果多重性为一或者零或一)或集合(如果多重性为多)。也可以选择使用单向导航,在这种情况下,只对参与关系的一种 而不是两种类型定义导航属性。

建议在映射到数据库中外键的模型中包含属性。加入了外键属性,您就可以通过修改依赖对象的外键值来创建或更改关系。此类关联称为外键关联。 在 N 层应用程序中,使用外键更为重要。请注意,在 1 对 1 或 1 对 0..1 关系中,没有单独的外键列,主键属性用作外键并且始终包含在模型中。

当模型中不包含外键列时,关联信息将作为独立对象管理。关系是通过对象引用而不是外键属性跟踪的。这种关联类型称为“独立关联”。修改独立关联 的最常见方式是修改为参与关联的每个实体生成的导航属性。

可以在您的模型中选择使用一种或两种类型的关联。不过,如果多对多关系是通过只包含外键的联接表连接的纯粹关系,EF 将使用独立关联来管理这样的多对多关系。

下图所示为使用实体框架设计器创建的概念模型。该模型包含两个参与一对多关系的实体。这两个实体都有导航属性。Course 为依赖实体,它定义了 DepartmentID 外键属性。

下图所示为使用 Code First 创建的相同模型。

配置/映射关系

本页面的其余部分介绍如何使用关系访问和操作数据。有关在模型中如何设置关系的信息,请参见以下页面。

创建和修改关系

外键关联 中,更改关系时,具有 EntityState.Unchanged 状态的依赖对象的状态会改为 EntityState.Modified。在独立关系中,更改关系不会更新依赖对象的状态。

下面的示例演示如何使用外键属性和导航属性关联相关对象。使用外键关联时可以使用任一种方法更改、创建或修改关系。使用独立关联,则不能使用外键属性。

  • 通过为外键属性指定新值,如下例所示。

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

  • 通过使用 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 查询

加载相关对象

在实体框架中,一般使用导航属性加载与定义的关联返回的实体相关的实体。有关更多信息,请参见加载相关对象


注意:在外键关联中,加载依赖对象的相关端时,将根据内存中当前的相关外键值加载相关对象:

 
// Get the course where currently DepartmentID = 1.
Course course2 = context.Courses.First(c=>c.DepartmentID == 2);

// Use DepartmentID foreign key property
// to change the association.
course2.DepartmentID = 3;

// Load the related Department where DepartmentID = 3
context.Entry(course).Reference(c => c.Department).Load();

在独立关联中,基于当前数据库中的外键值查询依赖对象的相关端。不过,如果修改了关系,并且依赖对象的引用属性指向对象上下文中加载的不同主对象,实体框架将尝试创建关系,就像它在客户端定义的那样。

管理并发

在外键和独立关联中,并发检查都是基于实体键和模型中定义的其他实体属性的。使用 EF 设计器创建模型时,将 ConcurrencyMode 特性设置为 fixed 可指定应对该属性进行并发检查。使用 Code First 定义模型时,应对要进行并发检查的属性使用 ConcurrencyCheck 注释。使用 Code First 时,也可以使用 TimeStamp 注释指定应对属性进行并发检查。一个给定类中只能有一个时间戳属性。Code First 将此属性映射到数据库中一个不可为 Null 的字段。

建议始终使用外键关联处理参与并发检查和解析的实体。

有关更多信息,请参见开放式并发模式

使用重叠键

重叠键是指键中某些属性亦是实体中其他键的一部分的那些组合键。在独立关联中不能包含重叠键。若要更改包含重叠键的外键关联,我们建议您修改外键值而不要使用对象引用。

关系与导航属性(摘自微软MSDN)的更多相关文章

  1. Dapper实现一对多对象关系聚合导航属性

    领域对象:Game(游戏), Room(游戏群),两者一对多的关系,SQL语句中会用到JOIN public class Game : AggregateRoot { public string Ta ...

  2. MVC3+EF4.1学习系列(五)----- EF查找导航属性的几种方式

    文章索引和简介 通过上一篇的学习 我们把demo的各种关系终于搭建里起来 以及处理好了如何映射到数据库等问题 但是 只是搭建好了关系 问题还远没有解决 这篇就来写如何查找导航属性 和查找导航属性的几种 ...

  3. EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public?

    前言 不知我们是否思考过一个问题,在关系映射中对于导航属性的访问修饰符是否一定必须为public呢?如果从未想过这个问题,那么我们接下来来探讨这个问题. EF 6.x和EF Core 何种情况下必须配 ...

  4. 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  5. Json.net对于导航属性的处理(解决对象循环引用)

    对于两张表A.B多对多的关系中,A的导航属性中有B,B的导航属性中有A,这样Json.net对A或者B对象序列化时会形成死循环 所以对于导航属性要加标签 首先在A.B实体类工程(Model)中引用Js ...

  6. ASP.NET MVC深入浅出(被替换) 第一节: 结合EF的本地缓存属性来介绍【EF增删改操作】的几种形式 第三节: EF调用普通SQL语句的两类封装(ExecuteSqlCommand和SqlQuery ) 第四节: EF调用存储过程的通用写法和DBFirst模式子类调用的特有写法 第六节: EF高级属性(二) 之延迟加载、立即加载、显示加载(含导航属性) 第十节: EF的三种追踪

    ASP.NET MVC深入浅出(被替换)   一. 谈情怀-ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态 ...

  7. 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...

  8. 《Entity Framework 6 Recipes》中文翻译系列 (23) -----第五章 加载实体和导航属性之预先加载与Find()方法

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-2  预先加载关联实体 问题 你想在一次数据交互中加载一个实体和与它相关联实体. ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (25) ------ 第五章 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-5  加载完整的对象图 问题 你有一个包含许多关联实体的模型,你想在一次查询中, ...

随机推荐

  1. 分享录制的正则表达式入门、高阶以及使用 .NET 实现网络爬虫视频教程

    我发布的「正则表达式入门以及高阶教程」,欢迎学习. 课程简介 正则表达式是软件开发必须掌握的一门语言,掌握后才能很好地理解到它的威力: 课程采用概念和实验操作 4/6 分隔,帮助大家理解概念后再使用大 ...

  2. JavaScript高级程序设计学习笔记--错误处理与调试

    try-catch语句 只要代码中包含finally子句,则无论try或catch语句块中包含什么代码--甚至return语句,都不会阻止finally子句的执行,来看下面这个函数: function ...

  3. 使用自定义标签模拟jstl的&lt;c:for each&gt;标签

    一.自定义标签的基本编写 下面编写一个自定义标签,它可以输出当前的时间. 1.编写标签类 类可以通过继承SimpleTagSupport类实现一个标签类编写.父类为我们提供了一些编写自定义标签的快捷的 ...

  4. paip.提升性能---list,arraylist,vector,linkedlist,map的选用..

    paip.提升性能---list,arraylist,vector,linkedlist,map的选用.. arraylist,vector基本一样,但是,vector线程安全的. 作者Attilax ...

  5. 详解如何用AD 生成Gerber文件

    以上gerber文件就出完了;  下面步骤是:进行导出" 钻孔文件 ". 以上钻孔文件就出完了;  到此就全部完成输出了. 下面的操作,也可以不用导的 .下面步骤是:进行导出&qu ...

  6. trident教程

      (一)理论基础更多理论以后再补充,或者参考书籍1.trident是什么?Trident is a high-level abstraction for doing realtime computi ...

  7. cloud-init 典型应用 - 每天5分钟玩转 OpenStack(174)

    本节介绍几个 cloud-init 的典型应用:设置 hostanme,设置用户初始密码,安装软件. 设置 hostname cloud-init 默认会将 instance 的名字设置为 hostn ...

  8. es6数组的扩展

    数组扩展运算符 ...(三个点) const demoArr=[0,1,2,3,4] console.log(...demoArr) // 0 1 2 3 4 // 他把一个数组用逗号分隔了出来 // ...

  9. Sprint 冲刺第三阶段第二天

    陈汝婷:播放音乐 1:做播放音乐这个功能时开始没有考虑周全,使用 PS P出来的图竟然没有用上,耗时耗人工.吃一见长一智,以后要考虑周全.还要耗了那么久,音乐的初效果终于出来了. 2:昨天出现的问题, ...

  10. sqlite limit offset

    limit 0,20 表示从第1条开始取20条数据 limit 20 offset 2  表示从第2条开始取出20条数据