N-level Undo with Private Backing Fields?

N-level Undo with Private Backing Fields?

Old forum URL: forums.lhotka.net/forums/t/6762.aspx


kennethkryger posted on Wednesday, April 08, 2009

I have a class, ConnectionPoint, that's able to snap to other ConnectionPoint-objects.

If ConnectionPoint A snaps onto ConnectionPoint B, they both need to know about it.

So I added two Managed Backing Fields to the ConnectionPoint-class:

- private ConnectionPoint SnappedTo
- private ConnectionPoint SnappedFrom

(only one of them can be set at any one time...)

and added a public SnapTo(ConnectionPoint) method.

In the case above, ConnectionPoint A snaps onto ConnectionPoint B like this:

var a = ConnectionPoint.NewConnectionPoint();
var b = ConnectionPoint.NewConnectionPoint();
b.SnapTo(a);

The Snap()-method on object b adds references, so they look like this afterwards:

b.SnappedTo = a
a.SnappedFrom = b

This works well, until you consider supporting N-level Undo... :(

However, I tried implementing the SnappedTo and SnappedFrom properties using Private Backing Fields instead of Managed, and Undo works!!??

According the Rocky's Expert C# 2008 BO book, I should be using the [NotUndoable] attribute the support N-level Undo and circular references, so I'm wondering if I'm just lucky right now, and if it will come around and bite me later, if I continue down this path...?

Any thoughs and comments are greatly appreciated.

Regards,
Kenneth

RockfordLhotka replied on Wednesday, April 08, 2009

When you are dealing with object references in an editable object, to an editable object, there are some rules to keep in mind.

P = parent object

C = child object

U = reference to non-child editable object (using relationship)

  1. Any reference from P to C (managed or private) will be followed by n-level undo, so it is important that C be referenced only once by P (or any parent - one parent per child)
  2. If you must have 2+ references to C, then the other references must be private fields and must be marked NotUndoable; they should probably be marked NonSerialized too, to keep the size of the serialized byte stream as small as possible
  3. If C references P, that reference must be a private field and must be marked NotUndoable or you'll get a circular reference; again it should be marked NonSerialized too, to keep the size of the byte stream small
  4. If P or C references U, that must be a private field, at must be marked NotUndoable and NonSerialized - if you use a managed field then U becomes a child, which is not what you want

In your case you have peer level objects referencing each other. These references must be implemented as U references - in other words using private fields that are marked with NotUndoable and (optionally, but recommended) NonSerialized. Otherwise it will bite you at some point.

Copyright (c) Marimer LLC