我在 C#/Blazor 作業
我有一個物件,例如 a Project,我從帶有外鍵及其相關導航屬性的資料庫中獲取該物件。我正在獲取物件,然后在斷開連接的狀態下使用它。
一旦物件被獲取,它就會被輸入到一個表單中,以便根據需要進行顯示/編輯/更新。我想創建一個單獨的 副本,Project在表單中用作 DTO,以便可以丟棄任何更改,而不會出現對原始 fetched 的參考問題Project。
例如,這是一個簡化的Project類:
public partial class Project
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(150)]
public string ProjectName { get; set; }
[Column("UpdatedBy_Fk")]
public int UpdatedByFk { get; set; }
[ForeignKey(nameof(UpdatedByFk))]
[InverseProperty(nameof(UserData.ProjectUpdatedByFkNavigations))]
public virtual UserData UpdatedByFkNavigation { get; set; }
}
在表單中,我顯示了Project使用@project.UpdatedByFkNavigation.FullName. 用戶根本無法修改導航欄位,它只能顯示。
我的問題是關于復制導航專案。現在為了簡單起見,在表單的 中OnInitialized,我將原始project物件傳遞給表單并objProject使用如下建構式創建一個新物件:
Project objProject = new() { Id = project.Id,
ProjectName = project.ProjectName,
UpdatedByFk = project.UpdatedByFk,
UpdatedByFkNavigation = project.UpdatedByFkNavigation,
這似乎正在作業并創建一個單獨的Project物件,該物件不是參考并且我可以將其用作我的 DTO,但是我不確定以virtual這種方式分配屬性是否合適。
這種方法是否遵循創建具有虛擬導航欄位的物件的非參考副本的最佳實踐,或者是否有其他方法可以解決這個問題?
uj5u.com熱心網友回復:
這取決于關系。參考在 EF 中很重要,因此您需要考慮是否希望新克隆參考相同的UserData 或具有相同資料的新的不同的 UserData。通常在多對一關系中,您希望使用相同的參考,或更新參考以匹配。如果原件被“John Smith” ID #201 修改,克隆將被“John Smith” ID #201 修改,或者更改為當前用戶“Jane Doe” ID #405,這將是相同的“Jane Doe”參考為用戶修改的任何其他記錄。您可能不希望 EF 創建一個以 ID #545 結束的新“John Doe”,因為 EF 被賦予了對具有“John Doe”副本的 UserData 的全新參考。
因此,在您的情況下,我假設您希望參考相同的現有用戶實體,因此您的方法是正確的。在使用像序列化/反序列化這樣的快捷方式進行克隆時,您需要小心。在這種情況下,序列化專案和任何加載的 UpdatedBy 參考將創建一個具有相同欄位甚至 PK 值的 UserData 的新實體。但是,當您使用新的 UserData 參考保存這個新專案時,您要么會遇到重復的 PK 例外、“已跟蹤相同鍵的物件”例外,要么會發現自己遇到了新的“John Doe” " 如果該物體設定為期望其 PK 的 Identity 列,則記錄 ID 為 #545。
關于使用導航屬性與 FK 欄位的典型建議:我的建議是使用其中之一,而不是兩者都使用。這樣做的原因是,當您同時使用兩者時,關系有兩個真實來源,并且取決于物體的狀態,當您更改一個時,另一個不一定會自動反映更改。例如,我通過 go: 來查看關系的一些代碼project.UpdatedByFk,而其他代碼可能使用project.UpdatedByFkNavigation.Id. 當涉及到導航屬性時,您的命名約定有點奇怪。對于您的示例,我會期望:
public virtual UserData UpdatedBy { get; set; }
一般來說,我會單獨使用導航屬性,并依賴 EF 中的陰影屬性來實作 FK。這看起來像:
public partial class Project
{
[Key]
public int Id { get; set; }
[Required]
[StringLength(150)]
public string ProjectName { get; set; }
[ForeignKey("UpdatedBy_Fk")] // EF Core.. For EF6 this needs to be done via configuration using .Map(MapKey()).
public virtual UserData UpdatedBy { get; set; }
}
在這里,我們定義導航屬性,并通過指定 FK 列名稱,EF 將為該 FK 在幕后創建一個不可直接訪問的欄位。我們的代碼揭示了這種關系的一個真相來源。
在某些速度很重要并且我幾乎不需要相關資料的情況下,我將宣告 FK 屬性而不是導航屬性。
參考這個:
[InverseProperty(nameof(UserData.ProjectUpdatedByFkNavigations))]
我還建議避免雙向參考,除非出于同樣的原因絕對必要。如果我希望給定用戶最后修改的所有專案,我真的不能通過以下方式獲得任何好處:
var projects = context.Users
.Where(x => x.Id == userId)
.SelectMany(x => x.UpdatedProjects)
.ToList();
我只會使用:
var projects = context.Projects
.Where(x => x.UpdatedBy.Id == userId)
.ToList();
通常,您應該通過聚合根來組織域及其中的關系:本質上是應用程式中具有頂級重要性的物體。雙向參考具有類似的問題,即在從一側修改這些關系時,在給定時間點不一定匹配的兩個真實來源。這在很大程度上取決于是否所有的關系都是預先加載的。
如果兩個物體都是聚合根并且關系足夠重要,那么這可以提供雙向參考和應有的額外關注。一個很好的例子可能是多對多關系,例如 CourseClass(即數學類 A)和學生之間的關系,其中 CourseClass 有很多學生,而 Student 有很多 CourseClass,從 CourseClass 的角度來看,列出是有意義的它是學生,從學生的角度列出他們的課程。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/370997.html
