NHibernate, Check Logic and AutoDirtyCheck
Here's my scenario:
I am using nhibernate by opening and closing a session in IHttpModule (PreRequestHandlerExecute and PostRequestHandlerExecute).
Ninject takes care of injecting my session into all of my repos, and I am very happy with it.
Now suppose an update for one of my objects (code simplified):
Controller:
User user = _userService.GetUser(id);
user.Name = "foo";
user.Email = "foo@bar.com";
user.Group = _groupService.GetGroup(idGroup);
if(_userService.Edit(user)) {
RedirectToAction("Index");
}
else {
return View(user);
}
Services:
if(ValidateUser(user) {
return _rep.Update(user);
}
return false;
The ValidateUser executes the validation logic and inserts any error into the IValidationDictionary, which is a wrapper around the ModelState in the controller.
So far so good, I get all errors (if any) and I can show them in the view.
Here's the problem:
When I try to save a user with an error (no name, for example) the _rep.Update (user) method is never called, but the user is saved anyway.
Approaching it, in my opinion, it is known that AutoDirtyCheck nhibernate means that if I modify an object in memory, it will persist automatically to the database.
A very powerful feature that I agree, but since my session ended in PostRequestHandlerExecute, my invalid object still persists, which I don't want.
I tried to remove this behavior using unhaddins , it worked, but then my child objects are not automatically saved when I only save the parent :(
So how do you solve this?
Make ValidadeUser public and validate copy before calling _userService.GetUser (id)?
Put your validation logic somewhere else? Maybe in an entity class? (I love it!).
Thank you very much in advance.
source to share
FYI - You can set the Nushernate Session FlushMode property FlushMode.Never
to have full control over when NHibernate will update the database. Maybe you can cheat and if the action is not allowed - never do a flash and the nhibernate session will die when the response is complete (if the session hasn't disappeared you should really check out the changed object, tho)
source to share
Personally, I call my validation code in mvc in defaultmodelbinder. My viewmodel (posted data) is checked before I do anything with it. I am using one validator class for each validation.
public class MyController : Controller
{
public ActionResult MyActionMethod(UserChangeModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("Index");
}
User user = _userService.GetUser(model.Id);
user.Name = model.Name;
user.Email = model.Email;
user.Group = _groupService.GetGroup(model.IdGroup);
return View(user);
}
}
public class MyDefaultModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var boundInstance = base.BindModel(controllerContext, bindingContext);
if (boundInstance != null)
{
var validator = findValidator(bindingContext.ModelType);
var errors = validator.Validate(boundinstance);
addErrorsToTheModelState(bindingContext, errors);
}
}
}
source to share
Fabio Molo has a very good recording / solution
http://fabiomaulo.blogspot.com/2009/03/ensuring-updates-on-flush.html
source to share
You can use ISession.Evict (obj) to remove the object from the session and this will prevent it from being saved automatically. It should be noted that this makes the object short-lived and trying to load any lazy initialized child objects (usually collections) will cause NH to throw a LazyInitializationException.
ETA: I just read your comment to Maurice that you cannot directly access the ISession. I am manually injecting ISession into the repository / service classes because it is needed for WinForms. There are several methods in ISession that I have had to access from time to time, especially Evict, Merge and Lock. I would expose ISession or a wrapper so you can use Evict.
source to share