How to prevent writing object changes to the database on Flush with Castle ActiveRecord / NHibernate

StackOverflow https://stackoverflow.com/questions/310286

Question

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.
Was it helpful?

Solution

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.

OTHER TIPS

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?).

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.

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...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top