Breeze 在BeforeSaveEntity中使用此.Context

Breeze 在BeforeSaveEntity中使用此.Context,breeze,Breeze,我一直在寻找一种在BeforeSaveEntity方法中组织验证规则的好方法,我在项目:BreezeMvcSPATemplate: // A second DbContext for db access during custom save validation. // "this.Context" is reserved for Breeze save only! 为什么不能使用这个.Context?很好的问题。答案并不明显,也不容易简单介绍。我试试看 EFContextProvider从

我一直在寻找一种在BeforeSaveEntity方法中组织验证规则的好方法,我在项目:BreezeMvcSPATemplate:

// A second DbContext for db access during custom save validation. 
// "this.Context" is reserved for Breeze save only!

为什么不能使用这个.Context?

很好的问题。答案并不明显,也不容易简单介绍。我试试看

EFContextProvider
从客户端获取保存的数据,并(最终)将这些数据转换为
EFContextProvider.Context
中的实体。批准保存后,
EFContextProvider
在此EF
Context
上调用
SaveChanges
方法,并将其所有内容保存为单个事务

有两个潜在问题

1.数据完整性和安全性 客户端数据永远无法完全信任。如果您的业务规则限制了授权用户可以看到或更改的内容,则必须将客户机派生的实体与数据库中相应的实体进行比较

EF
上下文
不能包含“同一实体”的两个副本。它不能保存具有相同密钥的两个实体。因此,您不能同时使用
EFContextProvider.Context
从数据库中获取干净的副本和保存有更改的副本

您需要第二个
上下文
来获取干净的副本,并且您必须编写逻辑来比较要保存在
EFContextProvider.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
禁用延迟加载,主要是为了在序列化过程中中断循环引用,同时也为了防止服务器性能下降

您可以通过随意加载任何实体或相关实体来解决这个问题。但是接下来您遇到了第二个问题:您加载以进行验证的实体可能与您试图保存在此批处理中的另一个实体冲突

EFContextProvider
不会一次全部填充其
上下文。它开始逐个验证实体,并将它们添加到
上下文中

继续我们的示例,假设我们在验证期间加载了
someItem
的父订单。该顺序现在位于
EFContextProvider.Context

保存过程继续到下一个实体并。。。惊喜,惊喜。。。下一个实体恰好是相同的父订单。
EFContextProvider
尝试将此副本附加到已经有副本(我们刚刚加载的副本)的
上下文中。。。不可能

有冲突。这两个订单中的哪一个属于
EFContextProvider
?我们刚刚加载的用于验证目的的干净副本。。。还是来自客户机的修改要保存的文件

也许你认为你知道答案。也许我同意。但事实是,
EFContextProvider
抛出了一个异常,因为在
上下文
中已经存在该键的顺序

结论 如果所有验证都是自一致性检查,则只需要
EFContextProvider.Context
。您不必创建第二个
上下文

但是如果您有涉及其他实体的数据安全问题和/或业务逻辑,则需要第二个
上下文
。。。您需要足够的EF技能来使用
上下文


这不是Breeze或实体框架的限制。无论您选择何种技术,非平凡的业务逻辑都需要相当的服务器端复杂性。这就是野兽的本性。

你的回答非常透彻,但我还是遗漏了一些东西。ListTodo和UserProfile之间的关系似乎是应用程序外键,而不是数据库外键。这和Breeze有关吗?没有。这只是一个从微软最初的例子中翻出的例子。查看DocCode示例中的
NorthwindEntitySaveGuard
(它位于DocCode.DataAccess.EF项目中);关注
ReadContext
。FWIW