Click here to Skip to main content
12,511,920 members (47,058 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

5.8K views
1 bookmarked
Posted

Removing Repetitive Boiler Maker Code From View Models

, 17 Nov 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
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.

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 always calls the resultCallback delegate.

The unsuccessful result of the method call 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.

I 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.


Filed under: C#, CodeProject, M-V-VM, MVVM, Prism, TPL, Visual Studio 2010, WPF General

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Karl Shifflett
Architect Infragistics
United States United States

I’m a passionate Platform Architect at Infragistics.

I’m a long-time WPF-Prism fanatic who enjoys writing developer tools and line of business applications.

My current front end passions are: XAML platforms (Xamarin.Forms, Xamarin, UWP, and WPF), Electron, ES2015 (ES6), Node.js, Aurelia, and AngularJS (Angular 1.5.x).

For the back end I use what is appropriate for the project: SQL Server and ASP.NET WebAPI, MongoDB, Express, Azure, Firebase, etc.

I am very pragmatic software engineer and strive to write simple, maintainable, and testable code. Simple code allows for solving complex problems in a maintainable way.

My Blog

My Github Repros

My YouTube Videos

Just a grain of sand on the worlds beaches.


You may also be interested in...

Pro
Pro

Comments and Discussions

 
Question"boiler maker" ? Pin
PIEBALDconsult8-Jan-15 9:40
protectorPIEBALDconsult8-Jan-15 9:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160929.1 | Last Updated 17 Nov 2010
Article Copyright 2010 by Karl Shifflett
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid