[英]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.