When working on a Silverlight application that received data from a WebService I notice that loading of an array of 1500 items takes a lot of time. With this problem, application launch that has 3 separate webservice lookups, took almost 5 minutes to complete.

The problem became more interesting when my colleague actually ran get the same data bound UI in WinForms in 2 seconds, so there were something fishy going on in out Silverlight application. Let’s see the original code that loaded the data from the webservice:

1
2
3
4
5
6
7
8
9
10
11
public virtual void Load()
{
var client = new ServiceClient();
client.GetAllFirstLookupCompleted += (s, e) => Lookup.AddRange(e.Result.MapMany<LookupResult, LookupResultDTO>());
client.GetAllFirstLookupAsync();
}

public virtual ObservableCollection<LookupResultDTO> Lookup
{
get; private set;
}

WebService call returned a LookupResult but we used LookupResultDTO object bound to UI so the first thing we needed to do was to convert between these types. Then our bound collection was a ObservableCollection. Unlike the same type in WPF, ObservableCollction in Silverlight has no construct that accepts an IList so you have to add individual items one by one! To make things look niceer, we’ve created an AddRange extension method that add items of an IList to an ObservableCollection.

So, why would the above code take this long?

First Mistake

First mistake was to use AutoMapper! Yes, it makes life easier by automagically map two unconvertable types by using reflection and flatting structure. It even works great if your collections are relatively small but avoid using it if you have a large collection (I don’t even consider a collection of 1000 items large!).

You can always roll your own mappers and it is easy to create and use. Our mapper looked like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LookupResultMapper
{
public LookupResultDTO MapOne(LookupResult result)
{
return new LookupResultDTO(result.PrimaryKey, result.Name, result.Number);
}

public IList<LookupResultDTO> MapMany(LookupResult[] results)
{
var list = new List<LookupResultDTO>();

foreach (var result in results)
{
list.Add(MapOne(result));
}

return list;
}
}

The reason is obvious! Using reflection is a lot slower and when you’re doing it for 1000 times, you’ll get 1000 times slower! This small change improved the performance more than 50%.

1
2
3
4
5
6
public virtual void Load()
{
var client = new ServiceClient();
client.GetAllFirstLookupCompleted += (s, e) => Lookup.AddRange(new LookupResultMapper().MapMany(e.Result));
client.GetAllFirstLookupAsync();
}

Second Mistake

When a UI element is bound to an observable collection, every addition and removal of an Item probably lead to some calculation in the element for things such as draing and layout calculation. Second problem was to add the collection one by one to the observable collection.

Unfortunately, as mentioned, ObservableCollection unlike WPF version has no construct that accepts an IList. You have some options though:

  • Make your UI element Invisible (Visibility=Collapsed or Hidden) then add the items to the bound collection. This will prevent exhaustive calculation.

  • Change the collection type from ObservableCollection to IList, if possible.

Depending on your scenarios, one of the above mentioned solutions may work for you. In our case, we didn’t need to add / remove items from the collection at a later time so switching to IList did the trick.

After refactoring, application that took almost 5 minutes to launch now runs in seconds!