C# 數據操作系列 - 7. EF Core 導航屬性配置

来源:https://www.cnblogs.com/c7jie/archive/2020/05/17/12904854.html
-Advertisement-
Play Games

在上一篇,大概介紹了Entity Framework Core關於關係映射的邏輯。在上一篇中留下了EF的外鍵映射沒有說,也就是一對一,一對多,多對一,多對多的關係等。這一篇將為大家細細分析一下,如何設置這些映射。 1. 實體之間的關係 從數據表來考慮,兩個表之前的關係有一對一,一對多(多對一)和多對 ...


在上一篇,大概介紹了Entity Framework Core關於關係映射的邏輯。在上一篇中留下了EF的外鍵映射沒有說,也就是一對一,一對多,多對一,多對多的關係等。這一篇將為大家細細分析一下,如何設置這些映射。

1. 實體之間的關係

從數據表來考慮,兩個表之前的關係有一對一,一對多(多對一)和多對多的關係。

其中一對一,指的是表A有一條記錄對應著表B最多有一條記錄與之對應。反過來也一樣,表A也最多有一條記錄與表B的某一條記錄對應。具體在數據表上表現為,A表和B表各有一個外鍵指向對方。

一對多和多對一是一個概念,只是參考的方向是相反的。所謂的一對多就是其中多方上有一個屬性或者列指向了另一個實體,而那個“一”的那頭則沒有對應的屬性指向多方。

多對多是指兩個類的實例各有一個集合屬性指向對方,換句話說就是A有0到多個B,B也有0到多個A。這裡有一個關於多對多的ER圖。

image-20200515220140873

2. 一對一關係

先給出兩個示例類,為了方便理解,我只保留了主鍵和導航屬性:

public class SingleModel
{
    public int Id { get; set; }
    public SingleTargetModel SingleTarget { get; set; }
}

public class SingleTargetModel
{
    public int Id { get; set; }
    public SingleModel Single { get; set; }
}

那麼我們開始寫配置文件:

public class SingleModelConfig : IEntityTypeConfiguration<SingleModel>
{
    public void Configure(EntityTypeBuilder<SingleModel> builder)
    {
        builder.ToTable("SingleModel");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        var relation = builder.HasOne(t => t.SingleTarget).WithOne(r => r.Single);

    }
}

public class SingleTargeModelConfig : IEntityTypeConfiguration<SingleTargetModel>
{
    public void Configure(EntityTypeBuilder<SingleTargetModel> builder)
    {
        builder.ToTable("SingleTargetModel");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
    }
}

其中HasOne表示當前實體是關係中“一”,WithOne 表示導航目標類的關係。

當然,如果直接應用這兩個配置到EF Context的話,在執行

Update-Database

會報以下錯誤:

The child/dependent side could not be determined for the one-to-one relationship between 'SingleModel.SingleTarget' and 'SingleTargetModel.Single'. To identify the child/dependent side of the relationship, configure the foreign key property. If these navigations should not be part of the same relationship configure them without specifying the inverse. See http://go.microsoft.com/fwlink/?LinkId=724062 for more details.

意思就是無法定義一對一關係中的子/從屬方

如何解決呢?之前在說的時候,EF會根據導航屬性自動生成一個外鍵,但是這一條在一對一這裡就有點不太起作用了。所以我們必須手動在導航屬性的一側實體類里配置外鍵,並用 HasForeignKey指定。(如果不使用Fluent API,也是需要在一端實體類配置外鍵,另一端則不需要)。

修改後:

public class SingleModel
{
    public int Id { get; set; }
    public int TargetId { get; set; }
    public SingleTargetModel SingleTarget { get; set; }
}
public class SingleTargetModel
{
    public int Id { get; set; }
    public SingleModel Single { get; set; }
}

所以最終的配置應該如下:

public class SingleModelConfig : IEntityTypeConfiguration<SingleModel>
{
    public void Configure(EntityTypeBuilder<SingleModel> builder)
    {
        builder.ToTable("SingleModel");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        builder.HasOne(t => t.SingleTarget).WithOne(r => r.Single).HasForeignKey<SingleModel>(t=>t.TargetId);

    }
}

