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實體)創建數據庫。生成后,查看數據表的關系映射,如下圖所示:

    

  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實體)創建數據庫。生成后,查看數據表的關系映射,如下圖所示:

 

  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實體)創建數據庫。生成后,查看數據表的關系映射,如下圖所示:

 

四. Fluent API

   要在Fluent API中配置關系,首先要確定組成關系的導航屬性。HasOne或HasMany標識開始配置的實體類型上的導航屬性。然后調用WithOneWithMany來標識反導航。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實體)創建數據庫。生成后,查看數據表的關系映射,如下圖所示:

  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 實體關系

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM