Questions about Undo scenario where BusinessBase<T> object hidden in cache "wrapper"?

Questions about Undo scenario where BusinessBase<T> object hidden in cache "wrapper"?

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


rsbaker0 posted on Wednesday, October 22, 2008

To support lazy loading, I've implement a very simple "wrapper" class that just has two fields:

1. key field (for fetching from the database)

2. reference to a BusinessBase<T> derived object

(class header looks something like this: public class SingleObjectCache<T> where T : MyFetchableBusinessBase<T>)

The purpose of the class is to let another class declare one of these as a member and just let it call GetObject(key) without worrying about duplicate hits to the database. On the first call, the SingleObjectCache object tries to fetch T from the database, and stores a both the key and reference to T if finds it and null otherwise. Subsequent calls using the same key return whatever reference was stored.

Here is a typical use...

SingleObjectCache<STATION> _StationCacheObj;

protected STATION GetStationObject()
{
   if (String.IsNullOrEmpty(_key))
   {
       return null;
   }
   else
   {
       if (_StationCacheObj == null)
           _StationCacheObj = new SingleObjectCache<STATION>();
       return _StationCacheObj.GetObject(_key);
   }
}

Since this seems to hide the IUndoableObject interface of T from containing object, CopyState, UndoChanges, ApplyChanges will just see this as a normal field just serialize the entire contents of the cache wrapper and it's contents without cascading the IUndoableObject interface to the reference of T.

My question is, why is this bad? I'm trying to figure out what will break. Certainly BeginEdit/CancelEdit/ApplyEdit on the root will dutifully manage the pushing/popping such object on the state stack, but the EditLevel of T won't ever change (or be compared with the root).

 

RockfordLhotka replied on Wednesday, October 22, 2008

I don't understand the issue you are having? It appears that your wrapper returns the actual business object, so any consuming code doesn't interact with the wrapper other than to get the object?

So I don't see how this is any different from any other direct use of the business object?

rsbaker0 replied on Thursday, October 23, 2008

The issue is that when I include one of these as a "child" of another object, the state management implementation sees only the wrapper, not the wrapped object inside it. So, for example, when CopyState() is called, it looks like a normal field rather than a BO and CopyState() just stores  the wrapper (and contained object) on the state stack -- it can't cascade the CopyState() call to the wrapped object.

My initial response (e.g. the question above) was "so, it still keeps a copy of the object on a stack", it just doesn't maintain the EditLevel. I was trying to figure out what problems this would cause.

RockfordLhotka replied on Thursday, October 23, 2008

Oh, I see, so you want a parent to “see through” this as a child.

 

You can do that, but your wrapper must participate in the undo process. In other words, the wrapper must implement IUndoableObject and cascade calls down to the child object. I would think that the wrapper could just act as a pass-through, so it wouldn’t track edit levels by itself – it would always just delegate the calls directly to the object it contains.

 

I haven’t done this, so I don’t know what side-issues you may encounter, but in general this should work.

 

Obviously the wrapper needs to be Serializable, and if you want to go to CSLA .NET for Silverlight, your wrapper will need to implement the functionality required by the MobileFormatter (which means implementing Csla.Serialization.Mobile.IMobileObject).

 

Rocky

rsbaker0 replied on Friday, October 24, 2008

Thanks, that's a great idea.

Just to be clear, a design point of the undo implementation is to preserve references to existing BusinessBase objects? Fields you just put on the state stack, but object that implement IUndoableObject don't get put on the stack -- you just cascade the call to them so existing references to them stay intact, right?

RockfordLhotka replied on Friday, October 24, 2008

That is correct, undo is an "in place" operation for child objects. I really think your wrapper would just delegate every call in that interface down to the child object it contains.

rsbaker0 replied on Friday, October 24, 2008

Interesting.

It seems to be working well but, I'm finding I'm duplicating much of the work you're doing with LoadProperty of a managed child in 3.5:

- Setting edit level (had to copy your ResetChildEditLevel implementation to a local helper class)...

- Set parent for wrapped child to the containing object of the wrapper...

- Adding wrapped child to active child list of parent (I started with CSLA 2.1 and had my own version of your "child data portal" that tracked a list of updateable child objects that needed to be saved when root is saved)...

- Cascading all the IUndoable object calls

I  haven't waded into the managed properties yet, but I suspect much of this may just "go away" when I do. Maybe an obvious way will reveal itself to formalize the concept of a "lazy child" in the framework itself...

Thanks again for your help. The framework is proving flexible enough to handle many interesting scenarios.

RockfordLhotka replied on Friday, October 24, 2008

Well, wait a minute, if all you are trying to do is lazy loading of a child – that is absolutely supported in 3.5 thanks to the field manager.

 

In fact one of the major reasons for building the FM the way I did was to simplify child object management. So even if you don’t use the FM for other properties, you REALLY should use it for all child object references.

 

The basic concept is this:

 

public MyChild Child

{

  if (!FieldManager.FieldExists(ChildProperty))

    LoadProperty(ChildProperty, Child.GetChild(this.Id));

  return GetProperty(ChildProperty);

}

 

There are variations of course, but you should be able to get the idea from that code.

 

Rocky

Copyright (c) Marimer LLC