問題記錄:EntityFramework 一對一關係映射

来源:http://www.cnblogs.com/xishuai/archive/2016/11/17/ef-one-to-one-fluent-api.html
-Advertisement-
Play Games

EntityFramework 一對一關係映射有很多種,比如主鍵作為關聯,配置比較簡單,示例代碼: 上面代碼表示 Teacher 和 Student 一對一關係,Fluent API 配置如下: 測試代碼: 生成 SQL 代碼: 另一種 Fluent API 配置如下: 執行同樣測試代碼,生成 SQ ...


EntityFramework 一對一關係映射有很多種,比如主鍵作為關聯,配置比較簡單,示例代碼:

public class Teacher
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Student Student { get; set; }
}

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Teacher Teacher { get; set; }
}

上面代碼表示 Teacher 和 Student 一對一關係,Fluent API 配置如下:

modelBuilder.Entity<Teacher>()
    .HasRequired(x => x.Student)
    .WithOptional(x => x.Teacher);
modelBuilder.Entity<Student>();

測試代碼:

var teachers = await _teacherRepository.GetAll().Include(x => x.Student).ToListAsync()

生成 SQL 代碼:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Teachers] AS [Extent1]
    INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]

另一種 Fluent API 配置如下:

modelBuilder.Entity<Teacher>();
modelBuilder.Entity<Student>()
    .HasRequired(x => x.Teacher)
    .WithOptional(x => x.Student);

執行同樣測試代碼,生成 SQL 代碼:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Teachers] AS [Extent1]
    LEFT INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]

根據上面的測試情況,我們可以得到一些信息,首先測試代碼查詢 Teacher,然後 Inclue Student,Fluent API 配置的不同,生成的 SQL 代碼也不同:

  • Fluent API 配置 Teacher,HasRequired Student 對應 INNER JOIN
  • Fluent API 配置 Student,HasRequired Teacher 對應 LEFT INNER JOIN

我們可以得出,一對一關係,Fluent API 只需要配置一個實體就可以了,根據查詢關聯的不同,配置對應的 HasRequired 和 WithOptional。

一對一關係,除了兩個實體主鍵映射外,還有一種情況就是主鍵和外鍵映射,可以理解為主表和子表映射,示例代碼:

public class Teacher
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int TeacherId { get; set; }
    public virtual Teacher Teacher { get; set; }
}

Student 中有一個 TeacherId 屬性,對應 Teacher 中的主鍵 Id,在微軟的官方示例中,Student 是作為主表,Teacher 作為子表,也就是說,我們在查詢的時候是查詢的 Student,然後 Include Teacher,Fluent API 配置:

modelBuilder.Entity<Teacher>();
modelBuilder.Entity<Student>()
    .HasRequired(x => x.Teacher)
    .WithMany()
    .HasForeignKey(x => x.TeacherId);

測試代碼:

var students = await _studentRepository.GetAll().Include(x => x.Teacher).ToListAsync();

生成 SQL 代碼:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[TeacherId] AS [TeacherId], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[Name] AS [Name1]
    FROM  [dbo].[Students] AS [Extent1]
    INNER JOIN [dbo].[Teachers] AS [Extent2] ON [Extent1].[TeacherId] = [Extent2].[Id]

這是沒有什麼問題的,需要註意的是 Teacher 中並沒有 Student 的導航屬性,如果直接添加的話,運行會直接報錯(Teachers 表預設生成的 Student_Id 欄位找不到),解決方式是需要配置 Teacher 的相關 Fluent API。

上面的一對一關係,其實就是主表的一個子表擴展,在主表中存儲子表的主鍵作為外鍵,查詢的時候直接 Include 子表就可以了,但還有一種情況是,我查詢子表,然後 Include 主表,主表的主鍵存儲在子表中作為外鍵,這裡的主表和子表概念只是相對的。

比如上面場景中,我查詢 Teacher 然後把 Student Include 包含進來,如果是上面的配置是沒有辦法的,因為 Teacher 並沒有配置導航屬性,所以,我們需要改一下代碼:

public class Teacher
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Student Student { get; set; }
}

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int TeacherId { get; set; }
    public virtual Teacher Teacher { get; set; }
}

上面說過,Teacher 增加 Student 導航屬性會直接報錯,然後我們再修改下 Fluent API 配置:

modelBuilder.Entity<Teacher>()
    .HasRequired(x => x.Student)
    .WithOptional(x => x.Teacher)
    .Map(x => x.MapKey("TeacherId"));
modelBuilder.Entity<Student>();

測試代碼:

var teachers = await _teacherRepository.GetAll().Include(x => x.Student).ToListAsync();

