Has方法与With方法

A.HasRequired(a => a.B).WithOptional(b => b.A);
上面一句配置意思就是A类包含B类一个不为null的实例,B类包含A类一个实例,也可以不包含。

Has方法:

  1. HasOptional:前者(A)包含后者(B)一个实例或者为null
  2. HasRequired:前者包含后者一个不为null的实例
  3. HasMany:前者包含后者实例的集合

With方法:

  1. WithOptional:后者(B)可以包含前者(A)一个实例或者null
  2. WithRequired:后者包含前者一个不为null的实例
  3. WithMany:后者包含前者实例的集合

一、一对一关系:

两个类中都要配置相应的引用属性

1、DataAnnotations方式

public class Person
{
public int PersonId { get; set; }
public int SocialSecurityNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public PersonPhoto Photo { get; set; }
} public class PersonPhoto
{
[Key, ForeignKey("PhotoOf")] 注意:PersonPhoto表中的PersonId既是外键也必须是主键
public int PersonId { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; }
public Person PhotoOf { get; set; }
}

2、Fluent API方式

1:0..1关系

modelBuilder.Entity<Person>().HasOptional(t => t.Photo).WithRequired(t => t.PhotoOf); 
modelBuilder.Entity<PersonPhoto>().HasRequired(p => p.PhotoOf).WithOptional(p => p.Photo);

PersonPhoto必须属于一个Person,但是Person不一定有PersonPhoto, 此种情况下Person是一定存在的,所以它是主从关系主的一方。

1:1 关系

modelBuilder.Entity<Person>().HasRequired(p => p.Photo ).WithRequiredPrincipal();
modelBuilder.Entity<PersonPhoto>().HasRequired(t => t.PhotoOf).WithRequiredDependent(t => t.Photo);

PersonPhoto必须属于一个Person,Person也必须有PersonPhoto。
此种情况下,两个都一定存在,要确定主从关系,需要使用WithRequiredPrincipal或WithRequiredDependent。

如果你选择WithOptionalPrincipal(当前实体为主体;目标实体为依赖对象,包含主体的外键)PersonPhoto表中有一个外键,指向Person表的主键。
如果你选择WithOptionalDependent则相反(当前实体为依赖对象,包含主体的外键;目标实体为主体)则代表Person表中有一个外键,指向PersonPhoto表的主键,

Person表可以没有对应的PersonPhoto表数据,但是PersonPhoto表每一条数据都必须对应一条Person表数据。意思就是人可以没有照片,但是有的照片必须属于某个人。

二、一对多关系:

1、DataAnnotations方式

一对多关系很多情况下我们都不需要特意的去配置,通过一些引用属性、导航属性等检测到模型之间的关系,自动为我们生成外键。

public class Destination
{//景点类
public int DestinationId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public string Description { get; set; }
public byte[] Photo { get; set; }
public List<Lodging> Lodgings { get; set; }
} public class Lodging
{//住宿类
public int LodgingId { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
public bool IsResort { get; set; }
public decimal MilesFromNearestAirport { get; set; }
public Destination Target { get; set; }
}

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

2、更改外键的nullable属性和外键的名字

默认情况下,如果你的外键命名是规范的话,Code First会将的该属性设置为外键,不再自动创建一个外键,
规范命名是指符合:命名为如下的形式

  • [目标类型的键名]
  • [目标类型名称]+[目标类型键名称]
  • [导航属性名称]+[目标类型键名称]

在这里目标类型就是Destination,相对应的命名就是:DestinationId,DestinationDestinationId,TargetDestinationId

当然我们也可以自己在类中增加一个外键。

使用Data Annotations指定外键:注意ForeignKey位置的不同,其后带的参数也不同。

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

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

用Fluent API指定外键:

1.Lodging一定归属于一个Destination,这种关系是1:n。

modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithRequired(l => l.Destination).Map(l => l.MapKey("DestinationId"));
modelBuilder.Entity<Lodging>().HasRequired(l => l.Target).WithMany(d=>d.Lodgings).HasForeignKey(l => l.TarDestinationId);
//如果实体类没定义AccommodationId,那么可以使用Map方法直接指定外键名:.Map(s => s.MapKey("AccommodationId"))

2.Post可以单独存在,不用归属于Blog,这种关系是0..1:n。

modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithOptional(l => l.Destination).Map(l => l.MapKey("DestinationId")); modelBuilder.Entity<Lodging>().HasOptional(l => l.Target).WithMany(d => d.Lodgings).HasForeignKey(l => l.TarDestinationId);

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

public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Lodging> PrimaryContactFor { get; set; }
public List<Lodging> SecondaryContactFor { get; set; }
} public class Lodging
{
public int LodgingId { get; set; }
public string Name { get; set; }
public string Owner { get; set; }
public bool IsResort { get; set; }
public decimal MilesFromNearestAirport { get; set; }
public Destination Target { get; set; }
//第一联系人
public Person PrimaryContact { get; set; }
//第二联系人
public Person SecondaryContact { get; set; }
}

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

因为在这两个表之间存在多个一对多关系,所以Code First无法处理这种情况。
为了让Code First知道它们之间的对应关系,在这里要用到逆导航属性来解决。

使用Data Annotations:

//第一联系人
[InverseProperty("PrimaryContactFor")]
public Person PrimaryContact { get; set; }
//第二联系人
[InverseProperty("SecondaryContactFor")]
public Person SecondaryContact { get; set; }

或使用Fluent API:

modelBuilder.Entity<Lodging>().HasOptional(l => l.PrimaryContact).WithMany(p => p.PrimaryContactFor);
modelBuilder.Entity<Lodging>().HasOptional(l=>l.SecondaryContact).WithMany(p=>p.SecondaryContactFor)).Map(p => p.MapKey("SecondaryPersonID "));;

在生成的数据库中为自动为Lodging表生成两个外键:PrimaryContact _PersonID 和SecondaryPersonID

4、级联删除

1、如果两个表之间存在一对多关系,Code First默认会开启两个表之间的级联删除功能
数据库里可以可视化的设置不级联删除,Fluent API配置此外键关系时可以设置不级联删除:

this.HasMany(d => d.Lodgings).WithRequired(l => l.Destination).Map(l => l.MapKey("DestinationId")) //一对多并指定外键名
.WillCascadeOnDelete(false); // 关闭级联删除

2、也可以在上下文的OnModelCreating方法中

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

移除这个默认约定,再在需要开启级联删除的FluentAPI关系映射中用. WillCascadeOnDelete(true) 单独开启

三、多对多关系

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

public class Activity
{
public int ActivityId { get; set; }
[Required, MaxLength()]
public string Name { get; set; }
public List<Trip> Trips { get; set; }
} public class Trip
{
public int TripId{get;set;}
public DateTime StartDate{get;set;}
public DateTime EndDate { get; set; }
public decimal CostUSD { get; set; }
public byte[] RowVersion { get; set; }
public List<Activity> Activities { get; set; }
}

Code First生成了一张中间表ActivityTrips,将另外两张表的主键都作为外键关联到了中间表上面。
中间表中键的命名默认为"[目标类型名称]_[目标类型键名称]".Activity_ActivityId 和Trip_TripId
并且也作为这个新的连接表的联合主键。

指定表名

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

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

或:

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

