Entity Framework Core in Action
Entityframework Core in action是 Jon P smith
所著的關於Entityframework Core 書籍。原版地址. 是除了官方文檔外另一個學習EF Core的不錯途徑, 書中由淺入深的講解的EF Core的相關知識。因為沒有中文版,所以本人對其進行翻譯。 預計每兩天一篇更新 PS: 翻譯難免限於本人水平有不准確的地方,建議英文水平不錯的同學直接查看原版,有不足的地方歡迎指正
第一部分目錄導航
掀開EF Core的引擎蓋看看EF Core內部是如何工作的
創建了MyFirstEfCoreApp應用程序后,你現在可以通過它查看EF Core的工作原理,重點不在於應用程序的代碼,而是在讀取和寫入數據到數據庫時EF Core內部會發生什么. 我的目標是讓你了解EF Core的工作機制,當你深入研究本書其余部分的命令時,這會很有幫助
注 書中僅給出了關鍵代碼, 完整示例在 https://github.com/JonPSmith/EfCoreInAction/tree/Chapter01
數據庫建模
在對數據庫進行操作之前,EF Core必須進行數據庫建模. 數據庫建模是EF Core通過實體類和其他EF Core配置來描述數據庫的方法. EF Core在所有的數據庫訪問中使用建立的模型
建模在創建應用程序的DbContext時就開始了,在本例中是AppDbContext(如圖1.5所示,在上一篇文章中). 它有屬性DbSet
圖1.6描述了建模過程的概述,它會幫助你理解EF Core數據庫建模的過程. 后續的章節將介紹一系列配置數據庫的相關命令,在本文中使用默認配置

圖1.6展示了EF Core在AppDbContext的建模步驟,下文對此過程進行更詳細的說明
- EF Core查看DbContext並找到所有公共的DbSet
屬性,並使用屬性名為表定義初始名稱. - EF Core查看DbSet
的泛型類,查看類的屬性構建列名,類型等. 它還會查找類和屬性用於提供額外建模配置的特殊Attribute - EF Core查找DbSet
類中引用的其他類. 在我們的例子中Book類有一個對Author類的引用,所以EF Core也會查看它. 它對Author類執行與步驟2相同的操作. 同時它使用類名Author做為表名 - 建模過程的最后一個步驟, EF Core運行DbContext的虛方法
OnModelCreating
, 可以通過重寫OnModelCreating
方法使用fluent Api進行更多的建模配置,但本例中為了保持示例的簡單並沒有這樣做 - EF Core根據收集的信息創建數據庫的內部模型,並緩存數據庫模式,以便提升訪問速度. 在之后的所有的數據庫訪問中使用此模型
你可能會注意到圖1.6並沒有展示數據庫,因為EF Core構建內部模型時,它不會去查看數據庫. 我強調這一點是為了說明構建一個的數據庫模型多么重要,如果EF Core認為數據庫模型和實際的數據庫不匹配,就會出現問題
在你的應用程序中你可以使用EF Core來創建數據庫,這會避免出現不匹配的情況. 如果你想要一個良好且高效的數據庫,那么在你的代碼中編寫良好的數據庫模型是非常重要的,這樣創建的數據庫會是高效的. 創建,更新和管理數據庫結構是一個很大的主題,將在11章詳細介紹
從數據庫中讀取數據
現在可以訪問數據庫了. 我們使用List(l)命令,讓程序讀取數據庫並在終端上打印信息. 圖1.7顯示了輸出

下面列出代碼清單, 用於將所有的圖書與作者輸出到控制台

EF Core使用Linq(語言集成查詢)執行它想要執行的命令,使用.net類保存數據
代碼清單中粗體顯示的兩行代碼進行了數據庫訪問. 下面讓我們看看EF Core如何使用Linq代碼訪問數據庫並返回數據. 圖1.8跟隨着這些代碼走進EF Core內部,看看不為人知的故事...

