NHibernate: how to manage entity-based validation using a session-by-request model, without ISession controllers


What is the best way to do entity-based validation (each entity class has an IsValid() method that validates its internal members) in ASP.NET MVC, with a "session-per-request" model, where the controller has zero (or limited) knowledge of the ISession? Here's the pattern I'm using:

  1. Get an entity by ID, using an IFooRepository that wraps the current NH session. This returns a connected entity instance.

  2. Load the entity with potentially invalid data, coming from the form post.

  3. Validate the entity by callings its IsValid() method.

  4. If valid, call IFooRepository.Save(entity), which delegates to ISession.Save(). Otherwise, display error message.

The session is currently opened when the request begins and flushed when the request ends. Since my entity is connected to a session, flushing the session attempts to save the changes even if the object is invalid.

What's the best way to keep validation logic in the entity class, limit controller knowledge of NH, and avoid saving invalid changes at the end of a request?

Option 1: Explicitly evict on validation failure, implicitly flush: if the validation fails, I could manually evict the invalid object in the action method. If successful, I do nothing and the session is automatically flushed.

Con: error prone and counter-intuitive ("I didn't call .Save(), why are my invalid changes being saved anyways?")

Option 2: Explicitly flush, do nothing by default: By default I can dispose of the session on request end, only flushing if the controller indicates success. I'd probably create a SaveChanges() method in my base controller that sets a flag indicating success, and then query this flag when closing the session at request end.

Pro: More intuitive to troubleshoot if dev forgets this step [relative to option 1]

Con: I have to call IRepository.Save(entity)' and SaveChanges().

Option 3: Always work with disconnected objects: I could modify my repositories to return disconnected/transient objects, and modify the Repo.Save() method to re-attach them.

Pro: Most intuitive, given that controllers don't know about NH.

Con: Does this defeat many of the benefits I'd get from NH?

Option 1 without a doubt. It's not counter intuitive, it's how NH works. Objects retrieved using NH are persistent and changes will be saved when the session is flushed. Calling Evict makes the object transient which is exactly the behavior you want.

You don't mention it but another option might be to use Manual or Commit FlushMode.