RIA Services is a new Microsoft framework for developing n-tier Line of Business (LoB) applications. RIA Services make it easier to build Silverlight applications that communicate with a server, which is the focus of this article. As a developer, RIA Services provides automatic code generation for common scenarios where you need to perform CRUD
operations on data and have a consistent model to validate data across tiers. The following sections explain how to get started with RIA Services, specify a data source and a UI, and automatically generate code for CRUD operations. Let me show you the big picture of RIA Services from an architectural perspective first.
Understanding RIA Services Architecture
RIA Services solves problems in n-tier application design for Silverlight applications by making it easier to serialize and stream objects across a network, sharing common validation among tiers, and automating asynchronous communication. To compare current development practices to what RIA Services offers, consider a common architecture, shown in Figure 1, using Web services to allow a Silverlight application to communicate with the server.
![Figure 1. N-Tier architecture with Web services.](https://codemag.com/Article/Image/0907131/Figure 1.tif)
To the left of Figure 1 is a Silverlight control in a browser, which is the presentation tier. Following the network connection to the right, the Web service resides on the server, which is a separate tier where Business Logic Layer (BLL) and Data Access Layer (DAL) code reside; the sidebar, Layers and Tiers, explains the difference between layers and tiers.
LoB application development is very much about managing data properly. In the Web services scenario in Figure 1, you can see how data management must be handled in each tier. Perhaps you have a framework that makes it easier to stream data across the wire, but you still have manual work to do in each tier to manage CRUD operations.
Observe in Figure 1 the separate validation code in each tier of the application, because in many cases you can’t make the assumption that presentation tier validation is sufficient. Especially in a Web services scenario, any client can send bad data.
A common problem with network communications is timeouts and managing perceived performance in the presentation tier in the face of latency. A common way to handle this is via asynchronous calls where your thread returns to the UI immediately and the application later handles the return thread from the Web service and marshals the call back onto the UI thread to update the screen. There are patterns and framework support for managing this, but you must write the code yourself. The solid line between tiers demonstrates the fact that communication is synchronous by default.
“Up to now, developers have been fortunate to have the tools to build sophisticated n-tier architectures easier than earlier generations of software technology. However, this scenario becomes even easier with RIA Services.”
Up to now, developers have been fortunate to have the tools to build sophisticated n-tier architectures easier than earlier generations of software technology. However, this scenario becomes even easier with RIA Services. Figure 2 shows how RIA Services simplifies data management, validation, and communications.
![Figure 2: RIA Services architecture.](https://codemag.com/Article/Image/0907131/Figure 2.tif)
The single Data Objects block in Figure 2 demonstrates that the UI and BLL/DAL tiers use the same objects. RIA Services supports LINQ to SQL, ADO.NET Entity Framework, and POCO out of the box, reducing the amount of work that you must do to move data across the network. Similarly, Validation has a single block, showing that the same validation rules defined in the BLL will be used in the UI. RIA Services build a proxy for the UI layer to encapsulate asynchronous communication, represented by the dashed line between UI and middle tier, freeing you from the extra coding.
The rest of this article shows you how RIA Services can help reduce the amount of code you write for n-tier LoB applications, starting with an explanation of how to start a Silverlight project that uses RIA Services.
Starting RIA Services in a Silverlight 3 Project
The easiest way to use RIA Services is via the Silverlight Application wizard in Visual Studio 2008. The following steps will walk you through the process of creating a Silverlight application that uses RIA Services. For this article I’ll use a C# project, but you can do the same with a Visual Basic project:
![Figure 3: New Project window.](https://codemag.com/Article/Image/0907131/Figure 3.tif)
![Figure 4: New Silverlight Application window.](https://codemag.com/Article/Image/0907131/Figure 4.tif)
If you’ve built Silverlight applications before, the New Silverlight Application window in Figure 4 will be familiar. The Host the Silverlight application in a new Web site check box creates a new ASP.NET Web site with sample pages containing the new Silverlight control. You can change the project name to anything you like, but this demo will use the default. This demo will also use the default of ASP.NET Web Application Project as the project type; the alternative being an ASP.NET Web Site project. What’s new, for Silverlight 3, in the New Silverlight Application window is the Link options section. To use RIA Services, you must check the box for Link to ASP.NET server project, which establishes the proper assembly references and any other settings required for using RIA Services. If you forget to check the Link to ASP.NET server project box and change your mind later, you can open the Properties window for the Silverlight project and there is an option called ASP.NET server project link on the Silverlight tab that you can change.
![Figure 5: New RIA Services application solution.](https://codemag.com/Article/Image/0907131/Figure 5.tif)
A couple items in Figure 5 are worthy of mention: assembly references and Toolbox controls. Notice that the Silverlight project, RIAServicesDemo, includes a reference to System.Windows.Ria; the assembly containing RIA Services types. In addition, you can see several new controls in the Toolbox such as DataForm, DataPager, and DatePicker that are new in Silverlight 3.
There are more new controls, many that are members of the Silverlight Toolkit, which is a set of LoB controls for Silverlight 2 available at http://silverlight.codeplex.com. Later sections of this article will introduce you to a couple of the new data controls.
“RIA Services supports multiple types of DAL in your architecture, including LINQ to SQL, LINQ to Entities, or custom.”
Adding a Middle Tier with Domain Service
Now that you have a project in-place, you need to set up a data source and build a middle tier, which will consist of a Business Logic Layer (BLL) and a Data Access Layer (DAL). As shown in Figure 2, RIA Services supports multiple types of DAL in your architecture, including LINQ to SQL, LINQ to Entities, or custom. The Domain Service resides in the Web application, RiaServicesDemo.Web, which is your middle-tier project. This article uses LINQ to Entities for a DAL and then builds the BLL on top of that with a Domain Service.
Building the DAL
To get started, add an entity data model to the Web application, RiaServicesDemo.Web. As a refresher, you can right-click on the RiaServicesDemo.Web project, select Add, New Item, select ADO.NET Entity Model, and work through the wizard to add an AdventureWorks.edmx file that includes all of the database tables. You now have an entity data model named AdventureWorksLT_DataEntities and are ready to add a Domain Service.
Adding the BLL
The Domain Service builds a BLL that connects to a DAL based on the AdventureWorksLT_DataEntities entity data model just created. To build the Domain Service, right-click on the RiaServicesDemo.Web project, select Add, New Item, select Domain Service Class, name the file CustomerService.cs, and click OK. The New Domain Service Class, shown in Figure 6, has parameters to configure the behavior of your Domain Service, including enabling client access, DAL selection, which entities to base the service on, and which type of operations that the service supports.
![Figure 6: New Domain Service Class window.](https://codemag.com/Article/Image/0907131/Figure 6.tif)
Check Enable client access to make the class discoverable by the Silverlight application. The Available DataContexts/ObjectContexts lists contains a list of LINQ to SQL DataContext and LINQ to Entities ObjectContext classes available in the Web application. Notice that the AdventureWorksLT_DataEntities entity data model is automatically selected as the DAL, since it is the only DataContext or ObjectContext available.
This application works with Customers so you need to check the Customer entity. Check Enable editing to enable insert, update, and delete support. In this article I won’t check Enable editing so the application will have read-only support.
I did not check Generate associated classes for metadata and won’t use it in this example; you use it to add metadata to auto-generated entities such as those generated by LINQ to Entities, which you don’t want to change directly. Click OK to generate the CustomerService class, shown in Listing 1.
“The benefit is that all the classes are generated for you automatically and all you need to do is code your business logic.”
Inside the DomainService Class
The CustomerService class in Listing 1 derives from LinqToEntiesDomainService<AdventureWorksLT_DataEntities**>,** is decorated with the EnableClientAccess attribute and contains a GetCustomers method, which are the direct result of settings in the New Domain Service Class window. Since the DAL is based on an ADO.NET Entity Framework data model, AdventureWorksLT_DataEntities, the base class is LinqToEntitiesDomainService<AdventureWorksLT_DataEntities**>.** Had the DAL been based on a LINQ to SQL entity model, the base class would have been LinqToSqlDomainService<AdventureWorksLT_DataEntities**>.** The EnableClientAccess attributes makes the CustomerService class discoverable by the Silverlight application, the implementation which you’ll soon read about in a later section of this article.
Since you selected the Customer entity, there is a GetCustomers method that will return an IQueryable<Customer> collection. The implementation of GetCustomers is particularly interesting because it references the base class Context property, which contains an ObjectContext reference for AdventureWorksLT_DataEntities. In its initial implementation, GetCustomers returns all customers in the database. However, what you might like to know is that you can now add business logic before and after the call. This is much like the business object implementation that many n-layer applications implement. The benefit is that all the classes are generated for you automatically and all you need to do is code your business logic. In addition to the auto-generated methods, you’ll want to add more methods for custom operations such as the need to get a single customer as shown below:
public IQueryable<Customer> GetCustomerByID(
int customerID)
{
return
from cust in Context.Customer
where cust.CustomerID == customerID
select cust;
}
Just add the code above to the CustomerService class. Clients will find the new GetCustomerByID method, shown above, because the CustomerService class has the EnableClientAccess attribute. The Data Service is now ready to be consumed by a client.
Consuming a Domain Service in Silverlight 3
The Data Service, created in the last section, is automatically available to the Silverlight application; made possible because Link to ASP.NET server project was checked, as shown in Figure 4. First, build the entire solution to generate a proxy file in the Silverlight application. You can see the proxy by selecting the Silverlight application, RiaServicesDemo, and then clicking on the Show All Files toolbar button in Solution Explorer. You’ll see a Generated Code folder containing a file named RiaServicesDemo.Web.g.cs, which is the proxy for the CustomerService Domain Service.
The generated proxy is named CustomerContext, which corresponds to the CustomerService Domain Service. You will have an XxxContext proxy for every XxxService Domain Service in your Web application. What is particularly useful about this proxy is a set of Load methods for each of the Get methods in the Domain Service. RIA Services uses a convention based on pre-defined prefixes to determine which methods to generate a proxy Load method for, including any methods that start with Get, Fetch, Find, Query, or Select. Therefore, the Domain Service method GetCustomer could have been named SelectCustomer and the proxy would have still been generated with the corresponding LoadCustomer method.
To see how to consume data, I’ll display Customer data on a Silverlight user control. Add a DataGrid to the MainPage.xaml page and a new handler to the UserControl Loaded event in RiaServicesDemo, as shown in Listing 2.
In Listing 2, the grid will show all customers and the Loaded event handler, UserControl_Loaded, will retrieve data and load the grid. Remember to remove line wrapping in Listing 2 namespaces, which I added for article formatting. Listing 3 shows how the UserControl_Loaded event handler is implemented to retrieve and display customer data.
The first thing to point out in Listing 3 is the using declaration for RiaServicesDemo.Web, which is the namespace generated for the proxy. Next, you can see the implementation of the UserControl_Loaded event handler. After getting a reference to the CustomerContext proxy, the code assigns the Customers property of the proxy to the CustomerDataGrid, but the data does not bind at that point in time. The data really binds asynchronously, courtesy of the LoadCustomer method. Figure 7 shows the Silverlight application in the browser, displaying customer data.
![Figure 7: Silverlight application consuming and displaying customer data via RIA Services.](https://codemag.com/Article/Image/0907131/Figure 7.tif)
“The beauty of the asynchronous binding pattern is that the Load method, LoadCustomer, of the proxy, CustomerContext, saves you from having to write the asynchronous code yourself.”
Remember, the Silverlight user control is running in the browser, but the Web application is where the Data Service, CustomerService, resides. To avoid timeouts and to keep the UI in the browser responsive, LoadCustomer performs an asynchronous call to the GetCustomer method on the server. When the data arrives back at the browser, LoadCustomer takes care of binding it to the CustomerDataGrid. This is why you might see a delay between the time the page starts and when the data actually appears when running the application. The beauty of the asynchronous binding pattern is that the Load method, LoadCustomer, of the proxy, CustomerContext, saves you from having to write the asynchronous code yourself.
Summary
Looking at n-tier implementations with Web services and comparing that with RIA Services should give you an idea of the types of problems the RIA Services solves and the points of productivity to be gained. You now know what tools are available and how to build an RIA Services project. The discussion led you through the techniques used with Data Services to create a middle-tier set of components, holding BLL and DAL layers of your architecture. You then learned how to consume the Data Service via an auto-generated proxy in a Silverlight application. This should give you a good idea of the benefits of RIA Services and how it will help you build LoB applications faster with Silverlight 3.
Looking Forward
This article demonstrated how easy it is to produce and consume data via RIA Services, but there is much more to explore. RIA Services also allows full CRUD operations, shared validation between tiers, and additional metadata support. Keep an eye on Nikhil Kothari’s and Brad Abram’s blogs at http://www.nikhilk.net and http://blogs.msdn.com/brada, respectively. Of course, don’t forget to come back to CODE Magazine for more articles on RIA Services.
Listing 1: The CustomerService Domain Service class
namespace RiaServicesDemo.Web
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Ria;
using System.Web.Ria.Data;
using System.Web.DomainServices;
using System.Data;
using System.Web.DomainServices.LinqToEntities;
// Implements application logic using the
// AdventureWorksLT_DataEntities context.
// TODO: Add your application logic to these
// methods or in additional methods.
[EnableClientAccess()]
public class CustomerService :
LinqToEntitiesDomainService
<AdventureWorksLT_DataEntities>
{
// TODO: Consider
// 1. Adding parameters to this method and
// constraining returned results, and/or
// 2. Adding query methods taking different
// parameters.
public IQueryable<Customer> GetCustomer()
{
return this.Context.Customer;
}
}
}
Listing 2: Configuring a Silverlight User Control UI for displaying customers
<UserControl
xmlns:data="clr-namespace:System.Windows.Controls;
assembly=System.Windows.Controls.Data"
x:Class="RiaServicesDemo.MainPage"
xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/">http://schemas.microsoft.com/winfx/2006/xaml/</a>
presentation"
xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</a>"
Width="400" Height="300"
Loaded="UserControl_Loaded">
<Grid x:Name="LayoutRoot" Background="White">
<data:DataGrid Name="CustomerDataGrid">
</data:DataGrid>
<data:DataGrid Name="CustomerDataGrid">
</data:DataGrid>
</Grid>
</UserControl>
Listing 3: Retrieving and displaying customer data with the CustomerContext proxy
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using RiaServicesDemo.Web;
namespace RiaServicesDemo
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void UserControl_Loaded(
object sender, RoutedEventArgs e)
{
var ctx = new CustomerContext();
CustomerDataGrid.ItemsSource = ctx.Customers;
ctx.LoadCustomer();
}
}
}