Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronously Lazy-Loading Navigation Properties of detached Self-Tracking Entities through a WCF service?

Tags:

I have a WCF client which passes Self-Tracking Entities to a WPF application built with MVVM. The application itself has a dynamic interface. Users can select which objects they want visible in their Work area depending on what role they are in or what task they are doing.

My self-tracking entities have quite a few Navigation Properties, and a lot of them are not needed. Since some of these objects can be quite large, I'd like to only load these properties on request.

My application looks like this:

[WCF] <---> [ClientSide Repository] <---> [ViewModel] <---> [View]

My Models are Self-Tracking Entities. The Client-Side Repository hooks up a LazyLoad method (if needed) before returning the Model to the ViewModel that requested it. All WCF Service calls are asyncronous, which means the LazyLoad methods are also asyncronous.

The actual implementation of the LazyLoad is giving me some trouble. Here are the options I have come up with.

EDIT - I removed the code samples to try and make this easier to read and understand. See previous version of question if you want to see it

Option A

Asynchronously LazyLoad the Model's properties from the WCF server in the Getter

Good: Loading data on demand is extremely simple. The binding in the XAML loads the data, so if the control is on the screen the data loads asynchronsly and notifies the UI when it's there. If not, nothing loads. For example, <ItemsControl ItemsSource="{Binding CurrentConsumer.ConsumerDocuments}" /> will load the data, however if the Documents section of the interface is not there then nothing gets loaded.

Bad: Cannot use this property in any other code before it has been initiated because it will return an empty list. For example, the following call will always return false if documents have not been loaded.

public bool HasDocuments  {      get { return ConsumerDocuments.Count > 0; } } 

OPTION B

Manually make a call to load data when needed

Good: Simple to implement - Just add LoadConsumerDocumentsSync() and LoadConsumerDocumentsAsync() methods

Bad: Must remember to load the data before trying to access it, including when its used in Bindings. This might seem simple, but it can get out of hand quickly. For example, each ConsumerDocument has a UserCreated and UserLastModified. There is a DataTemplate that defines the UserModel with a ToolTip displaying additional User data such as extension, email, teams, roles, etc. So in my ViewModel that displays documents I would have to call LoadDocuments, then loop through them and call LoadConsumerModified and LoadConsumerCreated. It could keep going too... after that I'd have to LoadUserGroups and LoadUserSupervisor. It also runs the risk of circular loops where something like a User has a Groups[] property, and a Group has a Users[] property

OPTION C

My favorite option so far... create two ways to access the property. One Sync and one Async. Bindings would be done to the Async property and any code would use the Sync property.

Good: Data is loaded asynchronously as needed - Exactly what I want. There isn't that much extra coding either since all I would need to do is modify the T4 template to generate these extra properties/methods.

Bad: Having two ways to access the same data seems inefficient and confusing. You'd need to remember when you should use Consumer.ConsumerDocumentsAsync instead of Consumer.ConsumerDocumentsSync. There is also the chance that the WCF Service call gets run multiple times, and this requires an extra IsLoaded property for every navigational property, such as IsConsumerDocumentsLoaded.

OPTION D

Skip the Asyncronous loading and just load everything synchronously in the setters.

Good: Very simple, no extra work needed

Bad: Would lock the UI when data loads. Don't want this.

OPTION E

Have someone on SO tell me that there is another way to do this and point me to code samples :)

Other Notes

Some of the NavigationProperties will be loaded on the WCF server prior to returning the object to the client, however others are too expensive to do that with.

With the exception of manually calling the Load events in Option C, these can all be done through the T4 template so there is very little coding for me to do. All I have to do is hook up the LazyLoad event in the client-side repository and point it to the right service calls.

like image 393
Rachel Avatar asked May 03 '11 20:05

Rachel


2 Answers

Gave it some thought, first of all I have to say that you must provide a clear to reader solution to this problem, DependecyProperties being loaded async when you bind to User.Documents property can be ok, but its pretty close to a side effect based solution. If we say that such behaviour in View is ok, we must keep our rest code very clear about it intentions, so we can see how are we trying to access data - async or sync via some verbose naming of something (method,classname,smth else).

So I think we could use a solution that is close to old .AsSynchronized() approach, create a decorator class, and provide each property a private/protected AsyncLoad & SyncLoad method, and a decorator class would be Sync or Async version of each lazyloadable class, whatever is more appropraite.

When you decorate your class with Sync decorator it wraps each lazyloadable class inside with a Sync decorator too so you will be able to use SynchUser(User).Documents.Count on sync class version with no probs cause it will be smth like SynchUser(user).SyncDocuments(Documents).Count behind in overloaded version of Documents property and would call sync getter function.

Since both sync and async versions will be operating on same object this approach wont lead to modifying some non referenced anywhere else object if you want to modify any property.

Your task may sound as one that can be solved in some magic "beautiful & simple" way but I dont think it can, or that it wont be any more simple than this one.

If this doesn't work im still 100% sure you need a clear way to differntiate in code whether a sync or async version of class is used or you will have a very hard to maintain code base.

like image 181
Valentin Kuzub Avatar answered Sep 16 '22 15:09

Valentin Kuzub


Option A should be the solution.

Create one property named LoadingStatus indicating data is loaded or loading not yet loaded. Load data asynchronously and set the LoadingStatus property accordingly.

Check the loading status in each property and if data not loaded then call function to load data and viceversa.

like image 40
Naresh Goradara Avatar answered Sep 19 '22 15:09

Naresh Goradara