一对多关系

项目中最常用到的就是一对多关系了。Code First对一对多关系也有着很好的支持。很多情况下我们都不需要特意的去配置,Code First就能通过一些引用属性、导航属性等检测到模型之间的关系,自动为我们生成外键。观察下面的类:

  1. public class Destination
  2. {
  3. public int DestinationId { get; set; }
  4. public string Name { get; set; }
  5. public string Country { get; set; }
  6. public string Description { get; set; }
  7. public byte[] Photo { get; set; }
  8. public List<Lodging> Lodgings { get; set; }
  9. }
  10.  
  11. public class Lodging
  12. {
  13. public int LodgingId { get; set; }
  14. public string Name { get; set; }
  15. public string Owner { get; set; }
  16. public bool IsResort { get; set; }
  17. public decimal MilesFromNearestAirport { get; set; }
  18. public Destination Destination { get; set; }
  19. }

Code First观察到Lodging类中有一个对Destination的引用属性,同时Destination中又有一个集合导航属性Lodgings,因此推测出Destination与Lodging的关系是一对多关系,所以在生成的数据库中为自动为Lodging表生成外键:

其实,只要在一个类中存在引用属性,即:

  1. public class Destination
  2. {
  3. public int DestinationId { get; set; }
  4. public string Name { get; set; }
  5. public string Country { get; set; }
  6. public string Description { get; set; }
  7. public byte[] Photo { get; set; }
  8. }
  9.  
  10. public class Lodging
  11. {
  12. public int LodgingId { get; set; }
  13. public string Name { get; set; }
  14. public string Owner { get; set; }
  15. public bool IsResort { get; set; }
  16. public decimal MilesFromNearestAirport { get; set; }
  17. public Destination Destination { get; set; }
  18. }

或一另一个类中存在导航属性:

  1. public class Destination
  2. {
  3. public int DestinationId { get; set; }
  4. public string Name { get; set; }
  5. public string Country { get; set; }
  6. public string Description { get; set; }
  7. public byte[] Photo { get; set; }
  8. public List<Lodging> Lodgings { get; set; }
  9. }
  10.  
  11. public class Lodging
  12. {
  13. public int LodgingId { get; set; }
  14. public string Name { get; set; }
  15. public string Owner { get; set; }
  16. public bool IsResort { get; set; }
  17. public decimal MilesFromNearestAirport { get; set; }
  18. }

Code First都能检测到它们之间一对多的关系,自动生成外键。

 指定外键

当然我们也可以自己在类中增加一个外键。默认情况下,如果你的外键命名是规范的话,Code First会将的该属性设置为外键,不再自动创建一个外键,如:

  1. public class Destination
  2. {
  3. public int DestinationId { get; set; }
  4. public string Name { get; set; }
  5. public string Country { get; set; }
  6. public string Description { get; set; }
  7. public byte[] Photo { get; set; }
  8. public List<Lodging> Lodgings { get; set; }
  9. }
  10.  
  11. public class Lodging
  12. {
  13. public int LodgingId { get; set; }
  14. public string Name { get; set; }
  15. public string Owner { get; set; }
  16. public bool IsResort { get; set; }
  17. public decimal MilesFromNearestAirport { get; set; }
  18. //外键
  19. public int TargetDestinationId { get; set; }
  20. public Destination Target { get; set; }
  21. }

规范命名是指符合:命名为“[目标类型的键名],[目标类型名称]+[目标类型键名称]”,或“[导航属性名称]+[目标类型键名称]”的形式,在这里目标类型就是Destination,相对应的命名就是:DestinationId,DestinationDestinationId,TargetDestinationId

对于命名不规范的列,Code First会怎做呢?

比如我们将外键改为:

  1. public int TarDestinationId { get; set; }

再重新生成数据库:

可以看到Code First没有识别到TarDestinationId是一个外键,于是自己创建了一个外键:Target_DestinationId。这时我们要告诉Code First该属性是一个外键。

使用Data Annotations指定外键:

  1. [ForeignKey("Target")]
  2. public int TarDestinationId { get; set; }
  3. public Destination Target { get; set; }

  1. public int TarDestinationId { get; set; }
  2. [ForeignKey("TarDestinationId")]
  3. public Destination Target { get; set; }

注意ForeignKey位置的不同,其后带的参数也不同。这样,生成的数据库就是我们所期望的了。Code First没有再生成别的外键。

用Fluent API指定外键:

  1. modelBuilder.Entity<Lodging>().HasRequired(p => p.Target).WithMany(l => l.Lodgings).HasForeignKey(p => p.TarDestinationId);

对同一个实体多个引用的情况

我们来考虑一下下面的情况:

Lodging(旅店)有两个对Person表的引用,分别是PrimaryContact与SecondaryContact,同时,在Person表中也有对这两个联系人的导航:PrimaryContactFor与SecondaryContactFor。