public class SingleTargeModelConfig : IEntityTypeConfiguration<SingleTargetModel>
{
    public void Configure(EntityTypeBuilder<SingleTargetModel> builder)
    {
        builder.ToTable("SingleTargetModel");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        //builder.HasOne(t => t.Single).WithOne(r => r.SingleTarget).HasForeignKey<SingleTargetModel>("SingleId");
    }
}

註意我註釋的這一行,現在EF只在SingleModel表中生成了一個外鍵關係,在檢索SingleTargetModel的時候,EF會從SingleModel表中檢索對應的外鍵關係,並引入進來。

如果取消這行註釋,EF會在SingleTargetModel表添加一個名為SingleId並指向SingleModel的外鍵,而取消SingleModel里的外鍵。

但是,這時候如果在SingleTargetModel里添加了一個非空屬性的SingleId,SQLite插入數據時會報錯。錯誤信息:

SQLite Error 19: 'FOREIGN KEY constraint failed'.

其他資料庫提示,外鍵不能為空。

所以也就是說EF不推薦這種雙方互導航的一對一關係。

這是生成的DDL SQL語句:

create table SingleModel
(
	Id INTEGER not null
		constraint PK_SingleModel
			primary key autoincrement,
	TargetId INTEGER not null
		constraint FK_SingleModel_SingleTargetModel_TargetId
			references SingleTargetModel
				on delete cascade
);

create unique index IX_SingleModel_TargetId
	on SingleModel (TargetId);

create table SingleTargetModel
(
	Id INTEGER not null
		constraint PK_SingleTargetModel
			primary key autoincrement
);

3. 一對多或多對一

照例,先來兩個類:

public class OneToManySingle
{
    public int Id { get; set; }
    public List<OneToManyMany> Manies { get; set; }
}

public class OneToManyMany
{
    public int Id { get; set; }
    public OneToManySingle One { get; set; }
}

如果從OneToManySingle來看,這個關係是一對多,如果從OneToManyMany來看的話這個關係就是多對一。

那麼我們看一下一對多的配置吧:

public class OneToManySingleConfig : IEntityTypeConfiguration<OneToManySingle>
{
    public void Configure(EntityTypeBuilder<OneToManySingle> builder)
    {
        builder.ToTable("OneToManySingle");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        builder.HasMany(t => t.Manies)
            .WithOne(p => p.One);
    }
}
public class OneToManyManyConfig : IEntityTypeConfiguration<OneToManyMany>
{
    public void Configure(EntityTypeBuilder<OneToManyMany> builder)
    {
        builder.ToTable("OneToManyMany");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        //builder.HasOne(p => p.One).WithMany(t=>t.Manies);
    }
}

在使用隱式外鍵的時候,只需要設置導航屬性的關聯即可。如果想在Single端設置,需要先用 HasMany表示要設置一個多對X的關係,然後調用WithOne 表示是多對一。如果是Many端,則必須先聲明是HasOne。

其中 WithXXX里的參數可以省略,如果只是配置了單嚮導航的話。

如果顯示聲明瞭外鍵,需要用HasForeignKey來標註外鍵。

以下是生成的DDL SQL語句:

create table OneToManySingle
(
	Id INTEGER not null
		constraint PK_OneToManySingle
			primary key autoincrement
);
create table OneToManyMany
(
	Id INTEGER not null
		constraint PK_OneToManyMany
			primary key autoincrement,
	OneId INTEGER
		constraint FK_OneToManyMany_OneToManySingle_OneId
			references OneToManySingle
				on delete restrict
);

create index IX_OneToManyMany_OneId
	on OneToManyMany (OneId);

4. 多對多

在講多對多的時候,需要先明白一個概念。多對多,對於導航兩端來說,是無法在自己身上找到對應的標記的。也就是說,各自的數據表不會出現指向對方的外鍵。那麼,如何實現多對多呢?增加一個專門的中間表,用來存放兩者之間的關係。

EF Core中取消了在映射關係中配置中間表的功能,所以在EF Core中需要一個中間表:

