如何使用 Castle ActiveRecord / NHibernate 防止在 Flush 上将对象更改写入数据库
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
特别针对 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.
使用 ISession.Evict(objectToEvict) 方法驱逐无效对象。
请参阅:http://www.tobinharris.com/2007/2/3/ nhibernate 常见问题解答
和
http://www.surcombe.com/nhibernate-1.2/api/ html/M_NHibernate_ISession_Evict.htm
Use the ISession.Evict(objectToEvict) method to evict invalid objects.
See: http://www.tobinharris.com/2007/2/3/nhibernate-faq
and
http://www.surcombe.com/nhibernate-1.2/api/html/M_NHibernate_ISession_Evict.htm
如果您不希望 NHibernate 刷新会话,那么为什么要告诉它刷新会话呢?
Flush()
是WriteAllChangesToObjectsToTheDatabase()
的简写。If you don't want NHibernate to flush the session, then why are you telling it to flush the session?
Flush()
is shorthand forWriteAllChangesToObjectsToTheDatabase()
.所以,我知道这完全是矫枉过正,但这是我解决它的方法。
转换为 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.
您还可以将会话与 FlushAction.Never 一起使用。 即:
这会将默认行为从自动保存所有内容切换为您明确需要在实体上调用 .Save() 。 所以你可以做任何你需要做的验证,然后才保存你想要的......
You can also use a Session with FlushAction.Never. Ie:
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...