在BeforeSaveEntity内部使用this.Context [英] Using this.Context inside BeforeSaveEntity

查看:77
本文介绍了在BeforeSaveEntity内部使用this.Context的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在寻找一种在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