Вот мой сценарий:
Я использую nhibernate, открывая и закрывая сеанс в IHttpModule (PreRequestHandlerExecute и PostRequestHandlerExecute).
Ninject заботится о том, чтобы вводить мою сессию во все мои репозитории, и я очень доволен ею.
Теперь предположим, что обновление для одного из моих объектов (код упрощен):
Контроллер:
User user = _userService.GetUser(id);
user.Name = "foo";
user.Email = "[email protected]";
user.Group = _groupService.GetGroup(idGroup);
if(_userService.Edit(user)) {
RedirectToAction("Index");
}
else {
return View(user);
}
Услуги:
if(ValidateUser(user) {
return _rep.Update(user);
}
return false;
ValidateUser выполняет логику проверки и вставляет любую ошибку в документ IValidationDictionary, который является оболочкой для ModelState в контроллере.
Пока все хорошо, я получаю все ошибки (если есть), и я могу показать их в представлении.
Здесь возникает проблема:
Когда я пытаюсь сохранить пользователя с ошибкой (нет имени, например), метод _rep.Update(пользователь) никогда не вызывается, но пользователь все равно сохраняется.
Приближение к нему, по-моему, известно, что AutoDirtyCheck nhibernate означает, что если я изменю объект в памяти, он будет сохраняться автоматически в базе данных.
Очень мощная функция, которую я согласен, но так как мой сеанс завершен в PostRequestHandlerExecute, мой недействительный объект все равно сохраняется, чего я не хочу.
Я попытался удалить это поведение, используя unhaddins, он работал, но тогда мои дочерние объекты не сохраняются автоматически, когда я сохраняю только родительский: (
Итак, как это решить?
Сделать ValidadeUser общедоступным и проверять копию перед вызовом _userService.GetUser(id)?
Поместите логику проверки в другое место? Может быть, в классе сущности? (Мне это очень нравится!).
Большое спасибо заранее.
FYI - вы можете установить для свойства Nushernate Session FlushMode значение FlushMode.Never
, чтобы полностью контролировать, когда NHibernate будет обновлять базу данных. Возможно, вы можете обманывать и если действие не разрешено - никогда не делайте флеш, и сеанс nhibernate будет умирать, когда ответ завершен (если сеанс не исчез, вы действительно должны выселить измененный объект, tho)
У Фабио Моло очень хорошая запись/решение
http://fabiomaulo.blogspot.com/2009/03/ensuring-updates-on-flush.html
Вы можете использовать ISession.Evict(obj), чтобы удалить объект из сеанса, и это предотвратит его автоматическое сохранение. Следует отметить, что это делает объект кратковременным и пытается загрузить любые ленивые инициализированные дочерние объекты (как правило, коллекции) заставит NH вызывать исключение LazyInitializationException.
ETA: Я просто прочитал ваш комментарий Морису, что вы не можете напрямую обращаться к ISession. Я вручную внедряю ISession в классы репозитория/службы, потому что это необходимо для WinForms. В ISession существует несколько методов, с которыми мне приходилось время от времени получать доступ, особенно Evict, Merge и Lock. Я бы разоблачил ISession или обертку, чтобы вы могли использовать Evict.
Я не пробовал это сам, но первое, что приходит на ум, - это отсоединить недопустимый пользовательский объект. Если вы не выполните другой запрос, который будет получать один и тот же пользователь, он также вернет тот же объект, теперь недействительный.
Лично я вызываю свой код проверки в mvc в defaultmodelbinder. Моя viewmodel (размещенные данные) проверяется, прежде чем я что-нибудь с ней сделаю. Я использую один класс валидатора для каждой проверки.
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);
}
}
}