我正在寻找一种在 BeforeSaveEntity 方法中组织验证规则的好方法,我在文件中找到了这条评论:项目中的 TodoContextProvider.cs:BreezeMvcSPATemplate:
// A second DbContext for db access during custom save validation.
// "this.Context" is reserved for Breeze save only!
为什么this.Context不能用?
我正在寻找一种在 BeforeSaveEntity 方法中组织验证规则的好方法,我在文件中找到了这条评论:项目中的 TodoContextProvider.cs:BreezeMvcSPATemplate:
// A second DbContext for db access during custom save validation.
// "this.Context" is reserved for Breeze save only!
为什么this.Context不能用?
很好的问题。答案并不明显,也不容易简单介绍。我会尝试。
从EFContextProvider
客户端获取保存数据并(最终)将这些数据转换为EFContextProvider.Context
. 批准保存后,EFContextProvider
调用SaveChanges
此 EF 上的方法Context
并将其所有内容保存为单个事务。
有两个潜在的问题。
客户数据永远无法完全信任。如果您的业务规则限制授权用户可以查看或更改的内容,则必须将客户端派生实体与数据库中的相应实体进行比较。
EFContext
不能包含“同一实体”的两个副本。它不能容纳具有相同密钥的两个实体。因此,您不能同时使用EFContextProvider.Context
两者来从数据库中获取干净的副本并保存带有更改的副本。
您需要一秒钟Context
来获得干净的副本,并且您必须编写逻辑来比较要保存的实体的关键值和EFContextProvider.Context
第二个中的干净实体的值Context
。
许多验证不需要将值与干净的实体进行比较。
例如,开箱即用的 System.ComponentModel.DataAnnotations
属性,例如Required
和MaxLength
是简单的数据验证,用于确定实体是否是自洽的。要么有价值,要么没有。该值小于最大长度或不是。对于此类测试,您不需要比较实体。
您可以编写自己的自定义System.ComponentModel.DataAnnotations
属性来比较单个实体中的数据值。您可能有一条规则说order.InvoiceDate
must be on-or-before order.ShipDate
。这也是一个自我一致性测试,您也不需要比较实体。
如果这些是您关心的唯一验证类型 - 并且您正在使用 EF DbContext
- 您可以让 EF 在其保存处理期间为您运行它们。你不需要第二个Context
。
但是跨实体验证是另一回事。在跨实体验证中,实体“A”仅在实体“B”(可能还有“C”、“D”、“E”……)的某些条件为真时才有效。例如,您可能要求订单项目具有已在数据库中的父订单。
EFContextProvider.Context
在您验证订单项目时,父订单很可能不在其中。
“没问题,”你说。“我将使用 导航到父级someItem.Order
。”
你不能。首先,它不起作用,因为EFContextProvider.Context
. 禁用延迟加载主要是为了EFContextProvider
在序列化过程中破坏循环引用,同时也是为了防止性能杀死服务器上的“n+1”错误。
您可以通过随意加载任何实体或相关实体来解决此问题。但是随后您遇到了第二个问题:您加载以进行验证的实体可能与您尝试在此批次中保存的另一个实体发生冲突。
EFContextProvider
不会Context
一次全部填充。它开始一个一个地验证实体,并将它们添加到其中Context
。
someItem
继续我们的示例,假设我们在验证期间加载了父订单。该订单现在在EFContextProvider.Context
.
保存过程继续到下一个实体,然后......惊喜,惊喜......下一个实体恰好是同一个父订单。EFContextProvider
尝试将此副本附加到已经有副本的副本(我们刚刚加载的Context
副本)......它不能。
有冲突。这两个订单中的哪一个属于EFContextProvider
?我们刚刚加载的用于验证目的的干净副本……还是来自客户端的要保存修改的副本?
也许你认为你知道答案。也许我同意。但事实是,EFContextProvider
引发异常,因为Context
.
如果您的所有验证都是自一致性检查,那么这EFContextProvider.Context
就是您所需要的。您不必再创建第二个Context
但是,如果您有涉及其他实体的数据安全问题和/或业务逻辑,则需要第二个Context
......并且您需要足够的 EF 技能来使用它Context
。
这不是 Breeze 或 Entity Framework 的限制。无论您选择哪种技术,重要的业务逻辑都需要相当的服务器端复杂性。这就是野兽的本性。