public class ManyToManyModelA
{
    public int Id { get; set; }
    public List<ModelAToModelB> ModelBs { get; set; }
}
public class ModelAToModelB
{
    public int Id { get; set; }
    public ManyToManyModelA ModelA { get; set; }
    public ManyToManyModelB ModelB { get; set; }
}
public class ManyToManyModelB
{
    public int Id { get; set; }
    public List<ModelAToModelB> ModelAs { get; set; }
}

那麼繼續看一下配置文件:

public class ManyToManyToModelAConfig : IEntityTypeConfiguration<ManyToManyModelA>
{
    public void Configure(EntityTypeBuilder<ManyToManyModelA> builder)
    {
        builder.ToTable("ManyToManyModelA");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        builder.HasMany(t => t.ModelBs).WithOne(p => p.ModelA);
    }
}

public class ManyToManyModelBConfig : IEntityTypeConfiguration<ManyToManyModelB>
{
    public void Configure(EntityTypeBuilder<ManyToManyModelB> builder)
    {
        builder.ToTable("ManyToManyModelB");
        builder.HasKey(p => p.Id);
        builder.Property(p => p.Id).ValueGeneratedOnAdd();
        builder.HasMany(t => t.ModelAs).WithOne(p => p.ModelB);
    }
}

與一對多的關係不同的地方是,這個需要兩方都配置一個多對一的映射,指向中間表。

在EF 6中 中間表可以僅存在於關係中,但是在EF Core3 還沒有這個的支持。也就是當前文章使用的版本。

5. 附加

在EF的外鍵約束中,導航屬性是預設可空的。如果要求非空,也就是導航屬性的另一端必須存在則需要在配置關係的時候添加:

IsRequired()

這個方法也用來聲明欄位是必須的。這個驗證是在EF 調用 SaveChanges 的時候校驗的。

6. 未完待續

照例的未完待續,下一篇將為大家介紹一下EF Core 在開發中的用法。

更多內容煩請關註我的博客《高先生小屋》

file


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Spring Boot項目啟動的時候會列印如下內容。 1 . ____ _ __ _ _ 2 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ 3 ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ 4 \\/ ___)| |_)| | | ...
  • https://www.wenjuan.com/s/mQf2uaH/https://www.wenjuan.com/s/mQf2uaHhttps://www.wenjuan.com/z/mQf2uaH/https://www.wenjuan.com/z/mQf2uaHhttps://www.wenj ...
  • 在上面文章abp(net core)+easyui+efcore實現倉儲管理系統——入庫管理之十一(四十七) 的學習之後,我們已經實現了入庫單前端的關於實現庫位的功能,今天我們來學習如何在後臺實現添加庫位的功能。接上文。 ...
  • C#硬體開發,一種是調用廠家提供的api;另一種就是通過com口,發送命令,和硬體通信。這2種方法,如果有硬體,業務流程很好調試。但是大部分硬體,只有和客戶聯調才會有硬體調試的機會。那業務流程沒有硬體,怎樣調試?一種是利用vs自帶的斷點跳過功能,這種方式慢,而且不同人調試都要加斷點,0效率低。另一種 ...
  • 上一篇:異常Exception(二) 使用try...catch...捕獲異常,如果能預料到某些異常可能發生,可以使用精確的異常例如“FileNotFoundException”、“DirectoryNotFoundException”、“IOException”等,最有使用一般異常“Excepti ...
  • 《ASP.NET MVC 5 編程實戰》 [作者] (美) Dino Esposito[譯者] (中) 潘麗臣[出版] 清華大學出版社[版次] 2015年03月 第1版[印次] 2015年03月 第1次 印刷[定價] 59.80元 【前言】 Web Forms 的最常見應用場景是,你要開發專註於呈現 ...
  • NuGet: Dapper 2.0.35 MySql.Data System.Data 實體(Entity) 1 public class student 2 { 3 public int Id { get; set; } 4 public string RealName { get; set; } ...
  • 上一篇文章(https://www.cnblogs.com/meowv/p/12896177.html)已經成功創建了博客項目,但是abp預設給我們引用了許多項目中用不到的組件。 本篇文章將給項目進行瘦身,刪掉對我們來說暫時用不到的組件。講解各個模塊之間的關係,寫一個Hello World,讓其成功 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...