從數據庫中讀取數據的過程如下
- Linq查詢中的
db.Books.AsNoTracking().Include(a => a.Author)
訪問應用程序DbContext的DbSet屬性, Include(a => a.Author)
顯式加載關系的Author部分. 數據庫提供程序將Linq翻譯成訪問數據庫的SQL命令. SQL被緩存以便如果再次使用相同的查詢語句時避免重新翻譯的成本
EF Core在數據庫訪問方面會盡可能高效. 在這種情況下,它將需要讀取的兩張表(Books和Author)組合到一個大表中,在一次數據庫訪問中完成工作. 下面的清單展示了EF Core和數據庫提供程序創建的SQL
SELECT [b].[BookId], [b].[AuthorId], [b].[Description], [b].[PublishedOn], [b].[Title], [a].[AuthorId], [a].[Name], [a].[WebUrl] FROM [Books] AS [b] INNER JOIN [Author] AS [a] ON [b].[AuthorId] = [a].[AuthorId] - 數據庫提供程序讀取數據后,EF Core通過以下過程放置數據: (a) 創建.NET類的實例 (b) 使用數據庫關系鏈接(外鍵),通過引用(稱為關系修復)將.NET類鏈接在一起. 結果是一組以正確方式鏈接的.NET類實例. 在本例中兩本書有相同的作者Martin Fowler,因此這兩本書的作者屬性指向同一個Author類
- 由於代碼中包含
AsNoTraching
, 所以EF Core知道禁止創建跟蹤快照. 跟蹤快照用於發現數據的變化, 你會在編輯WebUrl的示例中了解這一點. 由於這是一個只讀查詢,因此禁用跟蹤快速會使查詢更快
更新數據庫
現在使用MyFirstEfCoreApp中的第二個命令update(u)來更新圖書Quantum Networking作者的WebUrl列. 如圖1.9所示,首先列出所有書籍,會看到最后一本書的作者沒有WebUrl. 然后運行命令u,它將要求輸入Url. 這時輸入 httqs://entangled.moon
(這是一個虛構的Url,httpqs-.-),在更新成功后再次列出所有的書籍,這時可以看到Web Url值已經更新

代碼清單

圖1.10展示了EF Core內部發生了什么並跟蹤其進度,這比上一個read的示例復雜許多, 因此我會給你一些提示
圖頂部的讀取階段與上一個讀取示例類似,所以應該很熟悉. 在此基礎上使用圖書的標題做為過濾器載特定的圖書. 重要的是第2點: 對數據進行跟蹤
在圖的下半部分你可以看到EF Core如何將加載的數據與跟蹤快照進行比較並找到更改,可以看到只有WebUrl被更新了,它創建了一個SQL命令來只更新該列

圖中已經描述了大部分步驟,下面介紹Author的WebUrl列如何更新的詳細說明
- 應用程序使用LINQ查找包含作者信息的單個圖書,EF Core將LINQ查詢翻譯為SQL命令,讀取Title為Quantum Networking的行,返回Book和Author類的實例,因為使用了
Single
查詢,所以還會檢查是否只找到一行 - LINQ查詢中沒有
AsNoTracking
方法,所以該查詢是一個具有跟蹤的查詢,EF Core創建了數據的跟蹤快照 - 然后代碼更改了Book的Author的WebUrl屬性. 當調用SaveChanges時, 檢測更改階段會將跟蹤的所有類與跟蹤快照進行比較. 在這里它會檢測到所有已更改的內容. 在本例中主鍵為3的Author實例的WebUrl屬性值被更改
- 檢測到更改后,EF Core將啟動事務. 每個數據庫更新都以原子單位完成: 更改全部成功或者全部失敗. 這非常重要,因為如果僅應用了部分更改,關系數據庫可能會發生嚴重的錯誤
- 更新請求由數據庫提供程序轉換為SQL命令,如果執行成功則提交事務並返回SaveChanges方法,否則會拋出異常