CM 'best practice' for managing ChildViewModel data dependencies

May 10, 2011 at 4:25 AM

I have some large composite views that I use to manage with a single large UnitOfWork/ Manager models where I pre-populated all the data I needed in one DB call and then distributed it out to all my child ViewModels.  It was a disaster and with my re-write and implementation of CM I'm trying to tear it all down and make it simple.  I have created a single master ViewModel but pushed all the data fetching/updates down in to the childviewmodels/models.

However I've reached the point where I have 'shared' data that I need to pass around.  The way I would like my master view to work is that the various child views or links to the child views reflect that the child data is loading and/or it cannot be accessed yet.  Some of the childVMs will load the minimum amount of data and then lazy load the rest if they are actually activated.

How are people handling the case where childVM C needs the model data from childVM A & B and you want to keep everything as async as possible.

I see a few options:

1) Get all the data in the parent and push it down - I don't want to do this

2) setup a bunch of _isLoadedDataA, _isLoadedDataB and then subscribe to all the ChildVM.PropertyChanged events and and call a common method to do a if(_isLoadedDataA && _isLoadedDataB && !_isLoadedDataC) LoadDataC(DataA, DataB)?

3) Something similar /w the EventAggregator?

I have a few different scenarios where depending if the data is new or already configured then I have less dependencies etc.  Also I only really need to do it once and then the different viewModels can just work on their own.

thanks

jack

May 10, 2011 at 8:57 AM

Can't you design a service to deal with this scenario? What if all your data is managed by a 'fetcher' and such object is shared among all view-models (and passed as a dependency)?

It could be possible to implement all the fetching logic inside a single object, and provide lazy loading if needed... I was thinking about something along these lines

public enum FetchingState
{
      Idle,
      Completed,
      Failed,
      Running
}

public interface IFetcher<T>
{
     event EventHandler<FetchedEventArgs<T>> Fetched;
       
     void BeginFetching(object key, object parameter);

     void CancelFetching(object key);

     T GetFetchedData(object key, out FetchingState state);

     FetchingState GetFetchingState(object key);
}

public class FetchEventArgs<T> : EventArgs
{
     public FetchingState State { get; private set; }

     public T Data { get; private set; }

     public object Parameter { get; private set; }

     public object Key { get; private set; }

     public FetchEventArgs (object key, object parameter, FetchingState state, T data)
     {
         Key = key;
         Parameter = parameter;
         State = state;
         Data = data;
     }
}
Different data of the same type would be addressed using specific keys, while filtering the Fetched event would allow you to populate individual view-model collections asynchronously (lazy loading, if needed) and implement boolean guards to check if the data is available or not.

This way, the entire fetching implementation is completely separated from the view-models, and data can be shared and manipulated as needed without references to different view-models.

If you want, you can even add another layer for caching (or integrate it with an IFetcher<T>), using a service like this:

public IDataCache<T>
{
    T GetCachedData(object key);

    void ClearCachedData(object key);
}

Note that the cached data could even be a BindableCollection<V>, so you could use them directly in your view-models.

All in all, this approach would be similar to your #2, with it would be view-model agnostic and would use a centralized class to perform data retrival and caching.

What do you think? Could this be a possible solution?

May 10, 2011 at 9:20 AM
Edited May 10, 2011 at 9:29 AM

I don't have a definite solution for this.

[EDIT: it shouldn't be too bad, all in all, since BladeWise proposed a similar solution (yet way better explained than mine)]

Yet, I would also consider using an external service with the responsibility of tracking the data availability and coordinating the async loading operation.
The involved VMs could take a reference to a shared instance of the service from the container and tunnel all data requests through it. 
Different "clusters" of  master VM + respective children could identify themselves with a token, thus allowing the central service to handle multiple concurrent clusters (if required).
You can also make use of EventAggregator to let the central coordinator communicate changes in the availability of data (for example at completion of async loading).

Does it make sense?

 

May 10, 2011 at 9:23 AM

@BladeWise: you are a rocket! 

May 10, 2011 at 9:25 AM

@marco: the nice part is that we thought along the same lines independently :D

May 10, 2011 at 3:21 PM

Very nice – especially since I posted the request for help before going off to bed and there in the morning is a nice treat of an answer. It also is an improvement on a static application wide service I had plugged in earlier. It worked but was cumbersome and was application wide instead of generic.

I will have to think about how to best tie that into my existing CSLA framework. Currently what I have done is ‘merged’ a screen with a CSLA class to create a hybrid VM. The CM does everything UI and the CSLA child VM does everything data. The CSLA class is a pseudo ViewModel but manages all the Model data and is transferred back and forth to the server. So the whole object is serialized back and forth and runs on the server or on the client. I have set it up so the CSLA class is exposed as the Model property in my CM ViewModel so I should be able to just drop them in.

This would also give me a spot to do my caching. I’ve been struggling with where to slot it in with a hybrid of standalone classes and a few static data services.

Just to clarify – in this implementation the T is the smallest piece of data I need? So in my example where VMC needs modelA and modelB I would create two Fetching request

Thank you kindly for your time.

jack

From: BladeWise [email removed]
Sent: Tuesday, May 10, 2011 2:25 AM
To: jaddington@alexandergracie.com
Subject: Re: CM 'best practice' for managing ChildViewModel data dependencies [caliburnmicro:256994]

From: BladeWise

@marco: the nice part is that we thought along the same lines independently :D

May 10, 2011 at 3:33 PM

Well, in the above implementation the T is the set of data you want to retrieve (I supposed it could be something like IEnumerable<People>, so that you could implement a lazy bucketed fetch), but you could modify the interface to define T as the individual data type, and methods to return a collection of such objects.

Whatever the implementation, the basic idea is that you can use a specialized fetcher for different types and keys to distinguish among sub-sets of the same type (if needed). :)

May 10, 2011 at 3:38 PM

Thanks – we are on the same page.

From: BladeWise [email removed]
Sent: Tuesday, May 10, 2011 8:33 AM
To: jaddington@alexandergracie.com
Subject: Re: CM 'best practice' for managing ChildViewModel data dependencies [caliburnmicro:256994]

From: BladeWise

Well, in the above implementation the T is the set of data you want to retrieve (I supposed it could be something like IEnumerable<People>, so that you could implement a lazy bucketed fetch), but you could modify the interface to define T as the individual data type, and methods to return a collection of such objects.

Whatever the implementation, the basic idea is that you can use a specialized fetcher for different types and keys to distinguish among sub-sets of the same type (if needed). :)

May 10, 2011 at 6:13 PM

Bladewise,

I’m plugging away at this and just wanted to confirm something… is the GetFetchedData a generic method to either:

1 – kick off a Begin fetch (return null + state = running)

2 – synchronously get data (return data and get cached data or wait to retrieve data)

A BeginFetch will ultimately fire Fetched, unless cancelFetching is called.

Thanks

jack

From: BladeWise [email removed]
Sent: Tuesday, May 10, 2011 1:57 AM
To: jaddington@alexandergracie.com
Subject: Re: CM 'best practice' for managing ChildViewModel data dependencies [caliburnmicro:256994]

From: BladeWise

Can't you design a service to deal with this scenario? What if all your data is managed by a 'fetcher' and such object is shared among all view-models (and passed as a dependency)?

It could be possible to implement all the fetching logic inside a single object, and provide lazy loading if needed... I was thinking about something along these lines

public enum FetchingState
{
      Idle,
      Completed,
      Failed,
      Running
}
 
public interface IFetcher<T>
{
     event EventHandler<FetchedEventArgs<T>> Fetched;
       
     void BeginFetching(object key, object parameter);
 
     void CancelFetching(object key);
 
     T GetFetchedData(object key, out FetchingState state);
 
     FetchingState GetFetchingState(object key);
}
 
public class FetchEventArgs<T> : EventArgs
{
     public FetchingState State { get; private set; }
 
     public T Data { get; private set; }
 
     public object Parameter { get; private set; }
 
     public object Key { get; private set; }
 
     public FetchEventArgs (object key, object parameter, FetchingState state, T data)
     {
         Key = key;
         Parameter = parameter;
         State = state;
         Data = data;
     }
}
Different data of the same type would be addressed using specific keys, while filtering the Fetched event would allow you to populate individual view-model collections asynchronously (lazy loading, if needed) and implement boolean guards to check if the data is available or not.

This way, the entire fetching implementation is completely separated from the view-models, and data can be shared and manipulated as needed without references to different view-models.

If you want, you can even add another layer for caching (or integrate it with an IFetcher<T>), using a service like this:

public IDataCache<T>
{
    T GetCachedData(object key);
 
    void ClearCachedData(object key);
}

Note that the cached data could even be a BindableCollection<V>, so you could use them directly in your view-models.

All in all, this approach would be similar to your #2, with it would be view-model agnostic and would use a centralized class to perform data retrival and caching.

What do you think? Could this be a possible solution?

May 10, 2011 at 11:55 PM

My idea was to use B eginFetching to start the async fetching, and GatFetchedData would be a synchronous call to get the retrieved data so far. The fetched data would be issued every time a new chunk of data is retrieved, so that you can know when the fetching as completed.

Well, this was my idea, but I suppose any variation would do... :)

May 11, 2011 at 12:45 AM

Ahh.. I didn’t think of getting the data in chunks, that was what was throwing me. It makes perfect sense that way. I don’t currently do any ‘paged’ data retrievals but it actually works well with a couple of views where I get keys + refreshdates and then load valid cached data, then refreshed cache data etc.

Thanks

From: BladeWise [email removed]
Sent: Tuesday, May 10, 2011 4:56 PM
To: jaddington@alexandergracie.com
Subject: Re: CM 'best practice' for managing ChildViewModel data dependencies [caliburnmicro:256994]

From: BladeWise

My idea was to use B eginFetching to start the async fetching, and GatFetchedData would be a synchronous call to get the retrieved data so far. The fetched data would be issued every time a new chunk of data is retrieved, so that you can know when the fetching as completed.

Well, this was my idea, but I suppose any variation would do... :)