生成 SQL 代碼:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[StudentCount] AS [StudentCount], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[Name] AS [Name1], 
    [Extent2].[TeacherId] AS [TeacherId]
    FROM  [dbo].[Teachers] AS [Extent1]
    INNER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[TeacherId] = [Extent2].[Id]

上面這段代碼會執行報錯的,因為[Extent1].[TeacherId] = [Extent2].[Id]的 Id 順序錯了,MapKey 配置的是 Teacher,而不是 Student,所以,我們再修改下 Fluent API 配置:

modelBuilder.Entity<Teacher>();
modelBuilder.Entity<Student>()
    .HasRequired(x => x.Teacher)
    .WithOptional(x => x.Student)
    .Map(x => x.MapKey("TeacherId"));

需要註意的是,因為 Teacher 中有了 Student 導航屬性,所以我們沒有辦法再進行 HasForeignKey 的配置。

再次執行測試代碼,並沒有生成 SQL 代碼,而是直接報錯:Each property name in a type must be unique. Property name 'TeacherId' is already defined.

根據錯誤提示,我們去除 Student 中的 TeacherId 屬性,重新執行測試代碼。

生成的 SQL 代碼:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[StudentCount] AS [StudentCount], 
    [Extent3].[Id] AS [Id1], 
    [Extent3].[Name] AS [Name1], 
    [Extent3].[TeacherId] AS [TeacherId]
    FROM   [dbo].[Teachers] AS [Extent1]
    LEFT OUTER JOIN [dbo].[Students] AS [Extent2] ON [Extent1].[Id] = [Extent2].[TeacherId]
    LEFT OUTER JOIN [dbo].[Students] AS [Extent3] ON [Extent1].[Id] = [Extent3].[TeacherId]

結果是沒有什麼問題的,但 LEFT OUTER JOIN 了兩次,不知道具體是什麼原因。

網上找了相關的資料,但一對一關係示例都是那種:子表沒有導航屬性,主表存儲子表的主鍵作為外鍵,並有子表的導航屬性,上面的類似示例,stackoverflow 找到一個,但評論中的解決方式試過不行。

針對這種情況,如果有更好的實現方式,歡迎告知。


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

-Advertisement-
Play Games
更多相關文章
  • 使用了第三方的JS庫或框架,在VS中編寫JS代碼,發現真是個悲劇,完全只能手打,智能感知沒了,這不符合VS的一貫做風只要在寫代碼的JS文件加上以下代碼,就可以有智能感知了 說明:path內為使用的第三方JS庫或框架的js文件,如果文件沒有和在編碼的JS在同一目錄下,則要補全路徑 ...
  • 問題 怎樣用在 Web API 中創建 OData 服務。 解決方案 對於我們來說,在 Web API 中使用 OData最簡單的方式就是使用 ASP.NET 模板來創建Odata Controller。在 Controllers 文件夾上滑鼠右鍵->添加->新建項。 顯示一個如圖 12-1 的對話 ...
  • 今天去深圳溜達了一天,剛回來,看到首頁都是微軟大法好,看來離.NET的春天就差3個月了~~回到正題,這篇的教程講解下拉配置,詳情如下:... ...
  • 本文版權歸博客園和作者吳雙共同所有,轉載和爬蟲請註明原文地址:www.cnblogs.com/tdws 首先分享幾個振奮人心的新聞: 1.谷歌已經宣佈加入.NET基金會 2.微軟加入Linux基金會,繼續對Linux示好。換了CEO就是不一樣 3.微軟發佈VS For Mac! 第一步 下載dll ...
  • 從今天開始,正式進入Asp.net Core的開發,估計最近一段時間會經常寫博客了,記錄學些Asp.net Core中遇到的各種坑。 第一個問題:通過core編寫的webapi,預設返回的json會自動格式化為駝峰樣式,並沒有按照具體的類名來返回,如何讓其按照類名返回呢? 在Startup.cs全局 ...
  • 恢復內容開始 如何做到將客戶伺服器資料庫的備份,下載到本地的雲服務上? 在開發這個程式中中途也遇到了一下問題,下麵我將自己如何進解決的辦法寫出來供大家參考。 一.首先我需要進行描述一下問題: 1.比如有兩台伺服器A,B(雲伺服器) 將A中的伺服器中的資料庫的備份進行下載到B的雲服務中並保存。 2.當 ...
  • 感謝您的閱讀。喜歡的、有用的就請大哥大嫂們高抬貴手“推薦一下”吧!你的精神支持是博主強大的寫作動力以及轉載收藏動力。歡迎轉載! 版權聲明:本文原創發表於 【請點擊連接前往】 ,未經作者同意必須保留此段聲明!如有侵權請聯繫我刪帖處理! 我的博客:http://www.cnblogs.com/GJM6/ ...
  • 字元串轉組件名 字元串轉變數名 或 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...