How to: Create a Domain Service that uses POCO-defined Entities
[WCF RIA Services Version 1 Service Pack 2 is compatible with either .NET framework 4 or .NET Framework 4.5, and with either Silverlight 4 or Silverlight 5.]
This topic shows how to use WCF RIA Services to create a domain service that uses Plain Old CLR Objects (POCO). The objective is to show how to construct a very elementary POCO-based RIA Services application that explains, in particular, exactly what happens when the RIA Services tools (wizards and dialogue boxes) are used during various steps in the procedure. Data served up from POCO can be used to free an application from dependencies on backend databases for the purpose of portability or data security, or for testing purposes. The automatically generated client code is fully supported by RIA Services with POCO-defined entities as it is with Linq to SQL or to Entity Framework. The RIA Services domain service is, in fact, agnostic with regard to the data source, so this POCO class could be replaced later by a component that accesses data from some other source such as a database without changing the domain service itself.
For a walkthrough that outlines how to use a code first approach to initialize a database that is then made available to a Silverlight client for editing and viewing, see Walkthrough: Creating a RIA Service with the Code First Approach.
The procedure described here requires several prerequisite programs, such as Visual Studio 2010 and the Silverlight Developer Runtime and SDK, be installed and configured properly, in addition to WCF RIA Services. But the WCF RIA Services Toolkit is not required. Detailed instructions for the satisfaction of each of these prerequisites are provided by the topics within the Prerequisites for WCF RIA Services node. Follow the instructions provided there for the programs required before proceeding with this how-to to ensure that you encounter as few problems as possible.
Create a RIA Services solution
Create a new RIA Services project in Visual Studio 2010 by selecting File, New, and then Project.
The New Project dialog box appears.
Select the Silverlight Application template from Silverlight group of the Installed Templates and name the new project RIAServicesPocoExample.
Click OK.
The New Silverlight Application dialog box appears.
Select the Enable WCF RIA Services check box near the bottom of the dialog box. Checking this box creates a RIA Services link between the client project and the server project. To enable this connection this tool adds the following references to the client project:
System.ComponentModel.DataAnnotations
System.Runtime.Serialization
System.ServiceModel.dll
System.ServiceModel.DomainServices.Client
System.ServiceModel.DomainServices.Client.Web
System.ServiceModel.Web.Extensions
System.Windows.Browser
Click OK to create the solution.
The solution contains two projects: a client project and a server project.
RIAServicesPocoExample: the client project that contains the Silverlight code that you use to create the presentation tier.
RIAServicesPocoExample.Web: the server project that contains that middle tier code.
Create a Domain Service
Right-click the server project, select Add and New Item.
In the list of categories, select Web and then select the Domain Service Class template.
Name the class SovereignDomainService.cs (or SovereignDomainService.vb).
Click Add.
The Add New Domain Service Class dialog box appears.
Make sure that the Enable client access box is checked.
Note that the only option available on the dropdown menu for the Available DataContext/ObjectContext classes is the <empty Domain Service class> entity and that you cannot select the Generated associated classes for metadata check-box because there are no data contexts available with which to associate the service.
Click OK.
This wizard does several things. It generates an empty SovereignDomainService class in a new SovereignDomainService.cs (or SovereignDomainService.vb) file with associated attributes and using statements. It also adds four assembly references to the service project and configuration elements to the Web.config file.
To observe this, open the SovereignDomainService.cs (or SovereignDomainService.vb) file if it is has not opened automatically already. Notice that the file has the following characteristics:
using statements have been added:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server
The SovereignDomainService class derives from DomainService class, which is an abstract base class in the RIA Services framework. This is the base class for all the domain service exposed in RIA Services.
The SovereignDomainService class is marked with the EnableClientAccessAttribute attribute to indicate that it is visible to the client tier.
Notice that the following references have been added to the service project by the wizard:
System.ComponentModel.DataAnnotations
System.Runtime.Serialization
System.ServiceModel.DomainServices.Hosting
System.ServiceModel.DomainServices.Server
Finally open the Web.config file and examine the new elements that have been added by the wizard.
<configuration> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="DomainServiceModule" preCondition="managedHandler" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> <system.web> <httpModules> <add name="DomainServiceModule" type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </httpModules> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> </configuration>
Except for the configuration element targeting the .NET Framework 4.0, each of these elements has been added by the Add New Domain Service Class dialog. These elements enable various Internet Information Server (IIS) hosting and ASP.NET options.
The wizard adds a <modules> element in the <system.webserver> section that is required by IIS 7 hosting.
The wizard adds an <add> element of the <httpModules> element within the system.web section that is required by IIS 6 hosting.
RIA Services domain services are Windows Communication Foundation (WCF) services and when hosted with ASP.NET need to be hosted in ASP.NET Compatibility Mode. This requirement cannot be set in code and must be specified in the Web.config file. The ASP.NET Compatibility Mode is enabled by setting the aspNetCompatibilityEnabled property to true in the <ServiceHostingEnvironment>element of the <system.serviceModel> section.
Add the POCO class
This procedure describes how to indicate to the RIA Services framework that a POCO class is to be used as an entity type. Entity types provide data structures to the application’s data model and each entity type is required to have a unique entity key. The structure of the data is specified by the set of properties it contains. The entity key is provided by deputizing a property (or set of properties) that must provide a unique name for each entity object that distinguishes it from other entities of the same type. This is typically specified by using metadata of some variety. This procedure does this by applying the [Key] attribute to a property and it is, in fact, the application of this attribute that tells the RIA Services framework that instances of the POCO class are entity objects.
Open up the SovereignDomainSevice.cs file.
Within the scope of the RIAServicesPocoExample.Web namespace, below the stubbed out SovereignDomainService class, add the following code for the Sovereign class:
public class Sovereign { [Key] public int UniqueId { get; set; } public string Name { get; set; } public string House { get; set; } public string Dominion { get; set; } public int ReignStart { get; set; } public int ReignEnd { get; set; } public string Sobriquet { get; set; } }
In this example, the UniqueId property is the entity key that provides a unique name for each entity object of type Sovereign. The [Key] attribute is defined in the System.ComponentModel.DataAnnotations assembly, which has already been added to the server project, as has the using statement for the corresponding namespace that contains it. The entity key could also be specified in a metadata file or in other ways, but this is a convenient way to indicate this directly in the POCO class.
Add the FetchSovereigns() method to the Sovereign class that returns a list of Sovereign instances:
public List<Sovereign> FetchSovereigns() { List<Sovereign> sovereignList = new List<Sovereign> { new Sovereign() {UniqueId = 1, Name = "John", House = "Plantagenet", Dominion = "Angevin Empire", ReignStart = 1167, ReignEnd = 1216, Sobriquet = "Lackland" }, new Sovereign() {UniqueId = 2, Name = "Charles", House = "Stuart", Dominion = "England, Scotland, & Ireland", ReignStart = 1625, ReignEnd = 1649, Sobriquet = "The Martyr" }, new Sovereign() {UniqueId = 3, Name = "William", House = "Dunkeld", Dominion = "Scotland", ReignStart = 1165, ReignEnd = 1249, Sobriquet = "The Lion" }, new Sovereign() {UniqueId = 4, Name = "Elizabeth", House = "Tudor", Dominion = "England", ReignStart = 1555, ReignEnd = 1609, Sobriquet = "The Virgin Queen" }, new Sovereign() {UniqueId = 5, Name = "Ivan", House = "Vasilyevich", Dominion = "Russia", ReignStart = 1533, ReignEnd = 1584, Sobriquet = "The Terrible" }, new Sovereign() {UniqueId = 6, Name = "Charles", House = "Valois", Dominion = "France", ReignStart = 1380, ReignEnd = 1422, Sobriquet = "The Mad" } }; return sovereignList; }
Define the Domain Service
This procedure describes how to create a query in the domain service that will be accessible from the client to retrieve data from the POCO defined entities. The RIA Services framework needs to know which of its methods are to be made available on the client as queries and there is a naming convention used to achieve this. Method names that start with Get and that return either an IEnumerable<EntityType> or an IQueryable<EntityType> are recognized by the RIA Services framework as queries.
Tip
IQueryable<T> derives from IEnumerable<T>. Use IEnumerable<T> for in-memory collections such as our POCO defined entities and use IQueryable<T> when accessing an underlying or remote data source like a SQL database.
Add the GetSovereign() method to the SovereignDomainService class.
public IEnumerable<Sovereign> GetSovereigns() { Sovereign sovereign = new Sovereign(); return sovereign.FetchSovereigns(); }
This returns the all of the sovereign entities from the collection. But, typically, we only want to return a subset of the entities. To illustrate this, modify this query to return only the sovereigns from this list that reigned during the Middle Ages, that is where sovereign.ReignEnd <= 1500. The following code does this:
public IEnumerable<Sovereign> GetSovereignsByReignEnd(int ReignedBefore) { Sovereign sovereign = new Sovereign(); return sovereign.FetchSovereigns().Where<Sovereign>(p => p.ReignEnd <= 1500); }
Build (Ctrl+Shift+B) the solution to create the auto-generated client proxy code.
In Solution Explorer, select the RIAServicesPocoExample client project and click the Show All Files icon at the top of the window and examine the RIAServicesPocoExample.Web.g.cs file in the Generated_Code folder. Examine the auto-generated code in this file and take note of the following items:
A WebContext class that derives from the WebContextBase class is generated and used to manage the application context.
A Sovereign class that derives from the Entity class is generated for the entity exposed by the domain service. The Sovereign entity class in the client project matches the Sovereign entity on the server.
A SovereignDomainContext class that derives from the DomainContext class is generated. This class has a method named GetSovereignsByReignEndQuery that corresponds to the query method created in the domain service.
For more information on the automatic code generation, see the Client Code Generation topic. For information on how to customize code generation, see the Customizing Generated Code topics.
Display the query result in the Silverlight client
Open MainPage.xaml.
From the Toolbox on the left, drag a DataGrid control to within the Grid element in XAML view.
Dragging the DataGrid control from the Toolbox causes a namespace using System.Windows.Controls statement to be added to the MainPage.xaml.cs file and references to the System.Windows.Controls.Data and System.Windows.Controls.Data.Input assemblies to be added automatically to the client project.
Warning
If you add the DataGrid without dragging it from the Toolbox, you must add the references to the assemblies to the client project and the using statement manually in the code behind file.
Change the value of the AutoGeneratedColums to True, name the DataGrid element SovereignGrid, and adjust the Height and Width as shown in the following XAML.
<Grid x:Name="LayoutRoot" Background="White"> <sdk:DataGrid AutoGenerateColumns="True" Height="200" HorizontalAlignment="Left" Margin="157,86,0,0" Name="SovereignGrid" VerticalAlignment="Top" Width="600" /> </Grid>
Open the MainPage.xaml.cs file.
Add using (C#) or Imports (Visual Basic) two statements: using RIAServicesPocoExample.Web; and the using System.ServiceModel.DomainServices.Client;.
The RIAServicesPocoExample.Web namespace is the namespace containing the generated code for the client project in the RIAServicesPocoExample.Web.g.cs (or RIAServicesPocoExample.Web.g.vb).
To instantiate the SovereignDomainContext, add the line of code private SovereignDomainContext _sovereignContext = new SovereignDomainContext(); in the MainPage class.
Retrieve customer entities by calling the GetSovereignsQuery method with LoadOperation<TEntity>: LoadOperation<Sovereign> loadOp = this._sovereignContext.Load(this._sovereignContext.GetSovereignsByReignEndQuery(1500));.
Bind the entities loaded to the DataGrid with SovereignGrid.ItemsSource = loadOp.Entities;.
To summarize, the MainPage.xaml.cs file should now contain the following code:
//Namespaces added using RIAServicesPocoExample.Web; using System.ServiceModel.DomainServices.Client; namespace RIAServicesPocoExample { public partial class MainPage : UserControl { private SovereignDomainContext _sovereignContext = new SovereignDomainContext(); public MainPage() { InitializeComponent(); LoadOperation<Sovereign> loadOp = this._sovereignContext.Load(this._sovereignContext.GetSovereignsByReignEndQuery(1500)); SovereignGrid.ItemsSource = loadOp.Entities; } } }
Run (F5) the application.
You should see the table in the browser displaying the properties only of the medieval (whose reigns ended before 1500 Common Era) sovereigns (in alphabetic order).
See Also
Tasks
Walkthrough: Retrieving and Displaying Data From a Domain Service