Midbook pattern crisis: Do NOT reach for that pattern
From a question in the mailing list:
How do you do to "rollback" an entity state supposing the following scenario.
public JsonResult Reschedule(string id, DateTime anotherDate) { try { var dinner = session.Load<Dinner>(id); dinner.ChangeDate(anotherDate); schedulerService.Schedule(dinner); return Json(new { Message = "Bon apetit!" }); } catch (DinnerConcurrencyException ex) { return Json(new { Message = ex.Message }); } } // Base controller protected void OnActionExecuted(ActionExecutedContext filterContext) { if(filterContext.Exception != null) return; session.SaveChanges(); }
The problem that I have is when the schedulerService throws a DinnerConcurrencyException, it is catched at the controller.
After all, the OnActionExecuted will call SaveChanges and persist the dinner with an invalid state.
The next post was:
I have already tried to use Memento Pattern like this:
try { var dinner = session.Load<Dinner>(id); dinner.SaveState(); dinner.ChangeDate(anotherDate); schedulerService.Schedule(dinner); return Json(new { Message = "Bon apetit!" }); } catch (DinnerConcurrencyException ex) { dinner = dinner.RestoreState(); return Json(new { Message = ex.Message }); }But didn't work, beacuse I think Raven has proxied my dinner instance.
And here is the Memento implementation:
[Serializable] public abstract class Entity { MemoryStream stream = new MemoryStream(); public void SaveState() { new BinaryFormatter().Serialize(stream, this); } public T RestoreState<T>() { stream.Seek(0, SeekOrigin.Begin); object o = new BinaryFormatter().Deserialize(stream); stream.Close(); return (T)o; } }
You might notice that this create a new instance when you call RestoreState, and that has no impact whatsoever on the actual instance that is managed by RavenDB.
The suggested solution?
catch (DinnerConcurrencyException ex) { SkipCallingSaveChanges = true; return Json(new { Message = ex.Message }); }
No need for patterns here.

Comments
Comment preview