Removing Repetitive Boiler Maker Code From View Models

While I was writing the BBQ Shack, I noticed that view model code calling into the business layer was repetitive across view models. The pattern I used was simple and widely accepted; background worker to make the asynchronous calls to the business layer.

The below code snippet was typical.  Background worker, exception checking, call to business layer, successful result code path, and exception result code path.

Old Pattern

void BackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) {
    e.Result = _iBLLItemCategory.Select(Convert.ToInt32(e.Argument));
}

void BackgroundWorker_RunWorkerCompleted(object sender,
    System.ComponentModel.RunWorkerCompletedEventArgs e) {
    if (e.Error == null) {
        if (e.Result != null) {
            this.ic_ItemCategory = (ic_ItemCategory)e.Result;

        } else {
            _iViewModelUIService.MessageDialog("Data Error",
                "Record Not Found",
                string.Format("Item Category key: {0} not found", _objLoadKey.ToString));
        }

    } else {
        _objIViewModelUIService.ExceptionDialog(e.Error);
    }
}

I kept thinking about this, always looking for a better pattern.  I also wanted a pattern that only required a single line of code and still gave me all of the same benefits of the above code.

New Pattern

All of the above code condenses nicely down to one line of code that provides all of the above “old pattern” features.

The below repository method signatures describe the pattern.  The pattern for the method signatures is zero or more parameters, followed by the same last two; Action<T> resultCallback and Action<Exception> errorCallback.

public interface IItemRepository {

    void GetAll(Action<IEnumerable<Item>> resultCallback, Action<Exception> errorCallback);

    void Get(Int32 itemId, Action<Item> resultCallback, Action<Exception> errorCallback);

    Item Create();
}

The successful result of the method call is always calls the resultCallback delegate.

The unsuccessful result of the method call is always calls the errorCallback delegate.

This pattern is incredibly simple and leads to much cleaner code in the view model.  The errorCallback is a method in the view model base class.  This method can use a variety of techniques for displaying the error message to the user.

void LoadItems() {
    _itemRepository.GetAll(result => { this.DataItems.Source = result; }, this.DisplayException);
}

Repository Implementation – Bring on the Task Parallel Library (TPL)

The below implementation leverages the TPL Futures pattern.  Use of the TPL is optional, I’m using it here in this WPF application.  The TPL is not available in Silverlight 4 applications yet.  For Silverlight applications, change the implementation to use the common Silverlight asynchronous pattern.

The “pattern” is described in the above IItemRepository interface.  Like all interface contracts, implementation specifics can be platform specific and up to the developer.

Strongly recommend you read this book on MSDN: Parallel Programming with Microsoft .NET.  The book can also be purchased from Amazon here.  There is a book and Kindle version available.