看看Code First默认会生成怎样的数据库

天哪,竟然生成了四个外键。因为有两套类型一样的导航属性与引用属性,Code First无法确定它们之间的对应关系,就单独为每个属性都创建了一个关系。这肯定不是我们所期望的,为了让Code First知道它们之间的对应关系,在这里要用到逆导航属性来解决。

使用Data Annotations:

  1. //第一联系人
  2. [InverseProperty("PrimaryContactFor")]
  3. public Person PrimaryContact { get; set; }
  4. //第二联系人
  5. [InverseProperty("SecondaryContactFor")]
  6. public Person SecondaryContact { get; set; }

或使用Fluent API:

  1. modelBuilder.Entity<Lodging>().HasOptional(l => l.PrimaryContact).WithMany(p => p.PrimaryContactFor);
  2. modelBuilder.Entity<Lodging>().HasOptional(l=>l.SecondaryContact).WithMany(p=>p.SecondaryContactFor);

再重新生成数据库,结果如图:

多对多关系

如果有两个类中,各自都是导航属性指向另一个类,Code First会认为这两个类之间是多对多关系,例如:

  1. public class Activity
  2. {
  3. public int ActivityId { get; set; }
  4. [Required, MaxLength(50)]
  5. public string Name { get; set; }
  6. public List<Trip> Trips { get; set; }
  7. }
  8.  
  9. public class Trip
  10. {
  11. public int TripId{get;set;}
  12. public DateTime StartDate{get;set;}
  13. public DateTime EndDate { get; set; }
  14. public decimal CostUSD { get; set; }
  15. public byte[] RowVersion { get; set; }
  16. public List<Activity> Activities { get; set; }
  17. }

一个Trip类可以有一些Activites日程,而一个Activity日程又可以计划好几个trips(行程),显然它们之间是多对多的关系。我们看看默认生成的数据库是怎么样的:

可以看到,Code First生成了一张中间表ActivityTrips,将另外两张表的主键都作为外键关联到了中间表上面。中间表中键的命名默认为"[目标类型名称]_[目标类型键名称]".

指定表名

如果我们想指定中间表的名称和键名称,我们可以用Fluent API来配置。

  1. modelBuilder.Entity<Trip>().HasMany(t => t.Activities).WithMany(a => a.Trips).Map(m =>
  2. {
  3. m.ToTable("TripActivities");
  4. m.MapLeftKey("TripIdentifier");//对应Trip的主键
  5. m.MapRightKey("ActivityId");
  6. });

或:

  1. modelBuilder.Entity<Activity>().HasMany(a => a.Trips).WithMany(t => t.Activities).Map(m =>
  2. {
  3. m.ToTable("TripActivities");
  4. m.MapLeftKey("ActivityId");//对应Activity的主键
  5. m.MapRightKey("TripIdentifier");
  6. });

一对一关系

如果我们要将两个类配置为一对一关系,则两个类中都要配置相应的引用属性,如:

  1. public class Person
  2. {
  3. public int PersonId { get; set; }
  4. public int SocialSecurityNumber { get; set; }
  5. public string FirstName { get; set; }
  6. public string LastName { get; set; }
  7. [Timestamp]
  8. public byte[] RowVersion { get; set; }
  9. public PersonPhoto Photo { get; set; }
  10. }
  11.  
  12. public class PersonPhoto
  13. {
  14. [Key]
  15. public int PersonId { get; set; }
  16. public byte[] Photo { get; set; }
  17. public string Caption { get; set; }
  18. public Person PhotoOf { get; set; }
  19. }

我们为一个(Person)对应着一张相片(PersonPhoto),但如果根据这样的模型生成数据库为报错:

无法确定类型“BreakAway.PersonPhoto”与“BreakAway.Person”之间的关联的主体端。必须使用关系 Fluent API 或数据注释显式配置此关联的主体端

因为Code First无法确认哪个是依赖类,必须使用Fluent API或Data Annotations进行显示配置。

使用Data Annotations

  1. public class Person
  2. {
  3. public int PersonId { get; set; }
  4. public int SocialSecurityNumber { get; set; }
  5. public string FirstName { get; set; }
  6. public string LastName { get; set; }
  7. [Timestamp]
  8. public byte[] RowVersion { get; set; }
  9. public PersonPhoto Photo { get; set; }
  10. }
  11.  
  12. public class PersonPhoto
  13. {
  14. [Key, ForeignKey("PhotoOf")]
  15. public int PersonId { get; set; }
  16. public byte[] Photo { get; set; }
  17. public string Caption { get; set; }
  18. public Person PhotoOf { get; set; }
  19. }

使用Fluent API:

  1. modelBuilder.Entity<PersonPhoto>().HasRequired(p => p.PhotoOf).WithOptional(p => p.Photo);

注意:PersonPhoto表中的PersonId既是外键也必须是主键

来源:https://www.cnblogs.com/liangxiaofeng/p/5809451.html

EF Code First 导航属性 与外键(转载)的更多相关文章

  1. EF Code First 导航属性 与外键

    一对多关系 项目中最常用到的就是一对多关系了.Code First对一对多关系也有着很好的支持.很多情况下我们都不需要特意的去配置,Code First就能通过一些引用属性.导航属性等检测到模型之间的 ...

  2. EF Code First中的主外键约定和一对一、一对多关系的实现

    对于主外键约定的理解,其实是学习实体间一对一和一对多关系的基础. 1.1 主键(Key)约定 主键的默认约定是:只要字段名为--实体名(类名)+"id"(不区分大小写),这就算是默 ...

  3. EF Code First导航属性一对一关系中注意点及配置方法

    //学生 public class Student { [key] public int StId { get; set; } public int SocialSecurityNumber { ge ...

  4. ASP.NET EF 延迟加载,导航属性延迟加载

    ASP.NET EF 延迟加载,导航属性延迟加载   EF(EntityFramework)原理:属于ORM的一种实现 通过edmx文件来查看三部分:概念模型,数据模型,映射关系,上下文DbConte ...

  5. EF Core反向导航属性解决多对一关系

    多对一是一种很常见的关系,例如:一个班级有一个学生集合属性,同时,班级有班长.语文课代表.数学课代表等单个学生属性,如果定义2个实体类,班级SchoolClass和学生Student,那么,班级Sch ...

  6. EF架构~过滤导航属性等,拼接SQL字符串

    拼接T-SQL串,并使它具有通用性 好处:与服务器建立一次连接,给服务器发一条SQL命令,即可实现 代码如下: 1 /// <summary> 2 /// 构建Insert语句串 3 // ...

  7. EF架构~为导航属性赋值时ToList()的替换方案

    回到目录 今天在进行EF开发时,遇到一个问题,在进行join查询时,类中的一个集合类型的导航属性,在给它赋值时,将查询出来的结果ToList()后,出错了,linq to entity不支持这种操作, ...

  8. ASP.NET Core EF 查询获取导航属性值,使用Include封装

    // 引用 using Microsoft.EntityFrameworkCore; // 摘要: // Specifies related entities to include in the qu ...

  9. mvc EF框架中,加载外键对象序列化对象时报错 序列化类型为XX的对象时检测到循环引用

    Newtonsoft.Json.dll 或者通过->工具->库程序包管理工具->NuGet管理包->联机 输入Newtonsoft或者json.net Newtonsoft.J ...

随机推荐

  1. IDEA的Database表的基本操作

    1.创建表 方法一:直接创建:右键-new-table 方法2: 参考别的表,直接用语句,右键-DDL and Sources- 然后直接在控制台修改 修改后直接运行,表就建好了 2.备份表 先用上面 ...

  2. spark wordcount 编程模型详解

    spark wordcount中一共经历多少个RDD?以及RDD提供的toDebugString    在控制台输入spark-shell   系统会默认创建一个SparkContext   sc h ...

  3. Spark Programming--- Shuffle operations

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  4. C# WebSocket Fleck 调用非托管C++ DLL 实现通信(使用stringbuilder接收)

     [DllImport(@"XXX.dll", CallingConvention = CallingConvention.StdCall)]public static exter ...

  5. 【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置(2)

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我们介绍了如何扩展Ocelot网关,并实现数据库存储,然后测试了网关的路由功能,一切都是那么顺利,但是有一个问题未解决,就是如果网关 ...

  6. Dubbo 源码分析 - SPI 机制

    1.简介 SPI 全称为 Service Provider Interface,是 Java 提供的一种服务发现机制.SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加 ...

  7. Linux系统安装Mysql5.7

    1.下载tar包,这里使用wget从官网下载 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.22-linux-glibc2. ...

  8. Javascript高级编程学习笔记(17)—— 引用类型(6)基本包装类

    基本包装类 基本包装类这个概念或许有的小伙伴没有听说过 但是小伙伴们有没有想过,为什么基本数据类型的实例也有方法呢? 其实这些方法都来自基本包装类型 这是JS为了方便操作基础数据类型而创建的特殊引用类 ...

  9. [Postman]证书(13)

    Postman的本机应用程序提供了一种基于每个域查看和设置SSL证书的方法. 要管理客户端证书,请单击标题工具栏右侧的扳手图标,选择“设置”,然后选择“ 证书”选项卡. 添加客户端证书 要添加新客户端 ...

  10. Git:fatal: Authentication failed

    1.删除保存的用户名和密码 执行 下面的命令,删除保存的用户名和密码 git config --system --unset credential.helper 重新操作,提示输入用户名和密码,操作成 ...