Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-blocking lazy-loaded properties in model of MVVM

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?
like image 886
Greg Beech Avatar asked Jun 29 '11 14:06

Greg Beech


1 Answers

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.

like image 104
Rachel Avatar answered Nov 15 '22 23:11

Rachel