[Export(typeof(IItemRepository))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class ItemRepository : IItemRepository {

    readonly ThePhoneCompanyEntities _dataService;

    [ImportingConstructor]
    public ItemRepository(DataServiceFacade dataServiceFacade) {
        _dataService = dataServiceFacade.DataService;
    }

    void IItemRepository.GetAll(Action<IEnumerable<Item>> resultCallback,
        Action<Exception> errorCallback) {

        // This code can be refactored into a generic method
        // I left it this way to help with the learning process
        Task<RepositoryResult<IEnumerable<Item>>> task =
            Task.Factory.StartNew(() => {
                try {
                    return new RepositoryResult<IEnumerable<Item>>(
                                _dataService.Items.ToList(), null);
                } catch(Exception ex) {
                    return new RepositoryResult<IEnumerable<Item>>(null, ex);
                }
            });

        task.ContinueWith(r => {
            if(r.Result.Error != null) {
                errorCallback(r.Result.Error);
            } else {
                resultCallback(r.Result.Package);
            }
        }, CancellationToken.None, TaskContinuationOptions.None,
            TaskScheduler.FromCurrentSynchronizationContext());
    }

    void IItemRepository.Get(Int32 itemId, Action<Item> resultCallback,
        Action<Exception> errorCallback) {

        if(itemId != 0) {

            // This code can be refactored into a generic method
            // I left it this way to help with the learning process
            Task<RepositoryResult<Item>> task =
                Task.Factory.StartNew(() => {
                    try {
                        return new RepositoryResult<Item>(
                            _dataService.Items.Where(
                                i => i.ItemID == itemId).FirstOrDefault(), null);
                    } catch(Exception ex) {
                        return new RepositoryResult<Item>(null, ex);
                    }
                });

            task.ContinueWith(r => {
                if(r.Result.Error != null) {
                    errorCallback(r.Result.Error);
                } else {
                    resultCallback(r.Result.Package);
                }
            }, CancellationToken.None, TaskContinuationOptions.None,
                TaskScheduler.FromCurrentSynchronizationContext());
        } else {
            resultCallback(((IItemRepository)this).Create());
        }
    }

    // I always create my entity objects in the business layer and never in the presentation layer
    Item IItemRepository.Create() {
        return new Item();
    }
}

Key Points

  • Call that does the actual work is wrapped in a try catch block.
  • The result of the asynchronous operation is wrapped in a RepositoryResult object.
  • The RepositoryResult is used to drive invoking the resultCallback or the errorCallback on the calling thread. The TaskScheduler.FromCurrentSynchronizatikonContext method call in the task.ContinueWith ensures the two callbacks are invoked on the calling thread. 

Download

This code is from one of my presentations at the patterns & practices 2010 Symposium. 

The article and video is here: http://blogs.msdn.com/b/kashiffl/archive/2010/10/21/patterns-and-practices-2010-symposium-content.aspx

This code can be downloaded from the article.

Close

I hope that this short post encourages you to look for possible refactorings in your own code and to take a look at the TPL. 

Have a great day,

Just a grain of sand on the worlds beaches.

23 Responses to Removing Repetitive Boiler Maker Code From View Models

  1. kevinstumpf says:

    Hi Karl,

    I didn’t like the old pattern either. So I introduced a more generic Dispatcher Extension method in my projects (copied from a .NET 3.5 project) that reduces the boiler plate code to a minimum as well:


    public static class DispatcherExtensions
    {
    public static void CheckedInvoke(this Dispatcher dispatcher, Action action)
    {
    if (!dispatcher.CheckAccess())
    {
    dispatcher.Invoke(action);
    return;
    }

    action();
    }

    public static void ExecuteAsync(this Dispatcher dispatcher, Func call, Action callFinishedSuccessfully,
    Action callFailed) where T : class
    {
    ThreadPool.QueueUserWorkItem(state =>
    {
    T k;
    try
    {
    k = call();
    }
    catch (Exception ex)
    {
    dispatcher.CheckedInvoke(() => callFailed(ex));
    return;
    }
    dispatcher.CheckedInvoke(() => callFinishedSuccessfully(k));
    });
    }

    public static void ExecuteAsync(this Dispatcher dispatcher, Action call, Action callFinishedSuccessfully,
    Action callFailed)
    {
    ThreadPool.QueueUserWorkItem(state =>
    {
    try
    {
    call();
    }
    catch (Exception ex)
    {
    dispatcher.CheckedInvoke(() => callFailed(ex));
    return;
    }
    dispatcher.CheckedInvoke(callFinishedSuccessfully);
    });
    }
    }

    Just another simple approach.
    What do you think?

    Kevin

  2. peteohanlon says:

    Nice Karl, and a good use of the TPL.

  3. [...] This post was mentioned on Twitter by Karl Shifflett, Karl Shifflett, WPF Blogger, Larry King, patterns & practices and others. patterns & practices said: Karl Shifflett: Removing Repetitive Boiler Maker Code From View Models – http://cut.ms/4RN [...]

  4. edfrederick says:

    I have been using this pattern for the last few weeks after catching a video from PDC from Anders Heilsberg regarding Reactive Extensions. It got me thinking parallel and so far I’m loving it. The one question I have that I haven’t found any info to fully understand is:

    Consider a Task executing a thread coded within a ViewModel and the thread updates a property on the ViewModel instance that is databound to a WPF View. Does databinding implicitly use the Dispatcher to read/write to bound properties to ensure thread safety? I don’t want to use the Dispatcher thread context to make updates, and it seems like a bit of voodoo how I can update the bound property in my ViewModel from a non-dispatcher thread which in turn updates the View without having thread context problems. Do you have any info to share on this? As always, it’s a pleasure to read your great work!

    –Eddie Frederick

    • Eddie,

      Would not recommend loading up threading code into the view model. You can easily do it, as I’ve been doing this for years, but as this post shows, much easier to move the async code to the service layer and keep the view model simple.

      The UI must always be updated on the UI thread.

      Karl

  5. stephencleary says:

    Karl -

    While I applaud the use of the TPL, a more flexible interface would be to actually return the Task object (unless this interface needs to be shared with a Silverlight project). The Task-based Asynchronous Pattern (TAP) is described in a document in the Visual Studio Async CTP.

    -Steve

    • Steve,

      My goal was to keep any async or Task code out of the UI. This allows the async or Task code to evolve over time without forcing a change to the view model.

      Best,

      Karl

  6. onovotny says:

    Doesn’t the Silverlight 4 version of Rx include a back-port of the TPL? They back-ported it for the .NET 3.5 version of Rx…. If so, you could use the System.Threading library from Rx and use tasks everywhere…

    • onovotny,

      Not sure about the SL Rx.

      My goal was to keep any async or Task code out of the UI. This allows the async or Task code to evolve over time without forcing a change to the view model.

      For your SL services you could use Rx for implementation if it is availalbe. I know tha the WP7 has this.

      Karl

  7. Bryan Watts says:

    I have thought about this recently as well. I like your use of the TPL to implement the asynchrony.

    This blog post of mine outlines a similar approach I use in Silverlight. It is tailored specifically to the generated proxies, leveraging that machinery, but the thinking is the same.

    Thanks for posting.

  8. [...] Removing Repetitive Boiler Maker Code From View Models (Karl Shifflett) [...]

  9. ido.ran says:

    Hi Karl,
    I’ve implement the same thing only using Microsoft CCR as async library.
    The great thing about CCR is that it has very simple API yet it also you to do all sort of async patterns like scatter/gather, all-done, first-done and it handle errors in async manaer (you can read more about that in the CCR docs).

    Another nice thing is that it scale very well for multi-core machines and it has support to bride to exist APM code.

    Thanks for the post,
    Ido

  10. mpoelzl says:

    Hello Karl,

    Thanks for sharing this elegant solution. I’m looking to incorporate this into my next project which will really clean up my ViewModel code.

    One thing I would be interested to know is whether you have any thoughts on how this approach can be unit tested (both the repositories as well as the usage in the ViewModel)?

    Mike

    • Karl says:

      Mike,

      This code can be unit tested like other code. Most developers mock the interface when testing in isolation.

      I’m working on a project now that will have unit tests for this code. I hope to release soon.

      Cheers,

      Karl

Follow

Get every new post delivered to your Inbox.

Join 145 other followers