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).
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?
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.
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
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?
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.
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