天天看點

asp.net core系列 26 EF模型配置(實體關系)

asp.net core系列 26 EF模型配置(實體關系)

一.概述

  EF實體關系定義了兩個實體互相關聯起來(主體實體和依賴實體的關系,對應資料庫中主表和子表關系)。 在關系型資料庫中,這種表示是通過外鍵限制來展現。本篇主要講一對多的關系。先了解下描述關系的術語。

  (1) 依賴實體: 這是包含外鍵屬性的實體(子表)。有時稱為 child 。

  (2) 主體實體: 這是包含主/備用鍵屬性的實體(主表)。 有時稱為 parent。

  (3) 外鍵:依賴實體(子表)中的屬性,用于存儲主表的主鍵屬性的值。

  (4) 主鍵: 唯一辨別的主體實體(主表)的屬性。 這可能是 primary key 或備用鍵。

  (5) 導航屬性: 包含對相關實體引用,在的主體和或依賴實體上定義的屬性。

    集合導航屬性: 一個導航屬性,對多個相關實體的引用。

    引用導航屬性: 一個導航屬性,對單個相關實體的引用。

         下面示例代碼來說明Blog和Post之間的一對多關系。其中 

Post

 是依賴實體;

Blog

 是主體實體;

Post.BlogId

 是外鍵;

Blog.BlogId

 是主鍵;Post.Blog引用導航屬性;Blog.Posts集合導航屬性。

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}      

二.約定

  按照約定,當在實體類型上發現導航屬性時,将建立關系。如果屬性所指向的類型不能被目前資料庫提供程式映射為标量類型,則将其視為導航屬性。下面用三個示例來說明通過約定建立的主從實體關系。

  2.1 完全定義的關系

    關系最常見的模式是在關系的兩端定義導航屬性,并在依賴實體類中定義外鍵屬性。

    (1)如果兩個類型之間找到一對導航屬性,則它們将被配置為同一關系的反轉導航屬性。

    (2)如果依賴實體包含名為的屬性

<primary key property name>

, 

<navigation property name><primary key property name>

,或

<principal entity name><primary key property name>

然後它将配置為外鍵。

    下面代碼示例是完全定義的關系的實體,通過約定來建立主從實體關系。

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    //反轉導航屬性
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    //通過約定(<primary key property name>)建立外鍵
    public int BlogId { get; set; }
    //反轉導航屬性
    public Blog Blog { get; set; }
}      

    使用EF基于資料模型(Blog和Post實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:

asp.net core系列 26 EF模型配置(實體關系)

    

  2.2  沒有外鍵屬性

    雖然建議在依賴實體類中定義外鍵屬性,但這不是必需的。如果沒有找到外鍵屬性,那麼将引入一個名為<navigation property name> > principal key property name>的隐藏外鍵屬性(上篇有介紹)。在下面代碼中,依賴實體Post中沒有顯示定義外鍵BlogId。

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}      

   2.3 單一導航屬性

     僅包含一個導航屬性(沒有反轉導航屬性,也沒有外鍵屬性)就足以擁有約定定義的關系。還可以有一個導航屬性和一個外鍵屬性。代碼如下所示産:

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}      

 三. 資料注釋

   有兩個資料注釋可用于配置關系,[ForeignKey]和[InverseProperty]。

  3.1 ForeignKey可以将指定屬性設定為外鍵屬性。 這種設定通常是外鍵屬性不被約定發現時,顯示設定。如下面代碼示例, 在依賴實體Post中将BlogForeignKey指定為外鍵。代碼中生成的主從實體關系與上面的約定示例是一樣的。

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogForeignKey { get; set; }

    [ForeignKey("BlogForeignKey")]
    public Blog Blog { get; set; }
}      

   下面使用EF基于資料模型(Blog和Post實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:

asp.net core系列 26 EF模型配置(實體關系)

  3.2 InverseProperty配置依賴實體和主體實體上的導航屬性如何配對。當兩個實體類型之間有一對以上的導航屬性時,通常會這樣做。如下面代碼示例:

public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public Blog Blog { get; set; }

        public User Author { get; set; }

        public User Contributor { get; set; }
    }

    public class User
    {
        public string UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        [InverseProperty("Author")]
        public List<Post> AuthoredPosts { get; set; }

        [InverseProperty("Contributor")]
        public List<Post> ContributedToPosts { get; set; }
    }      

    下面使用EF基于資料模型(User和Post實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:

asp.net core系列 26 EF模型配置(實體關系)

四. Fluent API

   要在Fluent API中配置關系,首先要确定組成關系的導航屬性。HasOne或HasMany辨別開始配置的實體類型上的導航屬性。然後調用

WithOne

WithMany

來辨別反導航。HasOne/WithOne用于引用導航屬性,而HasMany/WithMany用于集合導航屬性。

  在EF基于現有資料庫進行反向工程時,根據資料庫将自動生成DbContext上下文類,裡面重寫了OnConfiguring方法。下面示例是一個MyContext上下文類,在OnModelCreating方法中确定了實體的關系。

  

  4.1 完全定義的關系

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog) //Post類有一個Blog引用導航屬性
            .WithMany(b => b.Posts);//Blog類有一個Posts反導航集合
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    //反導航集合
    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    //引用導航屬性
    public Blog Blog { get; set; }
}      

  4.2 單一導航屬性

    如果隻有一個導航屬性,那麼就會出現無參數重載的WithOne以及WithMany。這表明在關系的另一端有一個概念上的引用或集合,但是實體類中不包含導航屬性。

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .HasMany(b => b.Posts)//blog類有一個集合導航屬性
            .WithOne();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}      

    下面使用EF基于資料模型(User和Post實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:

asp.net core系列 26 EF模型配置(實體關系)

  4.3 外鍵

    可以使用 Fluent API 配置哪些屬性應用作給定關系外鍵屬性。

class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>()
            .HasOne(p => p.Blog)
            .WithMany(b => b.Posts)
            .HasForeignKey(p => p.BlogForeignKey);
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogForeignKey { get; set; }
    public Blog Blog { get; set; }
}      

    以下代碼清單示範如何配置複合外鍵。

class MyContext : DbContext
{
    public DbSet<Car> Cars { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Car>()
            .HasKey(c => new { c.State, c.LicensePlate });

        modelBuilder.Entity<RecordOfSale>()
            .HasOne(s => s.Car)
            .WithMany(c => c.SaleHistory)
            .HasForeignKey(s => new { s.CarState, s.CarLicensePlate });
    }
}

public class Car
{
    public string State { get; set; }
    public string LicensePlate { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }

    public List<RecordOfSale> SaleHistory { get; set; }
}

public class RecordOfSale
{
    public int RecordOfSaleId { get; set; }
    public DateTime DateSold { get; set; }
    public decimal Price { get; set; }

    public string CarState { get; set; }
    public string CarLicensePlate { get; set; }
    public Car Car { get; set; }
}      

  總結:關于實體關系,還講到了“必需和可選的關系”、“級聯删除”。以及關系模式中的“一對一關系”、“多對多關系",這些以後用到再參考文檔。 個人認為在傳統開發中,以建庫建表優先的情況下,不會去設定資料表的外鍵關系,這種關系是由程式設計去控制。 這樣對資料庫進行反向工程時,也不會生成有關系的主從實體模型。

 參考文獻:

    官方文檔:EF 實體關系

posted on 2019-02-18 15:18 花陰偷移 閱讀(...) 評論(...) 編輯 收藏