簡體   English   中英

如何解決虛擬實體框架對象的“不要在構造函數中調用可覆蓋的方法”警告?

[英]How can I resolve a “Do not call overridable methods in constructors” warning for virtual Entity Framework objects?

我正在使用實體框架並想對屬性使用延遲加載,所以我將屬性設置為virtual

一個例子:

public class Child
{
    public string Name { get; set; }
}

public class Parent
{
    public Parent()
    {
        Child = new Child();
    }

    public virtual Child Child { get; set; }
}

當我這樣做時,我收到一個CA2214警告:

Severity    Code    Description Project File    Line    Suppression State
Warning CA2214  'Parent.Parent()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: 

Parent..ctor()
Parent.set_Child(Child):Void    Project C:\Parent.cs    18  Active

我想刪除此警告,但如果我將Parent標記為已sealed ,則會收到預期的錯誤:

Severity    Code    Description Project File    Line    Suppression State
Error   CS0549  'Parent.Child.get' is a new virtual member in sealed class 'Parent' Project C:\Parent.cs    24  N/A

那么如何解決這個警告(不忽略它)並仍然使用virtual

在屬性上使用初始化器而不是構造器。

public class Parent
{
    public virtual Child Child { get; set; } = new Child();
}

編輯:關於上述和那個

...有時我需要在構造函數中為Child設置屬性...

簡單的規則是“你可能不應該”。 實體的作用是代表該實體的數據 state,僅此而已。 初始化實體“圖”不應在頂級實體的構造函數中完成,而應使用工廠模式。 例如,我使用帶有 EF 的存儲庫模式,我不僅管理 getter,還充當提供 Create 方法的工廠,以及處理軟刪除場景的 Delete。 這有助於確保始終在“最低限度完整”且有效的 state 中創建具有依賴關系的實體。

甚至我要說的上面的例子也是一個壞例子。 即使它沒有觸發編譯器警告,父構造點的實體也不是完整且有效的 state。 如果我要做類似的事情:

使用 (var context = new MyContext()) { var parent = new Parent(); parent.Name = "我自己"; context.SaveChanges(); }

如果父級自動初始化子級,則 SaveChanges 將要保存該新子級,並且沒有任何東西可以確保設置子級上的所有必填字段,則 SaveChanges 調用將失敗。 孩子的 state 不夠完整。

我主張自動初始化的唯一地方是 collections:

public class Parent
{
    public virtual ICollection<Child> Children { get; internal set; } = new List<Child>();
}

以上仍然是“完整的”,因為如果我在不添加任何子項的情況下填充新父項,則空集合不會嘗試為子項保存任何內容。 這也很方便,因此當我創建新的父級時,如果我沒有子級,我可以選擇立即開始添加/關聯子級,而不會觸發 null 引用異常。

使用工廠方法初始化 object 圖形有助於確保實體始終在最低限度完整的 state 中創建,這意味着它們可以立即保存而不會出錯。 正如我上面提到的,我通常使用我的存儲庫作為實體工廠,因為它已經通過工作單元與 DbContext 連接起來,以根據需要解決依賴關系。

例如,如果我可以創建一個 Order 實體,該實體由訂單號、客戶參考以及保存有效訂單所需的產品的一個或多個訂單行組成,我的 OrderRepository 可能有一個類似這樣的 CreateOrder 方法:

public Order CreateOrder(int customerId, IEnumerable<OrderedProductViewModel> orderedProducts)
{
    if (!orderedProducts.Where(x => x.Quantity > 0).Any())
       throw new ArgumentException("No products selected.");

    var customer = Context.Customers.Single(x => x.CustomerId == customerId);
    var products = Context.Products.Where(x => orderedProducts.Where(o => o.Quantity > 0).Select(o => o.ProductId).Contains(x.ProductId)).ToList();
    if (products.Count() < orderedProducts.Count())
       throw new ArgumentException("Invalid products included in order.");
   var order = new Order 
   { 
      Customer = customer,
      OrderLines = orderedProducts.Select(x => new OrderLine 
      { 
         Product = products.Single(p => p.ProductId == x.ProductId),
         Quantity = x.Quantity
      }
   }
   Context.Orders.Add(order);
   return order;
}

這是我可能使用的工廠方法的上下文示例,以及一些基本驗證。 OrderedProductViewModel 有效地表示 ProductId 和 Quantity 的元組。 它與客戶 ID 一起代表我允許保存的訂單的最小 state。 在訂單被認為足夠完整可以發貨之前,可能會在此方法之外設置其他可選詳細信息,但工廠會確保它足夠完整以進行保存。

我可以調用如下代碼:

using (var contextScope = ContextScopeFactory.Create())
{
    var order = OrderRepository.Create(selectedCustomerId, selectedProducts);
    contextScope.SaveChanges();
}

那會很高興。 或者我可以在調用 SaveChanges 之前繼續在訂單上設置可用信息。 我的 OrderRepository 將沒有任何業務邏輯,因為該業務邏輯可能依賴於客戶端的配置,並且存儲庫沒有業務知道或關心它; 但它可能依賴於 IOrderValidator 之類的東西來傳遞新提議的訂單,業務邏輯可以通過該訂單來斷言訂單足夠有效以進行保存。 存儲庫/工廠斷言它足夠完整,但它可以綁定到業務邏輯中的驗證器以斷言它足夠有效。 (即最小/最大訂單大小/價值等)

這與 DDD 相結合,我將所有設置器都設置為internal並在我的實體上使用操作方法有助於控制確保我的實體始終保持在完整的 state 中。 我猜這是你試圖通過使用構造函數來確保的東西,所以我想我會分享上面的例子,以提供一些可能的想法和替代方案來實現這一點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM