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實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISM9AnYldnJwAzN9c3Pn5GcuQ0MlMWbidXN51UMjRlTxEFRNNTQq1EdFpnTysGRNJTQE1EenRVT5FEVPhXQq1EdBpmTxUEVOhHOp10drRVT3lkeMdXWU5EeVRVT2NmMiNnSywEd5ITW110MaZHetlVdO1GT0UERNl3YXJGc5kHT20ESjBjUIF2Lc12bj5SYphXa5VWen5WY35iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.png)
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辨別開始配置的實體類型上的導航屬性。然後調用
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實體)建立資料庫。生成後,檢視資料表的關系映射,如下圖所示:
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 花陰偷移 閱讀(...) 評論(...) 編輯 收藏