EF Code First 学习笔记:关系

一对多关系

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

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 Destination { get; set; }
    }

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

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

 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 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 Destination { get; set; }
    } 

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

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

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

 指定外键

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

 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 int TargetDestinationId { get; set; }
        public Destination Target { get; set; }
    } 

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

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

比如我们将外键改为:

public int TarDestinationId { get; set; }

再重新生成数据库:

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

使用Data Annotations指定外键:

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

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

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

用Fluent API指定外键:

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:

       //第一联系人
        [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);

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

多对多关系

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

  public class Activity
     {
         public int ActivityId { get; set; }
         [Required, MaxLength(50)]
         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; }
    }

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

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

指定表名

如果我们想指定中间表的名称和键名称,我们可以用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");
                });

一对一关系

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

 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]
        public int PersonId { get; set; }
        public byte[] Photo { get; set; }
        public string Caption { get; set; }
        public Person PhotoOf { get; set; }
    }

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

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

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

使用Data Annotations

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")]
        public int PersonId { get; set; }
        public byte[] Photo { get; set; }
        public string Caption { get; set; }
        public Person PhotoOf { get; set; }
    }

使用Fluent API:

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

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

时间: 2024-11-08 17:29:37

EF Code First 学习笔记:关系的相关文章

EF Code First学习笔记

EF Code First学习笔记 初识Code First EF Code First 学习笔记:约定配置 Entity Framework 复杂类型 Entity Framework 数据生成选项DatabaseGenerated Entity Framework 并发处理 EF Code First 学习笔记:关系 Entity Framework Code First级联删除 EF Code First 学习笔记:表映射 EF Code First学习笔记:数据库创建 Entity Fr

EF Code First学习笔记 初识Code First

Code First是Entity Framework提供的一种新的编程模型.通过Code First我们可以在还没有建立数据库的情况下就开始编码,然后通过代码来生成数据库. 下面通过一个简单的示例来了解. 建立一个控制台项目.通过Nuget来获取Entity Framework. 增加两个模型类: public class Destination { public int DestinationId { get; set; } public string Name { get; set; }

EF Code First 学习笔记:表映射

多个实体映射到一张表 Code First允许将多个实体映射到同一张表上,实体必须遵循如下规则: 实体必须是一对一关系 实体必须共享一个公共键 观察下面两个实体: public class Person { [Key] public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName

EF Code First 学习笔记,关于特性InverseProperty 使用遇到的一些问题

一个实体多个引用的情况 我们来考虑一下下面的情况: 1 public class Lodging 2 { 3 public int LodgingId { get; set; } 4 public string Name { get; set; } 5 public string Owner { get; set; } 6 public bool IsResort { get; set; } 7 public decimal MilesFromNearestAirport { get; set;

EF Code First学习笔记:数据库创建

控制数据库的位置 默认情况下,数据库是创建在localhost\SQLEXPRESS服务器上,并且默认的数据库名为命名空间+context类名,例如我们前面的BreakAway.BreakAwayContext. 有几种方法可以改变这种默认约定. 利用配置文件 在配置文件中新加一个连接字符串 <connectionStrings> <add name="BreakAwayContext" providerName="System.Data.SqlClient

EF Code First 学习笔记:约定配置

要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就是Fluent API,通过新增相应的配置类来覆盖默认配置.现在我们用这两个来对比了解EF中的约定配置. 主键:KEY Data Annotations:通过Key关键字来标识一个主键 [Key] public int DestinationId { get; set; } Fluent API:

[转载]EF Code First 学习笔记:约定配置

要更改EF中的默认配置有两个方法,一个是用Data Annotations(在命名空间System.ComponentModel.DataAnnotations;),直接作用于类的属性上面;还有一个就是Fluent API,通过新增相应的配置类来覆盖默认配置.现在我们用这两个来对比了解EF中的约定配置. 主键:KEY Data Annotations:通过Key关键字来标识一个主键 [Key] public int DestinationId { get; set; } Fluent API:

iOS Code Signing 学习笔记&lt;转写&gt;

最近看了objc.io上第17期中的文章 <Inside Code Signing> 对应的中文翻译版 <代码签名探析> ,受益颇深,对iOS代码签名机制有了进一步的认识.想了解详细内容建议大家还是去看原文好了. 下面是对此文章的理解再结合自己之前对该部分的认识写出的学习笔记.本文的前提是已经对非对称加密有了一定的了解. 一.数字签名(digital signature) 对指定信息使用哈希算法,得到一个固定长度的信息摘要,然后再使用 私钥 (注意必须是私钥)对该摘要加密,就得到了

EF Code First学习

1. Code First : 代码优先, 然后根据代码创建数据库: code first的核心是 约定: 2.EF要求 一个类 必须有一个键属性. 规则约定 如果一个属性名为id 或 类名+id, 这一属性 就自动配置为键. 约定 不是必须, 如果不遵从约定 就必须使用 一些配置. 3.code first 使用配置 有两种方法(1).Data Annotations (数据注解)  (2).Fluent API  ; 允许配置各种属性. 关系.继承.级别和数据库映射. (1).Data An