3

I'm using POCOs (class objects) for every type of data when in memory. I have a myraid set of functions that operate on these and modify them in different ways. What is a simple way to achieve undo/redo functionality with minimal changes to the code? Some ideas:

  • Log changes using ORM - Property-level logging - The methods that modify the POCOs should log every single property they modify, or modify properties via a ORM instead of operating directly on said POCOs. The ORM would log changes and be able to revert when needed.

  • Deep clone and diff - Memento pattern - Save deep clones of objects before modifying them (wasted memory, slow). After modifications are done, call a function that calculates differences in object properties (must iterate thru all props, slow, tedious, recursive). Differences are stored in a log to be able to revert it later.

  • Each command supports undo/redo - Command pattern - The classical approach, but it would required too many additions to the code. I'm looking for something generic, and ideally something that doesn't require rewriting the app from the ground up.

I don't know the typical approaches to this kind of problem, but considering its fairly common I'm sure there are good patterns to solving this without much hassle. Do you know of any pattern/library that handles history for POCOs in a simple way?

I'm currently reviewing the following but still looking for better approaches.

Community
  • 1
  • 1
Robin Rodricks
  • 110,798
  • 141
  • 398
  • 607
  • You shouldn't be thinking about undo/redo for the POCOs themselves; you should be thinking about undo/redo for the state they represent. What exactly are you trying to achieve? – Ant P Jan 20 '14 at 08:00
  • You're probably looking for the Memento pattern, have a look here: http://stackoverflow.com/questions/8994433/how-is-the-memento-pattern-implemented-in-c4 – Moeri Jan 20 '14 at 08:01
  • @Ant P - I'm using POCOs for the primary data within the app. If I can undo/redo changes made on the POCOs, I can update the GUI fairly easily after that. – Robin Rodricks Jan 20 '14 at 08:02
  • 1
    I still say you're going about this the wrong way - tracked changes of *application functionality* belong *in* your POCOs, not in an *archive of* your POCOs. Again, what are you trying to achieve? Your question is way too abstract. – Ant P Jan 20 '14 at 08:06
  • I think the 'best' solution would use reflection compiled to expression trees. But I don't know of any such existing framework. – Jack Jan 20 '14 at 08:10
  • @Jack - "reflection compiled to expression trees"?? What? Can you elaborate a bit in a proper answer so I can possibly build it? – Robin Rodricks Jan 20 '14 at 08:15
  • Why the close vote? What is wrong with this question? – Robin Rodricks Jan 20 '14 at 08:18
  • 1
    The [CSLA.NET Framework](http://www.lhotka.net/cslanet/) features an [`UndoableBase`](https://github.com/MarimerLLC/csla/blob/master/Source/Csla/Core/UndoableBase.cs) class that handles undo and can be easily modified for redo, worth a review even if you don't use it. – Adam Houldsworth Jan 20 '14 at 09:26
  • @Robinicks Were you able to find a good and efficient way? I have exactly this problem and cannot find the right solution. My POCOs are not immutable. – Vahid Nov 13 '18 at 07:12

1 Answers1

2

A possible high-level approach:

  1. Mark your POCO's properties' with virtual
  2. Redirect object instantiation requests to a custom factory for said classes (this is easy iff you're already using dependency injection)
  3. The first time your custom factory receives an instantiation request for one of your target classes, use reflection to emit a new class which overrides the target properties (perhaps decorated with a custom [UndoRedo] attribute?)
  4. The overridden set{} implementation would identify changes, and create a change identification object (which merely identifies the property, object ref and old value) which is pushed onto your change stack. And then call the set method of the base class. Would probably require writing some code in IL.
  5. To undo changes, pop the change identifier off the stack and apply the old value to the given property on the given object ref using your preferred approach

The result is that consumers of the supported classes would not notice any difference (except for object creation, see step 2).

Jack
  • 1,064
  • 8
  • 11
  • This codeproject lib does something similar, minus the code generation/IL. You basically wrap each property in `UndoRedo<>` tags and the system logs changes on a per-property basis. http://www.codeproject.com/Articles/19550/Automated-Undo-Redo-library-based-on-Csharp-Generi – Robin Rodricks Jan 21 '14 at 05:51