Maurice de Beijer
Download the code
As was to be expected there were lots of announcements
around Silverlight at the last MIX conference in Las Vegas. One particular
product announcement is very interesting for Line Of Business (LOB) application
developers. This big announcement is all about the .NET RIA Services.
What are .NET RIA Services?
RIA, short for Rich Internet Applications, is a bit of an
umbrella term for all sorts of applications delivered through the browser. The
key aspect however is that they deliver some business function and are not just
about flashy graphics. Business applications tend to work with data and other
business resources so they are usually built in the standard N tier
architecture. If we take a look at this N tier architecture for the most common
type of business resource, the database, we typically see the Create, Read,
Update, Delete, (CRUD) pattern appear all the time. While implementing the CRUD
pattern in Silverlight isn’t extremely difficult the very fact that the
Silverlight application runs in the browser without direct database access and
all server communication is done asynchronously makes this harder than it needs
to be. This is exactly one of the problems the .NET RIA Services is trying to
solve. Of course there is more to the .NET RIA Services and the standard CRUD
operations is just one of the issues addressed. As we will see in this article it
addresses much more by including things like data validation, general
communication, keeping client and server code synchronized and more.
When evaluating the .NET RIA Services we should be looking
at it from two different perspectives. First of all it is a set of design guidelines
of how to create a RIA style application. Secondly it is a series of .NET
libraries and Visual Studio templates implementing the design guidelines. So
even if you don’t want to use the .NET RIA Services binaries, studying the
design is very useful for a Silverlight line-of-business (LOB) developer.
Another thing to keep in mind when evaluating the .NET RIA Services is that it
is not just about Silverlight but more general. The first samples may be with
Silverlight clients but a client could equally well be written in
ASP.NET/JavaScript, WPF or any other client that can call WCF services.
Some of the problems the .NET RIA Services tries to solve
When developing Silverlight LOB style applications
developers today are faced with a number of challenges not directly related to
the business problem they are trying to solve. Some of the problems a
Silverlight RIA developer faces include:
- Both the server and client side logic need to be
developed together but both are very separate from each other. Often when the
server side code has been updated the developer must manually update the WCF
service reference so the client side code is aware of the changes.
- It is not possible, at least without some
trickery, to use an assembly both in a regular .NET and a Silverlight assembly,
making it hard to share code between the client and the server.
- Using WCF to communicate to the server. The
asynchronous nature of WCF, or all external communication for that matter, when
used inside of Silverlight is an extra complicating factor.
- Data entered needs to be validated both on the
client, for immediate UI feedback, and again on the server, for security
purposes.
- The server has to be secured to prevent
unauthorized use by other than the intended client application.
All of these are common problems but really not related to
the actual business problem we are trying to solve. The .NET RIA Services tries
to solve these problems for us making it easier to develop LOB applications in
Silverlight.
Some of the techniques the .NET RIA Services uses to solve
these problems.
- The .NET RIA Services lets us work in such a way
that the server and client parts of our application are two sides of one single
application. This means we are not working in a Service Oriented Application
(SOA) style with a very disconnected application. Instead the .NET RIA Services
sees both parts of the application as one that just happens to be split into
parts by the network.
- Automatic, compile time, code generation is used
to connect the client with the server parts of the application. No need to
manually set a service reference or update it.
- By using file naming conventions code written in
the server part of the application can automatically be inserted into the
client part of the application. Again no need for the developer to do anything except
use a simple naming convention.
- Most of the WCF communication is completely
hidden from the developer and for the most part there is no need to worry about
the fact that all communication is asynchronous.
- Data validation rules are added to the entity
classes in the server part of the code. These are automatically added to the
client part and evaluated in both parts of the application.
- The standard simple declarative security model
we are all already familiar with can be used.
The main thing to remember when evaluating the .NET RIA
Services is that it is not intended for SOA style applications where the
service and the client are very loosely coupled. Where it is intended to be
used is where the client and service are tightly coupled; just like in desktop
client/server applications, only in this case the client and server parts of
the application are separated by an internet or intranet connection.
Installing the .NET RIA Services
Before we can start doing anything with the .NET RIA
Services we first need to install the library and Visual Studio templates. The
installer for the .NET RIA Services can be found here: http://go.microsoft.com/fwlink/?LinkId=144609.
However as the .NET RIA Services build on top of Silverlight 3 we first need to
install the current Silverlight 3 beta on our machine. Please keep in mind that
all of this is beta quality software and that Silverlight 3 and Silverlight 2
don’t support side by side installation so you may not want to install this on
your main development machine. The Silverlight 3 beta downloads can be found
here: http://silverlight.net/getstarted/silverlight3/default.aspx.
Note: If you want to install Silverlight 2 and 3 side by
side Amy Dullard has created a useful guide and batch file on how to do this.
You can read all about it here: http://blogs.msdn.com/amyd/archive/2009/03/18/switching-from-silverlight-3-tools-to-silverlight-2-tools.aspx
Comparing a Silverlight 2 style LOB application to a .NET RIA Services
application
The best way to understand the way a .NET RIA Services
application solves some of the problems we had when developing a regular
Silverlight 2 style LOB application. To demonstrate this I will use both
approaches to show a list of customers from the SQL Server Northwind sample
database using LINQ to SQL as the data access layer. The sample I am using is
deliberately very simple so as to focus on the .NET RIA Services and not be
distracted by other issues. Let’s first create the regular Silverlight
application and see what we need to do.
Building the standard Silverlight application.
When building the standard Silverlight application we need
to perform the following steps:
- Create a new Silverlight Application project.
- Add a LINQ to SQL item to the Web project.
- Add the Customers table to the LINQ to SQL
designer.
- Add a Silverlight enabled WCF Service to the Web
project.
- Add a GetCustomers function to the service class
returning all customers.
- Compile the Web project.
- Add a service reference in the Silverlight
project to the just created WCF service.
- Add a ListBox to the MainPage to display the
customer data.
- Create a GetCustomersCompleted handler setting
the page DataContext.
- In the Loaded event we need to create a WCF
proxy object to the WCF service.
- Set the event handler for the GetCustomersLoaded
event.
- And finally we need to call the
GetCustomersAsync method to load the actual data.
.jpg)
Image 1: The standard Silverlight application.
Private Sub MainPageLoaded() Handles Me.Loaded
Dim proxy = New NorthwindServiceClient()
AddHandler proxy.GetCustomersCompleted, _
AddressOf GetCustomersCompletedHandler
proxy.GetCustomersAsync()
End Sub
Private Sub GetCustomersCompletedHandler( _
ByVal sender As Object, _
ByVal e As GetCustomersCompletedEventArgs)
DataContext = e.Result
End Sub
Listing 1: The code required to populate the ListBox with a
WCF application.
Building the .NET RIA Services application.
When building the equivalent .NET RIA Services application
we need to take the following steps:
- Create a new Silverlight Navigation Application
- Add a LINQ to SQL item to the Web project.
- Add the Customers table to the LINQ to SQL
designer.
- Add a “Domain Service Class” to the Web project
selecting the Customer LINQ to SQL entity named NorthWindService.
- Compile the Web project.
- Add a ListBox to the HomePage to display the
customer data.
- In the HomePage Loaded event create a new
NorthwindContext object.
- Set the page DataContext.
- Call the LoadCustomers method.
.jpg)
Image 2: Our first .NET RIA Services application.
The thing to note is that using the .NET RIA Services there
are not only fewer steps to accomplish the same task but the steps left out are
all about WCF and its asynchronous nature, something we don’t want to worry
about when developing RIA applications. Additionally when we start adding
functionality to the NorthWindService this functionality automatically shows up
in the client NorthwindContext, all we need to do is compile, no longer do we
need to update the service reference.
Private Sub HomePageLoaded() Handles Me.Loaded
Dim ctx As New NorthwindContext
DataContext = ctx.Customers
ctx.LoadCustomers()
End Sub
Listing 2: The code required to populate the ListBox with a .NET
RIA Services application.
<ListBox ItemsSource="{Binding}"
Height="300">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding CompanyName}"
Width="250" />
<TextBlock Text="{Binding ContactName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Listing 3: The XAML ListBox
used in both samples.
.NET RIA Services communications
In the previous example we clearly had our Silverlight
client communicate with our web service. But as we never added a WCF Service
reference how does this actually work? The “magic” comes in two parts. The
first part was the “Link to ASP.NET server project” checkbox that was enabled
by default when we created the new .NET RIA Services project. Enabling this
creates a stronger coupling between the Silverlight client and Web server
project by allowing code generation. If you disabled this link when creating the
project you can later enable it at the bottom of the Silverlight tab in the
project settings.
The second part is the fact that the NorthwindService
“Domain Service Class” we added is decorated with the EnableClientAccess
attribute. Compiling the Web Server project will generate the Silverlight
client side counterpart of this NorthwindService class. This is the
NorthwindContext class we used in our Silverlight client application. If we
want to take a close look at the code that was generated for us in the client
project we need to click the “Show All Files” button in the Silverlight
project. Once we do this we will see a folder named Generated_Code. In the
sample project this folder contains a single file called
LOBUsingRiaServices.Web.g.vb. Inside this file we can find the generated code
for the Customer type and the NorthwindContext class as well as a
NorthwindContextEntityContainer class. I will go more in-depth into the
communication and code generation in a future article.
Working with different data sources
In the sample above I used LINQ to SQL to retrieve data from
the database. Using LINQ to SQL is easy so I like using it to get started, but
it is by no means a requirement. Out of the box the .NET RIA Services support
both LINQ to SQL and the Entity Framework. All “Domain Service Class” types
ultimately derive from the class DomainService and it is possible to derive
from this base class and return any enumeration of objects to the Silverlight
client application so this is fully extensible.
The user interface in the Silverlight Navigation Application
As you might have noticed both when adding the ListBox to
the HomePage and when running the Silverlight application the Silverlight
Navigation Application provides us with a default user interface to get started
with. This new project template is part of Silverlight 3 and can be used in any
Silverlight 3 application, not just when using the .NET RIA Services. In the
previous example we just added our data to the HomePage but we can do better
and use some of the new features to enhance our application.
.jpg)
Image 3: The customer DataGrid in the browser.
The first thing to note is that the left top of the main
page contains menu items that let us navigate to child pages. These child pages
are all user controls and by default stored in the Views folder. When the
application first loads the HomePage.xaml is loaded and displayed. This is done
inside the Frame control. The menu at the top consists of styled buttons. Another
interesting feature, again not .NET RIA Services specific, is the fact that the
URL in the browser changes. This allows us to bookmark individual pages and
navigate directly to them.
<UserControl x:Class="LOBUsingRIAServices.CustomerListPage"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
xmlns:web="clr-namespace:LOBUsingRIAServices.Web"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<data:DataGrid ItemsSource="{Binding}"
IsReadOnly="True">
</data:DataGrid>
</Grid>
</UserControl>
Listing 4: The CustomerListPage XAML.
Creating our own view page to display the customer list is
quite easy. The first step is to create a Silverlight User Control in the Views
folder and call it CustomerListPage. Drag a DataGrid control from the Toolbox
into the page and data bind the ItemsSource to the DataContext and set IsReadOnly
to true. Add the code we used before to load the Customers to the
CustomerListPageLoaded event as shown in listing 5.
Private Sub CustomerListPageLoaded() Handles Me.Loaded
Dim ctx As New NorthwindContext
DataContext = ctx.Customers
ctx.LoadCustomers()
End Sub
Listing 5: The CustomerListPageLoaded event handler.
With the CustomerListPage control in place all we need to do
is add a menu item to let the user navigate to our new page. This is very easy
to do, just go to the MainPage.xaml and copy one of the menu buttons changing
its Tag to point to our new CustomerListPage.xaml file and change the content
to “customers”. When we now run the Silverlight application we will see the
extra menu option appear and when we click it we will see a grid appear and,
after a brief pause when the data is loaded, the customers will show up.
Adding a DomainDataSource to load the customers
So far we have still been using a few lines of code to load
the customer’s collection from the server.
The .NET RIA Services also includes a DomainDataSource control so we can
load the data declaratively from our XAML.
The first thing we need to do is add a reference to the
System.Windows.Ria.Controls assembly. Next we can add the DomainDataSource to
the CustomerListPage and tell it to use the NorthwindContext type and the
LoadCustomers method to do the data loading for us. Next we need to change the
DataGrid ItemsSource binding to point to the Data property of the
DomainDataSource, this is done using element binding, another cool new
Silverlight 3 feature. In this simple example using the DomainDataSource was
actually more work than just using code. However it also enables features like
paging, sorting and filtering. I will cover these DomainDataSource features in
the next article.
<UserControl x:Class="LOBUsingRIAServices.CustomerListPage"
xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Ria.Controls"
xmlns:web="clr-namespace:LOBUsingRIAServices.Web"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<riaControls:DomainDataSource x:Name="CustomerDataSource"
LoadMethodName="LoadCustomers">
<riaControls:DomainDataSource.DomainContext>
<web:NorthwindContext />
</riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>
<data:DataGrid ItemsSource="{Binding Data, ElementName=CustomerDataSource}"
IsReadOnly="True"
MouseLeftButtonUp="DataGrid_MouseLeftButtonUp">
</data:DataGrid>
</Grid>
</UserControl>
Listing 6: The CustomerListPage using a DomainDataSource
control.
Adding a customer edit page
Adding another page to let us edit the selected customer is
quite easy to do. In this sample I am going to use the new ChildWindow control
that was added to Silverlight 3. The first step is to create another Silverlight
User Control and call it CustomerEditPage. Drag a DataForm control from the
toolbox onto this form. The DataForm control is a new control that lets us
quickly create edit forms. In this case the only property we need to change is
to data bind the CurrentItem to the DataContext.
.jpg)
Image 4: The default customer edit form.
<UserControl x:Class="LOBUsingRIAServices.CustomerEditPage"
xmlns:dataControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid x:Name="LayoutRoot" Background="White">
<dataControls:DataForm CurrentItem="{Binding}">
</dataControls:DataForm>
</Grid>
</UserControl>
Listing 7: The customer edit page XAML.
Next we need to add an event to the DataGrid in the
CustomersListPage to activate the child window. As the DataGrid does not have a
Click event the best place to do so is by adding a MouseLeftButtonUp event
handler. In this event handler we can now create a ChildWindow, set its
DataContext and add the previously created CustomerEditPage as the content.
When this is done all we need to to do is call the Show method to display the
popup.
Private Sub DataGrid_MouseLeftButtonUp( _
ByVal sender As System.Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs)
Dim dataGrid As DataGrid = CType(sender, DataGrid)
Dim editWindow As New ChildWindow
editWindow.DataContext = dataGrid.SelectedItem
editWindow.Content = New CustomerEditPage()
editWindow.Show()
End Sub
Listing 8: The code required to open the child edit window.
Even though we have not specified which fields to display,
both in the DataGrid and the DataForm all Customer properties show up in
alphabetical order and we can edit all fields. There are several ways to
control this behavior depending on the level of control we want. In a next
article I will go into more details about how to do so.
Conclusion
Even though I have hardly touched the surface of the .NET
RIA Services capabilities it is already obvious that using this library should
make life a lot easier for the average Silverlight RIA developer. Depending on
your needs you can use one of the data services supported out of the box or
create your own. In the next article I will dive deeper into the .NET RIA
Services and take a look at how code generation works, how we can add
validation to our classes and secure our application so that not everyone can
access it.
About Maurice de Beijer
Maurice de Beijer is an independent software consultant and
DevelopMentor instructor specializing in Silverlight, Windows Communication
Foundation, Workflow Foundation and .NET in general. He has been awarded the
yearly Microsoft Most Valuable Professional award since 2005. Besides
developing software Maurice also runs the user experience section of the
Software Development Network, the largest Dutch .NET user group. Maurice can be
reached through his website, www.TheProblemSolver.nl,
or by emailing him at Maurice@TheProblemSolver.nl.