在BeforeSaveEntity内部使用this.Context [英] Using this.Context inside BeforeSaveEntity
问题描述
我一直在寻找一种在BeforeSaveEntity方法中组织验证规则的好方法,并且我在文件中找到以下注释:项目内的TodoContextProvider.cs:BreezeMvcSPATemplate:
//自定义保存验证期间用于数据库访问的第二个DbContext。
// this.Context仅保留给Breeze保存!
为什么不能使用此上下文?
很好的问题。答案并不明显,简短地讲一下也不容易。我会尝试的。
EFContextProvider
从客户端获取保存数据,(最终)将这些数据转换为<$内的实体c $ c> EFContextProvider.Context 。批准保存后, EFContextProvider
在此EF Context 中调用
及其所有内容都保存为单个事务。 SaveChanges
方法
存在两个潜在的问题。
1。数据完整性和安全性
永远无法完全信任客户端数据。如果您有限制授权用户查看或更改内容的业务规则,则必须将客户端派生的实体与数据库中的相应实体进行比较。
EF 上下文
不能包含相同实体的两个副本。它不能容纳两个具有相同密钥的实体。因此,您既不能使用 EFContextProvider.Context
从数据库中获取干净副本,也不能使用更改来保存副本。
您将需要第二个 Context
以获得干净的副本,并且必须编写逻辑以比较要保存的实体在$中的关键值。 c $ c> EFContextProvider.Context 和第二个 Context
中干净实体的值。
2。跨实体验证
许多验证不需要将值与干净实体进行比较。
例如,框 System.ComponentModel.DataAnnotations
属性,例如 Required
和 MaxLength
是简单的数据验证,用于确定实体是否自洽。有一个值或没有一个值。该值小于或等于最大长度。此类测试不需要比较实体。
您可以编写自己的自定义 System.ComponentModel.DataAnnotations
属性 内的单个实体的数据值。您可能有一条规则,规定 order.InvoiceDate
必须在 order.ShipDate
之前或之前。这也是一种自我一致性测试,您也不需要该实体的比较实体。
如果您只关心这些验证类型,关于-并且您正在使用EF DbContext
-您可以让EF在其保存过程中为您运行它们。您不需要第二个上下文
。
但是跨实体验证是另一回事。在跨实体验证中,仅当实体 B(或 C, D, E,...)的某些条件为真时,实体 A才有效。例如,您可能需要一个订单项具有一个已经在数据库中的父订单。
很有可能父订单不在 EFContextProvider.Context
在验证订单商品时。
没问题,你说。 我将使用 someItem.Order
导航到父项。
不行。首先,由于 EFContextProvider.Context
禁用了延迟加载,因此无法使用。 EFContextProvider
禁用延迟加载主要是为了在序列化期间中断循环引用,还可以防止性能中断 n + 1;错误。
您可以通过任意加载任何实体或相关实体来解决此问题。但是,您遇到了第二个问题:为验证而加载的实体可能与您要保存在该批处理中的另一个实体发生冲突。
EFContextProvider
不会一次填充其 Context
。它开始一个一个地验证实体,然后将它们添加到 Context
中。
继续我们的示例,假设我们验证期间已加载 someItem
的父订单。现在,该订单位于 EFContextProvider.Context
。
保存过程继续到下一个实体,并且……令人惊讶,惊讶.. 。下一个实体恰好是相同的父订单。 EFContextProvider
尝试将此副本附加到 Context
中,该副本已具有副本(我们刚刚加载的副本)。
存在冲突。这两个订单中的哪个属于 EFContextProvider
?我们刚刚出于验证目的而加载的干净副本……还是来自客户端的要保存修改的副本?
也许您认为您知道答案。也许我同意。但是事实是, EFContextProvider
引发异常,因为在 Context
中已经存在带有该键的订单。 / p>
结论
如果所有验证都是自洽性检查,则 EFContextProvider.Context
是您所需要的。您不必创建第二个上下文
但是如果您有数据安全问题和/或业务逻辑涉及其他实体,您需要第二个 Context
...,并且您将需要足够的EF技能才能使用该 Context
。
这不是Breeze或实体框架的限制。无论您选择哪种技术,非平凡的业务逻辑都需要相当的服务器端复杂性。那就是野兽的本质。
I was looking for a good way to organize validation rules within BeforeSaveEntity method and I have found this comment in the file: TodoContextProvider.cs within the project: BreezeMvcSPATemplate:
// A second DbContext for db access during custom save validation.
// "this.Context" is reserved for Breeze save only!
Why this.Context can not be used?
Excellent question. The answer isn't obvious and it's not easy to cover briefly. I will try.
The EFContextProvider
takes the save data from the client and (ultimately) turns these data into entities within the EFContextProvider.Context
. When the save is approved, the EFContextProvider
calls the SaveChanges
method on this EF Context
and all of its contents are saved as a single transaction.
There are two potential problems.
1. Data integrity and security
Client data can never be fully trusted. If you have business rules that limit what an authorized user can see or change, you must compare the client-derived entity to the corresponding entity from the database.
An EF Context
cannot contain two copies of the "same entity". It can't hold two entities with the same key. So you can't use the EFContextProvider.Context
both to fetch the clean copy from the database and to hold the copy with changes.
You'll need a second Context
to get the clean copy and you'll have to write logic to compare the critical values of the entity-to-save in the EFContextProvider.Context
with the values of the clean entity in the second Context
.
2. Cross-entity validation
Many validation do not require comparison of values with a clean entity.
For example, the out-of-the-box System.ComponentModel.DataAnnotations
attributes, such as Required
and MaxLength
are simple data validations to determine if an entity is self-consistent. Either there is a value or there is not. The value is less than the maximum length or it is not. You don't need a comparison entity for such tests.
You could write your own custom System.ComponentModel.DataAnnotations
attributes that compare data values within a single entity. You might have a rule that says that order.InvoiceDate
must be on-or-before order.ShipDate
. that is also a self-consistency test and you won't need a comparison entity for that one either.
If these are the only kinds of validation you care about - and you're using an EF DbContext
- you can let EF run them for you during its save processing. You won't need a second Context
.
But cross entity validations are another story. In a cross-entity validation, entity 'A' is valid only when some condition is true for entity 'B' (and perhaps 'C', 'D', 'E', ...). For example, you may require that an order item have a parent order that is already in the database.
There is an excellent chance that the parent order is not in the EFContextProvider.Context
at the time you are validating the order item.
"No problem," you say. "I'll just navigate to the parent with someItem.Order
."
No you cannot. First, it won't work because lazy loading is disabled for the EFContextProvider.Context
. The EFContextProvider
disables lazy loading mostly to break circular references during serialization but also to prevent performance killing "n+1" bugs on the server.
You can get around that by loading any entity or related entities at will. But then you hit the second problem: the entity you load for validation could conflict with another entity that you are trying to save in this batch.
The EFContextProvider
doesn't populate its Context
all at once. It starts validating the entities one-by-one, adding them to the Context
as it goes.
Continuing our example, suppose we had loaded the parent order for someItem
during validation. That order is now in EFContextProvider.Context
.
The save process continues to the next entity and ... surprise, surprise ... the next entity happens to be the very same parent order. The EFContextProvider
tries to attach this copy to the Context
which already has a copy (the one we just loaded) ... it can't.
There's a conflict. Which of the two orders belongs in the EFContextProvider
? The clean copy we just loaded for validation purposes ... or the one that came from the client with modifications to be saved?
Maybe you think you know the answer. Maybe I agree. But the fact is, the EFContextProvider
throws an exception because there is already an order with that key in the Context
.
Conclusion
If all your validations are self-consistency checks, the EFContextProvider.Context
is all you need. You won't have to create a second Context
But if you have data security concerns and/or business logic that involves other entities, you need a second Context
... and you'll need sufficient EF skills to use that Context
.
This is not a limitation of Breeze or the Entity Framework. Non-trivial business logic demands comparable server-side complexity no matter what technology you choose. That's the nature of the beast.
这篇关于在BeforeSaveEntity内部使用this.Context的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!