Entity FrameWork Code First 配置关系的更多相关文章

  1. Entity Framework Code First关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  2. Entity Framework Code First主外键关系映射约定

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  3. MVC使用Entity Framework Code First,用漂亮表格显示1对多关系

    部门和职员是1对多关系.用一个表格列出所有部门,并且在每行显示该部门下的所有职员名称.如下: 部门和职员的Model: using System.Collections.Generic; namesp ...

  4. Entity Framework Code First关系映射约定【l转发】

    本篇随笔目录: 1.外键列名默认约定 2.一对多关系 3.一对一关系 4.多对多关系 5.一对多自反关系 6.多对多自反关系 在关系数据库中,不同表之间往往不是全部都单独存在,而是相互存在关联的.两个 ...

  5. Entity Framework Code First 映射继承关系

    转载 http://www.th7.cn/Program/net/201301/122153.shtml Code First如何处理类之间的继承关系.Entity Framework Code Fi ...

  6. Entity Framework Code First数据库连接

    1. 安装Entity Framework 使用NuGet安装Entity Framework程序包:工具->库程序包管理器->程序包管理器控制台,执行以下语句: PM> Insta ...

  7. Entity Framework Code First属性映射约定

    Entity Framework Code First与数据表之间的映射方式有两种实现:Data Annotation和Fluent API.本文中采用创建Product类为例来说明tity Fram ...

  8. Entity Framework Code First (一)Conventions

    Entity Framework 简言之就是一个ORM(Object-Relational Mapper)框架. Code First 使得你能够通过C#的类来描述一个模型,模型如何被发现/检测就是通 ...

  9. 使用 Entity Framework Code First

    使用 Entity Framework Code First 在家闲着也是闲着,继续写我的[ASP.NET MVC 小牛之路]系列吧.在该系列的上一篇博文中,在显示书本信息列表的时候,我们是在程序代码 ...

随机推荐

  1. 关于require js加载的时候报错的问题

    1.在项目中使用了requery.js  页面总是会出项一些奇怪的错误,如下 后来经过查找资料才发现,原来是因为依赖 的问题,因为require加载的时候是异步加载,而js之间是有相互依赖的,所以解决 ...

  2. 关于window.console&&console.log(123)的思考

    一.JS的且运算记得最开始看到window.console&&console.log(123),当时知道能起什么作用但是没有深入研究,最近在研究后总算弄明白了.要理解这个,首先得明白三 ...

  3. 【ES6】var、let、const三者的区别

    首先,一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? ECMAScript是一个国际通过的标准化脚本语言.JavaScript由ECMAScript和DOM.BOM ...

  4. 【读书笔记】读《编写可维护的JavaScript》 - 编程风格(第一部分)

    之前大致翻了一遍这本书,整体感觉很不错,还是不可追求快速,需要细细理解. 这篇随笔主要对本书的第一部分中对自己触动比较大的部分及与平常组织代码最为息息相关的部分做一个记录,加深印象. 主要讲述五点内容 ...

  5. 七、cent OS下干净卸载mysql

    使用以下命令查看当前安装mysql的情况rpm -qa | grep -i mysql显示之前安装的东西,示例:MySQL-client-5.5.25a-1.rhel5MySQL-server-5.5 ...

  6. tomcat开启远程调试和热部署(jrebel)启动tomcat

    @echo off set REBEL_HOME=D:\jrebel\jrebel--nosetup set JAVA_OPTS=-agentpath:%REBEL_HOME%\lib\jrebel6 ...

  7. ajax传json

    需求 前台有许多字段需要用ajax传送给后台, 如果给直接将字段封装成JSON对象传给后台会很方便 解决 ajax 发送 var str = {"name":"xiaom ...

  8. Docker镜像保存为文件及从文件导入镜像的方法

    参考 1.概述 我们制作好镜像后,有时需要将镜像复制到另一台服务器使用. 能达到以上目的有两种方式,一种是上传镜像到仓库中(本地或公共仓库),但是另一台服务器很肯能只是与当前服务器局域网想通而没有公网 ...

  9. Python之异常设计(一)

    一 定义 异常分为两类:一类是自动触发异常如除零错误:另一类是通过raise触发. 二 为什么要使用异常 当程序运行时,如果检测到程序错误,Python就会引发异常,我们可以在程序中使用try语句捕获 ...

  10. VC++ IPv6的支持

    最近根据项目需要,要在产品中添加对IpV6的支持,因此研究了一下IPV6的相关内容,Ipv6 与原来最直观的改变就是地址结构的改变,IP地址由原来的32位扩展为128,这样原来的地址结构肯定就不够用了 ...