14

I'm fairly new to MVVM, so please excuse me if this problem has a well-known solution.

We are building a bunch of model classes which have some core properties that are loaded up-front, as well as some additional properties which could be lazy-loaded on demand by making a web API call (update: to clarify, it would be a web API call per lazily-loaded property).

Rather than having multiple models, it seems sensible to have a single model with the lazy-loading logic in there. However, it also seems that the lazy-loaded properties should not block when accessed, so that when the View binds to the ViewModel and it binds to the Model, we don't block the UI thread.

As such, I was thinking of a pattern something along the lines of when a lazy property on the Model is accessed it begins an asynchronous fetch and then immediately returns a default value (e.g. null). When the asynchronous fetch is complete, it will raise a PropertyChanged event so that the ViewModel/View can re-bind to the fetched value.

I've tried this out and it seems to work quite nicely, but was wondering:

  1. Are there any pitfalls to this approach that I haven't found out about yet, but will run into as the app increases in complexity?
  2. Is there an existing solution to this problem either built into the framework, or which is widely used as part of a 3rd party framework?
Greg Beech
  • 133,383
  • 43
  • 204
  • 250
  • The possible problem i can come up with, is that you need to listen for the PropertyChanged event on all of these lazy loaders, meaning that if you have a property or function that relies on multiple of these loaders, it will have to wait for all the loaders it relies on to finish before executing its own code. This could result in having to write a lot of logic that could otherwise have been written as a single threaded call that combines fetching the lazy loaders "synchronously" within that separate thread. – Timothy Groote Jun 29 '11 at 14:38
  • @Timothy - Good point. I've already had a think about that, and my feeling is that due to the nature of the lazily loaded data it's unlikely that anything would depend on multiple pieces of lazy data. It's likely that multiple things would depend on a single piece of lazy data, but I don't think that presents a problem. – Greg Beech Jun 29 '11 at 14:44
  • I use the exact approach you describe above and it's worked very well. – Jeff Jun 29 '11 at 14:46

1 Answers1

11

I did something like this in the past and the one thing I kept forgetting about is you can't call your async property through any kind of code behind and expect it to have a value.

So if I lazy-load a list of Customer.Products, I can't reference Customer.Products.Count in the code-behind because the first time it's called the value is NULL or 0 (depending on if I create a blank collection or not)

Other than that, it worked great for the bindings. I was using the Async CTP library for making my async calls, which I found was absolutely wonderful for something like this.

public ObservableCollection<Products> Products
{
    get
    {
        if (_products == null)
            LoadProductsAsync();

        return _products;
    }
    set { ... }
}

private async void LoadProductsAsync()
{
    Products = await DAL.LoadProducts(CustomerId);
}

Update

I remember another thing I had issues with was data that actually was NULL. If Customer.Products actually returned a NULL value from the server, I needed to know that the async method had run correctly and that the actual value was null so that it didn't re-run the async method.

I also didn't want the async method to get run twice if someone called the Get method a 2nd time before the first async call had completed.

I solved this at the time by having an Is[AsyncPropertyName]Loading/ed property for every async property and setting it to true during the first async call, but I wasn't really happy about having to create an extra property for all async properties.

Rachel
  • 130,264
  • 66
  • 304
  • 490