Large Lists in WPF and WCF – Part Two

by Chad 18. January 2010 16:18

Welcome Back
In part one of this four-part series, I showed how data binding to list controls works in WPF. This post will show you how to build a simple WCF service to serve up a list of Food objects. I will then show some patterns for dealing with large lists of items.

Building the Food Service
For detailed instructions on building a WCF service, you can download my WCF presentation materials and follow the source code and slides or lookup any of the many tutorials elsewhere. This series of posts will focus mainly on the message contracts for lists.

The NutrientDB service will expose a GetAllFoods operation that will return all of the Food objects in the nutrient database. I will start with a small set of Food objects, and then increase the size of the database to illustrate some of the issues that you need to consider for large sets of data.

Starting Simple
The simplest approach is to have your message contract return an IEnumerable<T> of your objects.

// Server-side code

[MessageContract(WrapperNamespace = ServiceConstants.NAMESPACE)]

public class GetAllFoodsResponse

{

    [MessageBodyMember]

    public IEnumerable<Food> Results { get; set; }

}

Clients of the service simply ask for all of the Food objects in the database and the service returns them all at once.

// Client-side code

private void LoadWCF_Click(object sender, RoutedEventArgs e)

{

    NutrientDBClient client = new NutrientDBClient();

 

    _foodItems = client.GetAllFoods();

    FoodList.ItemsSource = _foodItems;

 

    client.Close();

}

image

The problems with this approach usually aren’t visible with small sets of data and low network latency. However, as the data set increases and/or network speeds decrease (i.e. deploy your service to the internet) some problems will begin to present themselves. The three main problems are:

  1. The UI will “lock up” while data is downloading.
  2. Message size will increase; Eventually the default settings for WCF services will prevent the message from being received.
  3. A noticeable delay to load data will begin to slow your users down.

Let’s resolve the first problem now.

The UI will “lock up”
In WPF the user interface executes on it’s own thread. This thread is managed by an object called the Dispatcher. The Dispatcher is used to synchronize calls on the UI thread. When an event, such as a button click, is fired the event handler code is running on the UI thread. When our long-running WCF call is made on the UI thread, other UI operations are blocked from executing. This means that redrawing of the window, scrolling, and clicking all have to wait for your WCF request to complete. Additionally, WPF requires that any updates to the UI happen on the Dispatcher thread.

To solve this problem we need a way to offload the WCF request to a non-UI thread, then return back to the UI thread to update the ListView. Built into the Visual Studio 2008 service proxy generator is a setting to create asynchronous methods for each service operation. To get to this setting: right-click your service reference > choose “Configure Service Reference” > check “Generate asynchronous operations” > click OK.

When you do this Visual Studio will rebuild your proxies with four new members: BeginGetAllFoods(), EndGetAllFoods(), and GetAllFoodsAsync() methods, as well as a GetAllFoodsCompleted event. This gives you two options for calling WCF services asynchronously: BeginGetAllFoods() and EndGetAllFoods() are used together and GetAllFoodsAsync() and GetAllFoodsCompleted are used together. I’ll demonstrate the latter.

private void LoadWCFAsync_Click(object sender, RoutedEventArgs e)

{

    NutrientDBClient client = new NutrientDBClient();

    client.GetAllFoodsCompleted +=

        new EventHandler<GetAllFoodsCompletedEventArgs>(

            client_GetAllFoodsCompleted);

 

    client.GetAllFoodsAsync();

}

 

void client_GetAllFoodsCompleted(object sender, GetAllFoodsCompletedEventArgs e)

{

    if (e.Error == null)

    {

        _foodItems = e.Result;

        FoodList.ItemsSource = _foodItems;

    }

    else { MessageBox.Show(e.Error.Message); }

 

    NutrientDBClient client = (NutrientDBClient)sender;

    client.Close();

}

Now when I increase the number of foods in the database to 100 and run the program, there is short delay while I wait for data to come back, but the UI is responsive the whole time. When the GetAllFoodsCompleted event handler, client_GetAllFoodsCompleted(), is called we are already back on the UI thread, so I do not have to worry about managing thread synchronization.

Summary
In this post, I started with a very simple implementation of a WCF service that returned a set of Food objects. I pointed out tree problems with the simple solution: Unresponsive UI, excessive message size, and a noticeable delay to display results. I provided a solution to the unresponsive UI by generating asynchronous proxy methods and invoking the WCF calls on a non-UI thread. I will address the last two problems in parts three and four.

Source code for this post can be downloaded here.

Tags:

WCF | WPF

Comments

2/14/2010 11:29:17 PM #

trackback

Windows Client Developer Roundup for 2/15/2010

This is Windows Client Developer roundup #11. The Windows Client Developer Roundup aggregates information

Community Blogs

Comments are closed

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

About the author

Chad

Meeeee!!!

Hi, my name is Chad Boschert and I'm a software developer in Springfield, Missouri. I've been developing .NET applications in C# since 2002.

Recent Comments

Comment RSS

Calendar

<<  April 2014  >>
MoTuWeThFrSaSu
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar