如何使用 Castle ActiveRecord / NHibernate 防止在 Flush 上将对象更改写入数据库

发布于 2024-07-08 14:41:51 字数 398 浏览 4 评论 0原文

NHibernate 的默认行为是在调用 Session.Flush() 时将对对象的所有更改写入数据库。 无论你是否愿意,它都会这样做。

当我们需要执行诸如验证业务规则或输入之类的操作时,如何防止将错误数据写入数据库?

例如..

  • 客户名称不为空。
  • 用户打开 Web 浏览器(无 JavaScript)并删除客户名称。
  • 点击更新。
  • Customer.Name 属性已更新,并且 ..
  • Customer.IsValid() 被调用。
  • 即使 IsValid() 为 false 并且我们显示错误消息,NHibernate 仍然会更新数据库。

The default behavior of NHibernate is the write all changes to objects to the database when Session.Flush() is called. It does this whether you want it to or not.

How do we prevent writing bad data to the database when we need to do things like validate business rules or input?

For instance ..

  • Customer Name is not null.
  • User opens a web browser (without javascript) and deletes the customer name.
  • Hits update.
  • Customer.Name property is updated and ..
  • Customer.IsValid() is called.
  • Even if IsValid() is false and we show error messages NHibernate still updates the database.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

尐籹人 2024-07-15 14:41:51

特别针对 ActiveRecord:如果您自己不更改 SessionScope,AR 默认采用 session-per-call 的会话管理模式,其中为 ActiveRecordMediator 的每个操作创建一个新会话。 因此,一旦您检索到所有对象,它们就已经与其父会话断开连接。 更改不会持久,直到/除非您调用 Save(或 SaveAndUpdate,甚至 Update),这在每次调用会话模式中,将创建一个会话,将您要保存的对象附加到该会话,调用 Save,然后然后处理会话(导致刷新并因此写入更改)。

当您以这种方式使用 AR 时,它会完全按照您想要的方式执行(即,除非您显式调用“保存”,否则不会写回任何更改)。 但这明显违背了预期的 NHibernate 行为,并且您无法进行延迟加载或充分利用缓存。 我已经在一些 Web 应用程序中使用了这种模式,但它们是预先为急切加载而设计的,并且相当多的持久对象实际上是不可变的和静态的,可以在启动时集体加载并缓存,而无需连接到任何对象。会议。 如果您的应用程序不适合此模型,那么每次调用会话可能是一个坏主意。

由于您似乎正在使用 UOW 模式,因此您无法利用此行为。 因此,您必须从 NHibernate 会话中逐出该对象(并且访问真实的 ISession 实例实际上并不像 AR 中看起来那么容易),或者更改应用程序的工作方式,以便持久对象的属性实际上不会被修改直到您的业务规则得到验证之后。

Specifically for ActiveRecord: if you don't change the SessionScope yourself, AR defaults to a session management pattern of session-per-call, where a new session is created for every operation of ActiveRecordMediator. Thus all objects you retrieve are already disconnected from their parent session once you retrieve them. Changes will not be persisted until / unless you call Save (or SaveAndUpdate, or even Update) which, in a session-per-call pattern, will create a session, attach the object you're saving to that session, call Save, and then dispose the session (causing a Flush and thus the writing of changes).

When you use AR this way, it does exactly what you seem to want (ie no changes are written back unless you explicitly call Save). But this explicitly goes against the expected NHibernate behaviour, and you can't do lazy loading or get much use out of caching. I have used this pattern for some Web apps, but they are designed up-front for eager loading and quite a lot of the persistent objects are effectively immutable and static and can be loaded en-masse at startup time and cached without a connection to any session. If your app doesn't suit this model then session-per-call is probably a bad idea.

Since you seem to be using a UOW pattern, you can't take advantage of this behaviour. Thus you must either evict the object from the NHibernate session (and getting access to the real ISession instance is actually not quite as easy as it seems in AR), or change the way your app works so that properties of persisted objects are not actually modified until after your business rules have been validated.

怪我太投入 2024-07-15 14:41:51

NHibernate 的默认行为是在调用 Session.Flush() 时将对对象的所有更改写入数据库。 无论您是否愿意,它都会这样做。

如果您不希望 NHibernate 刷新会话,那么为什么要告诉它刷新会话呢? Flush()WriteAllChangesToObjectsToTheDatabase() 的简写。

当我们需要执行诸如验证业务规则或输入之类的操作时,如何防止将错误数据写入数据库?

  • 在修改模型对象之前验证更改。 不允许内存中存在无效的模型对象(从模型公开的公共 API 的角度来看,并且排除多线程场景)。
  • 将您的更改包装在事务中,如果模型对象的验证失败,事务将会失败(为什么您首先允许无效的模型对象?)。

The default behavior of NHibernate is the write all changes to objects to the database when Session.Flush() is called. It does this whether you want it to or not.

If you don't want NHibernate to flush the session, then why are you telling it to flush the session? Flush() is shorthand for WriteAllChangesToObjectsToTheDatabase().

How do we prevent writing bad data to the database when we need to do things like validate business rules or input?

  • Validate the changes before modifying the model objects. Don't allow invalid model objects to exist in memory (from the perspective of the public API which your model exposes, and excluding multithreading scenarios).
  • Wrap your changes in a transaction, which will fail if validation of your model objects fails (why are you allowing invalid model objects in the first place?).
西瓜 2024-07-15 14:41:51

所以,我知道这完全是矫枉过正,但这是我解决它的方法。

转换为 NHibernate 2.0 并使用 NHibernate Validator 项目来运行我的输入验证规则。 我还没有处理业务规则,但我想我可以使用自定义验证器规则来完成它们,如果这不起作用,我可以使用 nhibernate 事件。

由于我已经在使用存储库模式,因此转换非常简单。 只花了大约 4-5 个小时,而且我们的模型非常广泛。 能够从 AR 属性生成 .xml 文件可以节省大量时间。

So, I know this is completely overkill but here's how I fixed it.

Converted to NHibernate 2.0 and used the NHibernate Validator project to run my input validation rules. I haven't handled the business rules yet but I think I can do them with custom Validator rules, if that doesn't work I can use the nhibernate events.

Since I was already using the repository pattern the conversion was incredibly simple. It only took about 4-5 hours and our model is pretty extensive. Being able to generate the .xml files from the AR attributes was a huge time saver.

心作怪 2024-07-15 14:41:51

您还可以将会话与 FlushAction.Never 一起使用。 即:

SessionScope session = new SessionScope(FlushAction.Never);

这会将默认行为从自动保存所有内容切换为您明确需要在实体上调用 .Save() 。 所以你可以做任何你需要做的验证,然后才保存你想要的......

You can also use a Session with FlushAction.Never. Ie:

SessionScope session = new SessionScope(FlushAction.Never);

This will switch the default behaviour from automatically saving everything to you explicitly needing to call .Save() on your entities. So you can do whatever validation you need to do and only then save what you want to...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文