7

默認情況下,EF Core有一個「代碼優先思路」,即它應該以代碼優先的方式使用,即使支持數據庫優先方法,它也被描述爲對現有方法進行反向工程數據庫並創建它的代碼優先表示。我的意思是,在代碼中「手工」(代碼優先)創建並從數據庫(通過Scaffold-DbContext命令)生成的模型(POCO類)應該是相同的。在Entity Framework Core中編寫實體POCO類的正確方法是什麼?

令人驚訝的是,官方EF核心文件證明了顯着的差異。下面是在代碼中創建的模型的例子:從現有的數據庫https://ef.readthedocs.io/en/latest/platforms/aspnetcore/new-db.html這裏是的例子逆向工程是:https://ef.readthedocs.io/en/latest/platforms/aspnetcore/existing-db.html

這是在第一種情況下的實體類:

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; } 
} 

,這是第二種情況下的實體類:

public partial class Blog 
{ 
    public Blog() 
    { 
     Post = new HashSet<Post>(); 
    } 

    public int BlogId { get; set; } 
    public string Url { get; set; } 

    public virtual ICollection<Post> Post { get; set; } 
} 

第一個例子是一個非常簡單,非常明顯的POCO類。它在文檔中隨處可見(數據庫生成的示例除外)。第二個例子,雖然,有一些補充:

  • 類被聲明爲部分(即使有無處可看它的另一個部分定義)。
  • 導航屬性的類型爲ICollection < T>,而不僅僅是列表< T>。
  • 導航屬性初始化爲新的HashSet < T>()在構造函數中。代碼優先例子中沒有這樣的初始化。
  • 導航屬性被聲明爲虛擬
  • 生成的上下文類中的DbSet成員也是虛擬

我已經試過從數據庫(本文寫作時最新的工具)腳手架模型,它生成的實體完全如圖所示,所以這不是一個過時的文檔問題。所以官方的工具生成不同的代碼,官方文檔建議編寫不同的(不重要的)代碼 - 沒有部分類,虛擬成員,構造初始化等。

我的問題是,試圖在代碼中構建模型,應該如何我寫我的代碼?我喜歡使用ICollection而不是List,因爲它更通用,但除此之外,我不確定是否需要關注文檔或MS工具?我需要將它們聲明爲虛擬嗎?我需要在構造函數中初始化它們嗎?等等......

我從舊時代EF知道,虛擬導航屬性允許延遲加載,但它不支持連(尚)在EF核心,我不知道任何其他用途。也許它會影響性能?也許工具嘗試生成面向未來的代碼,這樣,當延遲加載將被執行,在POCO類和上下文就能支持呢?如果是這樣,我能夠擺脫他們,因爲我並不需要懶加載(所有數據查詢是在回購封裝的)?

不久,請幫助我瞭解爲什麼差別,以及風格應該在代碼構建模型時怎麼用?

+1

關於第3點,根據我自己最近項目的經驗...我強烈建議不要初始化它們以清空構造函數中的集合。原因在於,在許多情況下,您的業務邏輯需要能夠確定明細集合是空的還是尚未加載。 –

回答

11

我試着給一個簡短的回答每一個點你提到

  • partial類的工具生成的代碼特別有用。假設你想實現一個僅模型的派生屬性。對於代碼第一,你只要做到這一點,無論你想要的。對於數據庫,如果更新模型,則類文件將被重寫。因此,如果您想保留擴展代碼,您希望將其放置在託管模型之外的其他文件中 - 這是partial可幫助您在不調整自動生成的代碼的情況下擴展該類的地方。

  • ICollection絕對是一個合適的選擇,即使對於第一代碼。沒有排序聲明,您的數據庫可能不會支持已定義的訂單。

  • 構造函數初始化至少是方便......假設你有一個空收集數據庫明智的,或者你沒有加載所有的財產。沒有構造函數,你必須明確地在代碼中的任意點處理null個案。無論你是否應該使用ListHashSet,我現在都無法回答。

  • virtual爲數據庫實體啓用代理創建,這可以幫助您做兩件事情:如您已經提到的延遲加載並更改跟蹤。代理對象可以使用setter立即跟蹤對虛擬屬性的更改,而上下文中的普通對象需要在SaveChanges上進行檢查。在某些情況下,這可能會更有效(通常不會)。

  • virtual IDbSet背景下的條目允許單元測試的測試,樣機環境設計更容易。其他用例也可能存在。

+1

請注意,EF Core還沒有延遲加載(或更改跟蹤)代理。我真的不知道爲什麼RevEng生成虛擬導航道具。 – bricelam

+0

我已經提交了問題[#6326](https://github.com/aspnet/EntityFramework/issues/6326)與團隊討論了使用虛擬技術。 – bricelam

+0

大多數藏品都由獨特的價值構成。例如,'List '集合中不會有兩個相同的條目。因此,HashSet通常是表示這種情況的語義上正確的選擇。 –

相關問題