[ This document was written for WCF Services Version 1 Service Pack 2 and might not be up to date
Please see Release Notes or Changelog for a list of changes since WCF RIA Services ]
This walkthrough provides you with an overview of many of the features in Open Ria Services. In this walkthrough, you create a Open Ria Services application that retrieves data from tables in the AdventureWorks OLTP sample database. First, you retrieve the data by specifying the LoadOperation. You then retrieve that data with the DomainDataSource control. You specify sorting, filtering, and paging for the data presentation control, and add a DataForm control to present a detailed view of the data. You apply validation rules to the fields and enable the user to edit data values. You restrict access to a domain operation to authenticated users. Finally, you define the association between two related tables and display the related data.
This and the other walkthroughs presented in the Open Ria Services documentation require several prerequisite programs, such as Visual Studio and the Silverlight Developer Runtime and SDK, be installed and configured properly, in addition to Open Ria Services and the Open Ria Services Toolkit. They also require installing and configuring SQL Server 2008 R2 Express with Advanced Services and installing the AdventureWorks OLTP and LT database.
Detailed instructions for the satisfaction of each of these prerequisites are provided by the topics within the Prerequisites for Open Ria Services node. Follow the instructions provided there before proceeding with this walkthrough to ensure that you encounter as few problems as possible when working through this Open Ria Services walkthroughs.
Creating and Setting Up the Solution
In this section, you create and set up the solution.
To create a new Open Ria Services application
In Visual Studio, create a new Open Ria Services project by selecting File, New, and then Project.
The New Project dialog box appears.
In the Installed Templates pane, expand the Visual Basic or Visual C# node, and select the Silverlight category.
Select the Silverlight Business Application template and name the application HRApp.
Click OK.
Notice the structure of the solution that is created:
The solution consists of two projects: a Silverlight client project named HRApp and an ASP.NET Web Application server project named HRApp.Web.
The default solution contains many automatically implemented features including navigation, user login and logout, and new user registration.
Build and Run (F5) the application and explore the default implementation.
Close your Web browser.
To set up the application
In Solution Explorer, in the client project, open MainPage.xaml.
In XAML view, find the TextBlock named ApplicationNameTextBlock.
As shown in the following markup, notice that the application name is retrieved from a resource.
Run the application and notice the new Employee List link on the upper right corner of the page, between the Home and About links. Click on it to display “Employee List” in the body of the page.
Displaying Data
In this section, you create an ADO.NET Entity Data Model for tables in the AdventureWorks sample database. Then, you create a domain service that exposes the entities and displays that data in the client project.
To add a data source
In Solution Explorer, right-click the HRApp.Web project, click Add, and then click New Item.
The Add New Item dialog box is displayed.
In the Data category, select the ADO.NET Entity Data Model template.
Change the name to AdventureWorks.edmx, and then click Add.
The Entity Data Model Wizard opens.
On the Choose Model Contents page, click Generate from database, and then click Next.
On the Choose Your Data Connection page, create a connection to the AdventureWorks database.
Name the entity connection settings AdventureWorks_DataEntities and then click Next.
On the Choose Your Database Objects page, expand the Tables node.
Add check marks next to the Employee, PurchaseOrderDetail, and PurchaseOrderHeader tables.
Name the model namespace AdventureWorks_DataModel and then click Finish.
The entity data model appears in the designer.
Build the solution.
To add a domain service object and query for data
In Solution Explorer, right-click the HRApp.Web project, click Add, and then click New Item.
The Add New Item dialog box is displayed.
In the Web category, select the Domain Service Class template.
Name the new item OrganizationService.
Click Add.
In the Add New Domain Service Class dialog box, select Employee, PurchaseOrderDetail, and PurchaseOrderHeader from the Entities list, and then select Enable editing for each entity.
Ensure that the Enable client access and Generate associated classes for metadata check boxes are selected.
Click OK.
OrganizationService.cs/vb and OrganizationService.metadata.cs/vb files are added to the project.
Open the OrganizationService.cs/vb file.
Notice that query, insert, update, and delete methods have been created for each entity. A query method is always created for an entity. The insert, update, and delete methods were added because Enable editing was selected.
Customize the GetEmployees() query method to return employees sorted by EmployeeID by replacing the generated code with the following code.
Public Function GetEmployees() As IQueryable(Of Employee)
Return Me.ObjectContext.Employees.OrderBy(Function(e) e.EmployeeID)
End Function
Building the solution generates the Domain Context and entities in the client project.
Open EmployeeList.xaml.
From the Toolbox, drag a DataGrid control to Design view just after the TextBlock control.
Dragging a DataGrid to Design view adds a reference to the System.Windows.Controls.Data assembly and adds the sdk prefix to the Page element.
Change the default values for the DataGrid control by removing the Height and Width properties, making it read-only, setting it to automatically generate columns, and setting its minimum height.
Instantiate the OrganizationContext class and load employee data by adding the following code to EmployeeList.xaml.cs/vb.
The OrganizationContext class is automatically generated in the client project based on the OrganizationService class in the server project.
Partial Public Class EmployeeList
Inherits Page
Dim _OrganizationContext As New OrganizationContext
Public Sub New()
InitializeComponent()
Me.dataGrid1.ItemsSource = _OrganizationContext.Employees
_OrganizationContext.Load(_OrganizationContext.GetEmployeesQuery())
End Sub
'Executes when the user navigates to this page.
Protected Overrides Sub OnNavigatedTo(ByVal e As System.Windows.Navigation.NavigationEventArgs)
End Sub
End Class
publicpartialclassEmployeeList:Page{OrganizationContext _OrganizationContext =newOrganizationContext();publicEmployeeList() {InitializeComponent();this.dataGrid1.ItemsSource=_OrganizationContext.Employees;_OrganizationContext.Load(_OrganizationContext.GetEmployeesQuery()); } // Executes when the user navigates to this page.protectedoverridevoidOnNavigatedTo(NavigationEventArgs e) { }}
Run the application.
Click the Employee List link to see the DataGrid.
To add a custom query
In the HRApp.Web project, open OrganizationService.cs/vb.
Add a new method named GetSalariedEmployees by adding the following code to the body of the class.
Public Function GetSalariedEmployees() As IQueryable(Of Employee)
Return Me.ObjectContext.Employees.Where(Function(e) e.SalariedFlag = True).OrderBy(Function(e) e.EmployeeID)
End Function
Run the application and click the Employee List link.
Notice that all of the displayed employees have the SalariedFlag value checked. Employees with EmployeeID 1, 2, and 4 no longer appear in the list because they are not salaried.
To add a domain data source
Open EmployeeList.xaml.
From the Toolbox, drag the DomainDataSource control to Design view, just before the DataGrid. The DomainDataSource might appear at the bottom of the list of controls.
[!TIP] If the DomainDataSource control is not in the Toolbox, right click in the Toolbox, and click Choose Items. Under the Silverlight Components tab check DomainDataSource, and click OK.
When you drag the DomainDataSource control to Design view, a reference with the prefix riaControls is created for the System.Windows.Controls namespace in the Page element. Also, a data source icon appears in the lower-left corner of Design view.
For C# solutions, add the following namespace declaration to the XAML file.
xmlns:ds="clr-namespace:HRApp.Web"
For Visual Basic solutions, add the following namespace declaration to the XAML file.
xmlns:ds="clr-namespace:HRApp"
Name the DomainDataSource control employeeDataSource and set the LoadSize, AutoLoad, and query method by replacing the existing XAML with the following XAML.
In the constructor, remove or comment out the code to instantiate the OrganizationContext instance, the call to GetSalariedEmployeesQuery(), and the code to set the DataGrid control’s ItemsSource property.
You no longer need to explicitly load data, since the DomainDataSource will do this automatically.
'Dim _OrganizationContext As New OrganizationContext
Public Sub New()
InitializeComponent()
'Me.dataGrid1.ItemsSource = _OrganizationContext.Employees
'_OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery())
End Sub
//OrganizationContext _OrganizationContext = new OrganizationContext();publicEmployeeList(){InitializeComponent(); //this.dataGrid1.ItemsSource = _OrganizationContext.Employees; //_OrganizationContext.Load(_OrganizationContext.GetSalariedEmployeesQuery());}
Run the application and click the Employee List link.
The application works the same as before.
To add sorting, filtering, and paging to the data source
Open EmployeeList.xaml.
In the DomainDataSource, add the following SortDescriptors to specify how data is sorted in the DataGrid.
This XAML shows how to sort the VacationHours column in ascending order.
Run the application and click the Employee List link.
You see only 5 rows of filtered data per page and pager controls below the DataGrid.
Creating a Master/Detail View
In this section, you use the DataForm control from the Silverlight Toolkit to provide a detailed view of the data. By default, the Silverlight Business Application project template contains the System.Windows.Controls.Data.DataForm.Toolkit.dll binary in the Libs folder.
Run the application and click the Employee List link.
The DataForm displays details of the item selected in the DataGrid.
Updating the Database
When you select the Enable editing check box in the Add New Domain Service Class dialog box, methods are generated in the domain service layer to update, insert, and delete the entity. In this section, you add edit buttons to the user interface of the employee list to enable users to execute these operations.
To update a record
Open the EmployeeList.xaml file.
After the DataForm control, add the following XAML to add a Submit button.
Add the following event handler for the button click event.
The submitButton is disabled to prevent the user from submitting the change again as the operation is being processed.
Private Sub submitButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
submitButton.IsEnabled = False
employeeDataSource.SubmitChanges()
End Sub
Add an event handler for the SubmittedChanges event that checks whether the submit operation completed successfully and enables submitButton.
Private Sub employeeDataSource_SubmittedChanges(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SubmittedChangesEventArgs)
If (e.HasError) Then
MessageBox.Show(String.Format("Changes were not saved: {0}", e.Error.Message))
e.MarkErrorAsHandled()
End If
submitButton.IsEnabled = True
End Sub
privatevoidemployeeDataSource_SubmittedChanges(object sender,SubmittedChangesEventArgs e){if (e.HasError) {MessageBox.Show(string.Format("Changes were not saved: {0}",e.Error.Message));e.MarkErrorAsHandled(); }submitButton.IsEnabled=true;}
Run the application and click the Employee List link.
Select an employee and click the pencil icon in the upper-right hand corner of the data form to enable editing.
You can now modify any editable field.
Make changes to the employee data and click OK.
Click the Submit button to save the data.
Changes are saved to the database on the server only when you click the Submit button.
To add custom methods to a domain service
In the HRApp.Web server project, open OrganizationService.cs/vb. File
Add the following custom method named ApproveSabbatical.
Public Sub ApproveSabbatical(ByVal current As Employee)
Me.ObjectContext.Employees.AttachAsModified(current)
current.CurrentFlag = False
End Sub
Add the following event handler for the button click event that calls the ApproveSabbatical domain operation.
Private Sub approveSabbatical_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim luckyEmployee As Employee
luckyEmployee = dataGrid1.SelectedItem
luckyEmployee.ApproveSabbatical()
employeeDataSource.SubmitChanges()
End Sub
Run the application and click the Employee List link.
Click the Approve Sabbatical button and note that the CurrentFlag check box for the selected employee is cleared.
Validating Data
The DataForm control can show validation errors from the data access layer (DAL). For example, if you enter a non-integer value in the VacationHours field, a validation error is displayed. The following illustration shows an example of the validation behavior.
When you select the Generate associated classes for metadata check box in the New Domain Service Class dialog box, a file that contains metadata is created. In this walkthrough, the metadata file is named OrganizationService.metadata.cs/vb. In this section, you add validation attributes to this file. The validation rules will be enforced in the client and server projects.
You also create a user interface to allow the addition of new employee records to the database. The validation rules that you added in the previous sections will automatically be applied in the new user interface.
To add basic validation
In the HRApp.web project, open OrganizationService.metadata.cs/vb.
Add the following attributes to the Gender and VacationHours properties.
<Required()> _
Public Property Gender As String
<Range(0, 70)> _
Public Property VacationHours As Short
Run the application and click the Employee List link.
Select an employee and click the pencil icon in the upper-right hand corner of the data form to enable editing.
Enter a value in the Vacation Hours field that is not within the valid range (0-70) and move the focus to another control.
You see a validation error for vacation hours.
Delete the value in the Gender field and move the focus to another control.
You see a validation error for gender.
To add custom validation
In Solution Explorer, right-click the HRApp.Web project, click Add, and then click New Item.
The Add New Item dialog box is displayed.
In the Code category, select the Code File template.
Name the new item OrganizationService.shared.cs or OrganizationService.shared.vb.
Files that end with .shared.cs or .shared.vb are available in both the client and server projects. Shared files enable you to run the same validation rule in both projects. After you build the solution in a later step, look in the hidden Generated_Code folder on the client, and you will see the OrganizationService.shared.cs/vb file.
Click Add.
To create a customized validation class that verifies the values assigned to the Gender property, add the following code to the shared file.
Imports System
Imports System.ComponentModel.DataAnnotations
Public Module GenderValidator
Public Function IsGenderValid(ByVal gender As String, ByVal context As ValidationContext) As ValidationResult
If gender = "M" OrElse gender = "m" OrElse gender = "F" OrElse gender = "f" Then
Return ValidationResult.Success
Else
Return New ValidationResult("The Gender field only has two valid values 'M'/'F'", New String() {"Gender"})
End If
End Function
End Module
usingSystem;usingSystem.ComponentModel.DataAnnotations;namespaceHRApp.Web{publicstaticclassGenderValidator {publicstaticValidationResultIsGenderValid(string gender,ValidationContext context) {if (gender =="M"|| gender =="m"|| gender =="F"|| gender =="f") {returnValidationResult.Success; }else { return new ValidationResult("The Gender field only has two valid values 'M'/'F'", new string[] { "Gender" });
} } }}
Open OrganizationService.metadata.cs/vb.
Add the following custom validation attribute to the Gender property.
<Required()> _
<CustomValidation(GetType(GenderValidator), "IsGenderValid")> _
Public Property Gender As String
Add the following code to create a new Employee instance and handle either committing the new instance or canceling the insertion.
Partial Public Class EmployeeRegistrationWindow
Inherits ChildWindow
Public Sub New()
InitializeComponent()
NewEmployee = New Employee
addEmployeeDataForm.CurrentItem = NewEmployee
addEmployeeDataForm.BeginEdit()
End Sub
Public Property NewEmployee As Employee
Private Sub OKButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles OKButton.Click
Me.addEmployeeDataForm.CommitEdit()
Me.DialogResult = True
End Sub
Private Sub CancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs) Handles CancelButton.Click
NewEmployee = Nothing
addEmployeeDataForm.CancelEdit()
Me.DialogResult = False
End Sub
End Class
Add the following code to handle the button click event and display the EmployeeRegistrationWindow.
Private Sub addNewEmployee_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim addEmp As New EmployeeRegistrationWindow()
AddHandler addEmp.Closed, AddressOf addEmp_Closed
addEmp.Show()
End Sub
private void addNewEmployee_Click(object sender, RoutedEventArgs e)
{
EmployeeRegistrationWindow addEmp = new EmployeeRegistrationWindow();
addEmp.Closed += new EventHandler(addEmp_Closed);
addEmp.Show();
}
Add the following method to handle the closed event for EmployeeRegistrationWindow.
Private Sub addEmp_Closed(ByVal sender As Object, ByVal e As System.EventArgs)
Dim emp As EmployeeRegistrationWindow = sender
If Not emp.NewEmployee Is Nothing Then
Dim _OrganizationContext As OrganizationContext = employeeDataSource.DomainContext
_OrganizationContext.Employees.Add(emp.NewEmployee)
employeeDataSource.SubmitChanges()
End If
End Sub
Modify the InsertEmployee method by adding the following code.
Public Sub InsertEmployee(ByVal employee As Employee)
employee.HireDate = DateTime.Now
employee.ModifiedDate = DateTime.Now
employee.VacationHours = 0
employee.SickLeaveHours = 0
employee.rowguid = Guid.NewGuid()
employee.ContactID = 1001
employee.BirthDate = New DateTime(1967, 3, 18)
If ((employee.EntityState = EntityState.Detached) _
= False) Then
Me.ObjectContext.ObjectStateManager.ChangeObjectState(employee, EntityState.Added)
Else
Me.ObjectContext.Employees.AddObject(employee)
End If
End Sub
Run the application and click the Employee List link.
Click the Add Employee button.
The EmployeeRegistrationWindow opens.
Add data in the window and select the Salaried check box.
Click OK.
Refresh the page and ensure that the new employee appears in the DataGrid.
Authenticating Users
In this section, you restrict access to the ApproveSabbatical method to only authenticated users.
To add authentication
Open OrganizationService.cs/vb.
Add the RequiresAuthentication attribute to the ApproveSabbatical method.
When you apply the RequiresAuthentication attribute to a domain operation, you ensure that only authenticated users can call the operation. If an anonymous user clicks the Approve Sabbatical button, the operation is not executed.
<RequiresAuthentication()> _
Public Sub ApproveSabbatical(ByVal current As Employee)
Me.ObjectContext.Employees.AttachAsModified(current)
current.CurrentFlag = False
End Sub
[RequiresAuthentication]
public void ApproveSabbatical(Employee current)
{
// Start custom workflow here
this.ObjectContext.Employees.AttachAsModified(current);
current.CurrentFlag = false;
}
using OpenRiaServices.Client.Authentication;
using HRApp.LoginUI;
Modify the approveSabbatical_Click method and add a LoggedIn handler to check whether the user is authenticated.
Private Sub approveSabbatical_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If WebContext.Current.User IsNot Nothing AndAlso WebContext.Current.User.IsAuthenticated Then
Dim luckyEmployee As Employee = dataGrid1.SelectedItem
luckyEmployee.ApproveSabbatical()
employeeDataSource.SubmitChanges()
Else
AddHandler WebContext.Current.Authentication.LoggedIn, AddressOf Current_LoginCompleted
Dim newWindow As New LoginRegistrationWindow
newWindow.Show()
End If
End Sub
Private Sub Current_LoginCompleted(ByVal sender As Object, ByVal e As AuthenticationEventArgs)
Dim luckyEmployee As Employee = dataGrid1.SelectedItem
luckyEmployee.ApproveSabbatical()
employeeDataSource.SubmitChanges()
RemoveHandler WebContext.Current.Authentication.LoggedIn, AddressOf Current_LoginCompleted
End Sub
Run the application and click the Employee List link.
Select an employee record and click the Approve Sabbatical button.
You are redirected to the login window.
Click the Register now link.
Complete the required fields to create a new account.
Click OK.
You are logged in with that account and your name appears in a bar below the navigation links.
Displaying Related Data
With Open Ria Services you can easily work with data from related tables. In this section, you add a new Silverlight Page and display data from the PurchaseOrderHeader and PurchaseOrderDetail tables. You can also customize the data modification operations so the related data is modified together. For an example of modifying the data in related tables through a domain operation, see Compositional Hierarchies.
To display data from related tables
In the HRApp project, right-click the Views folder, click Add, and then click New Item.
Add a new Silverlight Page named PurchaseOrders.xaml.
Add the Include and Composition attributes to the PurchaseOrderDetails property in the PurchaseOrderHeaderMetadata class.
<Include()> _
<Composition()> _
Public Property PurchaseOrderDetails As EntityCollection(Of PurchaseOrderDetail)
[Include]
[Composition]
public EntityCollection<PurchaseOrderDetail> PurchaseOrderDetails { get; set; }
Open OrganizationService.cs/vb.
Change the GetPurchaseOrderHeaders method so that the related records in PurchaseOrderDetails are also retrieved with the query.
Public Function GetPurchaseOrderHeaders() As IQueryable(Of PurchaseOrderHeader)
Return Me.ObjectContext.PurchaseOrderHeaders.Include("PurchaseOrderDetails").OrderBy(Function(p) p.PurchaseOrderID)
End Function
public IQueryable<PurchaseOrderHeader> GetPurchaseOrderHeaders()
{
return this.ObjectContext.PurchaseOrderHeaders.Include("PurchaseOrderDetails").OrderBy(p => p.PurchaseOrderID);
}
Open PurchaseOrders.xaml.
On the Data menu, click Show Data Sources to open the Data Sources window.
Drag the PurchaseOrderHeader node to the design surface for PurchaseOrders.xaml.
A DataGrid with columns from the PurchaseOrderHeader table appears.
In the Data Sources window, expand the PurchaseOrderHeader node.
Drag the PurchaseOrderDetails node that is located inside the PurchaseOrderHeader node to the design surface just under the DataGrid for PurchaseOrderHeader.
A DataGrid with columns from the PurchaseOrderDetails table appears.
In XAML view, find the DataGrid controls for both PurchaseOrderHeader and PurchaseOrderDetails.
Remove the Width=”400” property from each DataGrid so that it will fill the available width.
Before the PurchaseOrderHeader DataGrid, add the following TextBlock control to label the data.
<TextBlock Text="Order Headers"></TextBlock>
Before the PurchaseOrderDetails DataGrid, add the following TextBlock control to label the data.
<TextBlock Text="Order Details"></TextBlock>
To limit the number of records that are retrieved, add the following filter descriptor to the DomainDataSource control.
Run the application and click the Purchase Orders link.
Select different records in the PurchaseOrderHeader DataGrid.
Notice that the related PurchaseOrderDetail records are automatically displayed.
Next Steps
This walkthrough has given you a tour of many of the features in Open Ria Services. To learn the details of specific areas, see the other walkthroughs in this documentation.