Performance tips for RIA Service with Silverlight 4

A few tips we can use to improve performance. Well mostly we will cover the Pagination and Limiting Query results and Output cache of the results.


Performance is a vital part of any application, possibly more important for a web based application. With the wizard based approach of RIA service, generally we used to include all entity exposing to client and also allowing the DomainService to fetch everything from the database. Not only does this approach take a toll on security but also it loads the middle tier unnecessarily.

So this article is about a few tips we can use to improve performance. Well mostly we will cover the Pagination and Limiting Query results and Output cache of the results.

The Example

Since we need a database with records that really exist in a real world scenario. I am going to continue to use the Adventure Database example database from Microsoft. As mentioned in my earlier post, more details regarding the database can be found from here. Also you can download database samples from codeplex with the following links: AdventureWorksLT 2005 Version, AdventureWorksLT 2008 Version. For a presentation model scenario I am going to use a part of the Data Model which involves Customer and Address.

And we will use the same sample as mentioned in my Presentation Model article.

image_thumb.png

Before beginning with the performance measurements, allow me to put some light on basic configurations and coding of the project. In this example the DataBinding is achieved  declaratively through DomainDataSource control. The DomainDataSourceControl code is as follows:

1
2
3
4
5
6
7
8
<riaControls:DomainDataSource AutoLoad="True" d:DesignData="{d:DesignInstance my:CustomerPresentationModel, CreateList=true}" Height="0"
                              LoadedData="customerPresentationModelDomainDataSource_LoadedData"
                              Name="customerPresentationModelDomainDataSource"
                              QueryName="GetCustomersWithAddressQuery" Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:ADVWORKDomainContext />
    </riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>

The next step is bind the the DomainDataSource to the Item Source of the DataGrid:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<sdk:DataGrid AutoGenerateColumns="False" Height="468"  ItemsSource="{Binding ElementName=customerPresentationModelDomainDataSource, Path=Data}" Margin="0,66,0,0" Name="customerPresentationModelDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Top">
    <sdk:DataGrid.Columns>
        <sdk:DataGridTextColumn x:Name="addressIDColumn" Binding="{Binding Path=AddressID}" Header="Address ID" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="addressLine1Column" Binding="{Binding Path=AddressLine1}" Header="Address Line 1" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="addressLine2Column" Binding="{Binding Path=AddressLine2}" Header="Address Line 2" Width="SizeToHeader" />
        <sdk:DataGridTemplateColumn x:Name="addressModifiedDateColumn" Header="Address Modified Date" Width="SizeToHeader">
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <sdk:DatePicker SelectedDate="{Binding Path=AddressModifiedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=AddressModifiedDate, StringFormat=\{0:d\}}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        <sdk:DataGridTextColumn x:Name="cityColumn" Binding="{Binding Path=City}" Header="City" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="customerIDColumn" Binding="{Binding Path=CustomerID, Mode=OneWay}" Header="Customer ID" IsReadOnly="True" Width="SizeToHeader" />
        <sdk:DataGridTemplateColumn x:Name="customerModifiedDateColumn" Header="Customer Modified Date" Width="SizeToHeader">
            <sdk:DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <sdk:DatePicker SelectedDate="{Binding Path=CustomerModifiedDate, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellEditingTemplate>
            <sdk:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=CustomerModifiedDate, StringFormat=\{0:d\}}" />
                </DataTemplate>
            </sdk:DataGridTemplateColumn.CellTemplate>
        </sdk:DataGridTemplateColumn>
        <sdk:DataGridTextColumn x:Name="emailAddressColumn" Binding="{Binding Path=EmailAddress}" Header="Email Address" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="firstNameColumn" Binding="{Binding Path=FirstName}" Header="First Name" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="lastNameColumn" Binding="{Binding Path=LastName}" Header="Last Name" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="phoneColumn" Binding="{Binding Path=Phone}" Header="Phone" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="postalCodeColumn" Binding="{Binding Path=PostalCode}" Header="Postal Code" Width="SizeToHeader" />
        <sdk:DataGridTextColumn x:Name="stateProvinceColumn" Binding="{Binding Path=StateProvince}" Header="State Province" Width="SizeToHeader" />
    </sdk:DataGrid.Columns>
</sdk:DataGrid>

Performance Snapshot - Before

Using Visual Studio Profiler Wizard with CPU sampling, the snapshot shows the elapsed time for calling the query and fetching the detail from the database. The straight forward approach to RIA service with Domain DataSource control is to retrieve all 800 records at one shot.

image_thumb_5.png


Well a detailed tutorial about the performance wizard in Visual Studio 2010 can be found here.

Performance Tips and Tricks

As the number of records returned by the query always have a performance impact over web so the following tips and tricks in combination with proper coding standards always makes a difference; let's discuss a few of them.

  • Pagination with LoadSize and PageSize

The first step of making improvements lies with applying paging to the data. Well paging works without a DomainDataSource control but it works better with a DomainDataSource control. So let's add a DataPager control to the page and bind the source to the Domaindatasource control.

1
2
3
<sdk:DataPager Height="26" Margin="0,0,0,44" Name="dpCustomers" PageSize="10" VerticalAlignment="Bottom"
               DisplayMode="FirstLastPreviousNextNumeric" AutoEllipsis="False"
               Source="{Binding Path=ItemsSource,ElementName=customerPresentationModelDataGrid}"/>

Although the paging option will start working but still we are loading everything from the database so we need to do some tweaks to the DomainDataSource control:

SNAGHTML1826281_thumb.png

As mentioned in the above image, LoadSize loads exactly the specified number of records with each hit to the Tier interaction. For the next 30 records it hits the database again.

image_thumb_4.png


So here with the above snapshot from Tier Interaction you can see that for each record after the 30th that the DomainDataSource control is sending fresh a request to fetch the next set.

While working with LoadSize and PageSize make sure that the PageSize of Datapager and DomainDataSource control are the same; here in this example it's defined as 10. Also make sure the page size is less than the LoadSize.

  • Limit Results of Query Operation

Instead of fetching all the details, try using QueryAttribute to limit the results from the query operation. The Domain Service at the server side supports an attribute to limit the result set. For example here in this example each call to the domain service returns a maximum of 30 results.

image_thumb_6.png


Well make sure that your loadsize and pagesize as mentioned in the above tips for Datapager are within this limit. Well it is worth mentioning that one of my readers was facing issues; the famous LoadOperation failed and on digging a bit we found that the real problem was the timeOut value. Once we set the ResultLimit, the project was run.

  • Cashing of output

Well this option really is not meant for real time data where changes are very frequent but you can always cache the output of a method call using attributes.

image_thumb_7.png

As shown above the OutputCacheLocation specifies the location where the resource needs to be cached.

Performance Snapshot - After

Well we talked much and now it's time for results; as mentioned above the TierInteraction gives you a complete picture of the time taken for each method call.

image_thumb_8.png

Comparison of Performance

The last call where you can compare both performance results lets you see a complete picture over the improvement.

image_thumb_9.png


The Delta here shows a sharp increment in performance of Tier Interaction for the GetCustomerWithAddress query method.

Conclusion

May be there are lots of room available for performance improvement such as exposing entities that need to be exposed to the client using a Presentation Model and a few tricks over native WCF service but for the time being I hope the above will make a difference. Keep providing me with your suggestions, ideas and comments.

Source Code and Performance Reports

Download Link - : RIAPerformanceTips.zip