简体   繁体   English

如何解决虚拟实体框架对象的“不要在构造函数中调用可覆盖的方法”警告?

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

I'm using Entity Framework and want to use lazy loading on properties, so I'm making the properties virtual .我正在使用实体框架并想对属性使用延迟加载,所以我将属性设置为virtual

An example:一个例子:

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

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

    public virtual Child Child { get; set; }
}

When I do that, I get a CA2214 warning:当我这样做时,我收到一个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

I'd like to remove this warning, but if I mark Parent as sealed I get the expected error:我想删除此警告,但如果我将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

So how can I resolve this warning (without ignoring it) and still use virtual ?那么如何解决这个警告(不忽略它)并仍然使用virtual

Use an initializer on the property rather than the constructor.在属性上使用初始化器而不是构造器。

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

Edit: Regarding the above and that编辑:关于上述和那个

...there are times when I would need to set properties for Child in the constructor... ...有时我需要在构造函数中为Child设置属性...

The simple rule is "you probably shouldn't".简单的规则是“你可能不应该”。 An entity's role is to represent data state for that entity, nothing more.实体的作用是代表该实体的数据 state,仅此而已。 Initializing an entity "graph" should not be done in a top-level entity's constructor, but rather using a Factory pattern.初始化实体“图”不应在顶级实体的构造函数中完成,而应使用工厂模式。 For instance, I use a Repository pattern with EF which I manage not only getters, but also serve as the factory providing Create methods, as well as handling Delete for soft-delete scenarios.例如,我使用带有 EF 的存储库模式,我不仅管理 getter,还充当提供 Create 方法的工厂,以及处理软删除场景的 Delete。 This helps ensure that an entity with dependencies is always created in a "minimally complete" and valid state.这有助于确保始终在“最低限度完整”且有效的 state 中创建具有依赖关系的实体。

Even the above example I would say is a bad example.甚至我要说的上面的例子也是一个坏例子。 Even though it doesn't trip the compiler warning, the entity at the point of a parent's construction isn't in a complete and valid state.即使它没有触发编译器警告,父构造点的实体也不是完整且有效的 state。 If I were to do something like:如果我要做类似的事情:

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

If the Parent auto-initializes a Child, that SaveChanges will want to save that new Child, and there is nothing that ensured that all required fields on the child are set, that SaveChanges call will fail.如果父级自动初始化子级,则 SaveChanges 将要保存该新子级,并且没有任何东西可以确保设置子级上的所有必填字段,则 SaveChanges 调用将失败。 Child isn't in a complete enough state.孩子的 state 不够完整。

The only place I would advocate auto-initializing would be collections:我主张自动初始化的唯一地方是 collections:

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

The above is still "complete" in that an empty collection won't attempt to save anything for children if I populate a new parent without adding any children.以上仍然是“完整的”,因为如果我在不添加任何子项的情况下填充新父项,则空集合不会尝试为子项保存任何内容。 It is also convenient so that when I create a new parent, I have the option to immediately start adding/associating children without tripping a null reference exception if I had no children.这也很方便,因此当我创建新的父级时,如果我没有子级,我可以选择立即开始添加/关联子级,而不会触发 null 引用异常。

To initialize an object graph with a factory method helps ensure that entities are always created in a minimally complete state, which means they can be saved immediately without error.使用工厂方法初始化 object 图形有助于确保实体始终在最低限度完整的 state 中创建,这意味着它们可以立即保存而不会出错。 As I mentioned above, I generally use my Repository to serve as the entity factory since it's already wired up with the DbContext through the unit of work to resolve dependencies as needed.正如我上面提到的,我通常使用我的存储库作为实体工厂,因为它已经通过工作单元与 DbContext 连接起来,以根据需要解决依赖关系。

As an example if I have an Order entity that I can create that consists of an order number, customer reference, and one or more order lines for products which are required to save a valid order, my OrderRepository might have a CreateOrder method something like this:例如,如果我可以创建一个 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;
}

This is a contextual example of a factory method I might use, and some of the basic validation.这是我可能使用的工厂方法的上下文示例,以及一些基本验证。 OrderedProductViewModel represents effectively a tuple of a ProductId and a Quantity. OrderedProductViewModel 有效地表示 ProductId 和 Quantity 的元组。 It along with a Customer ID represent the minimum state of an order I would allow to be saved.它与客户 ID 一起代表我允许保存的订单的最小 state。 There may be other optional details that might be set outside of this method before an Order is considered complete enough to ship, but the factory ensures it is complete enough to save.在订单被认为足够完整可以发货之前,可能会在此方法之外设置其他可选详细信息,但工厂会确保它足够完整以进行保存。

I could have calling code like:我可以调用如下代码:

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

And that would be happy.那会很高兴。 Or I could continue to set available information on the order before calling SaveChanges.或者我可以在调用 SaveChanges 之前继续在订单上设置可用信息。 My OrderRepository would not have any business logic because that business logic may be dependent on configuration for the client and the repository has no business knowing or caring about that;我的 OrderRepository 将没有任何业务逻辑,因为该业务逻辑可能依赖于客户端的配置,并且存储库没有业务知道或关心它; but it could have a dependency for something like an IOrderValidator to pass the newly proposed Order to which the business logic could run across to assert that the Order is valid enough to be saved.但它可能依赖于 IOrderValidator 之类的东西来传递新提议的订单,业务逻辑可以通过该订单来断言订单足够有效以进行保存。 The repoistory/factory asserts it is complete enough, but it can be tied to a validator back in the business logic to assert it is valid enough.存储库/工厂断言它足够完整,但它可以绑定到业务逻辑中的验证器以断言它足够有效。 (Ie minimum/ maximum order size / value, etc.) (即最小/最大订单大小/价值等)

This coupled with DDD where I make all setters internal and use action methods on my entities helps control ensuring that my entities are always maintained in a complete state.这与 DDD 相结合,我将所有设置器都设置为internal并在我的实体上使用操作方法有助于控制确保我的实体始终保持在完整的 state 中。 I'm guessing this is something you are trying to ensure through using the constructors so I thought I'd share the above as an example to provide some possible ideas and alternatives to accomplish that.我猜这是你试图通过使用构造函数来确保的东西,所以我想我会分享上面的例子,以提供一些可能的想法和替代方案来实现这一点。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么不在构造函数中调用可覆盖的方法? - Why do not call overridable methods in constructors? MSOCAF验证-不要在构造函数中调用可重写的方法 - MSOCAF Verification - Do not call overridable methods in constructors 数据库第一个自动类导致CA2214:不要在构造函数中调用可覆盖的方法 - Database first automatic class causes CA2214: Do not call overridable methods in constructors 在构造函数中访问已实现的抽象属性会导致CA2214:不要在构造函数中调用可覆盖的方法 - Accessing an implemented abstract property in the constructor causes CA2214: Do not call overridable methods in constructors 可覆盖的方法不能是静态的:我怎样才能做我想做的事情? - Overridable methods cannot be static: How else can I do what I'm trying to do? 如何关联定制的虚拟对象以链接到像实体框架一样工作的关系对象? - How do I associate custom made virtual objects to link to relational objects working like Entity Framework? 构造函数中的可重写方法-帮助修复 - Overridable Methods In Constructors -Help to Fix 如何在Linq中调用Entity Framework中的函数方法并保持分页? - How do I call function methods in Linq to Entity Framework and keep pagination? 实体框架4.1:创建新实体后如何访问虚拟实体属性? - Entity Framework 4.1: How can I access virtual entity properties after creating a new entity? 如何使用Entity Framework 6在字段中存储对象列表 - How do I store a list of objects in field using Entity Framework 6
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM