DM-V-VM part 3: A sample DataModel

In part 2, I showed a base class for DataModels. In this post, I will describe a sample implementation. For this sample, I'll use a DataModel that represents a stock. The model will be in charge of asynchronously fetching the stock quote and making it's value available.

As part of making this class more testable, we first define an interface for retrieving stock quotes:

    public interface IStockQuoteProvider

    {

        /// <summary>

        /// Get a quote. This function may block on slow operations like hitting the network.

        /// </summary>

        /// <param name="symbol">The stock symbol.</param>

        /// <param name="quote">The quote.</param>

        /// <returns>Whether we were able to get a quote.</returns>

        bool TryGetQuote(string symbol, out double quote);

    }

This will let us plug in mock providers for unit testing. Let's walk through the model implementation. First the class declaration and constructor:

    public class StockModel : DataModel

    {

        public StockModel(string symbol, IStockQuoteProvider quoteProvider)

        {

            _symbol = symbol;

            _quoteProvider = quoteProvider;

 

            this.State = ModelState.Fectching;

 

            // Queue a work item to fetch the quote

            if (!ThreadPool.QueueUserWorkItem(new WaitCallback(FetchQuoteCallback)))

            {

                this.State = ModelState.Invalid;

            }

        }

The constructor takes the stock symbol and the quote provider. It sets the initial model state to fetching and queues a work item to be called on a background thread. Don't forget to check the return result of QueueUserWorkItem! If it fails, the model will be put in the invalid state.

Next, the symbol property. This one is simple!

        public string Symbol

        {

            get { return _symbol; }

        }

For the quote property, we'll have a private setter that will send the property changed event:

        public double Quote

        {

            get

            {

                VerifyCalledOnUIThread();

 

                return _quote;

            }

 

            private set

            {

                VerifyCalledOnUIThread();

 

                if (_quote != value)

                {

                    _quote = value;

                    SendPropertyChanged("Quote");

                }

            }

        }

Now, here's the callback to fetch the quote. Remember, this will be called on a background thread where it's okay for us to do expensive operations like hitting a web service to get a quote.

        private void FetchQuoteCallback(object state)

        {

            double fetchedQuote;

            if (_quoteProvider.TryGetQuote(_symbol, out fetchedQuote))

            {

                this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,

                    new ThreadStart(delegate

                    {

                        this.Quote = fetchedQuote;

                        this.State = ModelState.Active;

                    }));

            }

            else

            {

                this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,

                    new ThreadStart(delegate

                    { this.State = ModelState.Invalid; }));

            }

        }

We need to dispatch back to the UI thread to set the properties. This avoids any complex locking logic and makes sure that all of the property changed events come from the UI thread.

Finally, we just have the fields:

        private string _symbol;

        private double _quote;

        private IStockQuoteProvider _quoteProvider;

    }

So, that's it! What we've got is a class that's perfectly suited to being displayed in a WPF DataTemplate. It's got the symbol and quote available for binding. And, the state can be used in a trigger to change the look while it's fetching or if there's an error. The UI thread will never be blocked on slow operations.

Next up, I'll show some unit tests for this class, which will show you how to unit test code that uses a dispatcher.