Chapter 35. Wcf Ria Services

WHAT'S IN THIS CHAPTER?

  • Understanding WCF RIA Services

  • Creating a domain service

  • Exposing data

  • Consuming WCF RIA Services in Silverlight

In Chapter 31 you saw how WCF provided a standardized means of communication in a technology-agnostic manner. WCF RIA Services (commonly referred to as just RIA Services) is a layer on top of WCF that provides a prescriptive pattern and framework for designing data-driven applications that consume data from a server. WCF RIA Services currently target Silverlight applications, but with a view to support additional presentation technologies. This chapter looks at how to use RIA Services to create an end-to-end Silverlight application.

GETTING STARTED

RIA Services is currently most closely associated with and focused toward Silverlight for the client platform, so you will start by creating a Silverlight project. You will find a Business Application template (as shown in Figure 35-1 under the Silverlight category), which will create all the solution structure required to start with RIA Services (a Silverlight project, an ASP.NET web application, and the RIA Services link between the two).

Figure 35-1

Figure 35-1. Figure 35-1

This creates a Silverlight project and an ASP.NET project — with the structure of the ASP.NET project shown in Figure 35-2.

Figure 35-2

Figure 35-2. Figure 35-2

The ASP.NET project already supports and implements some basic functionality using the RIA Services pattern. You'll note from Figure 35-2 that there is a Services folder and a Models folder in the project. The Services folder already contains two domain services (AuthenticationService and UserRegistrationService) for providing authentication and user registration operations to the client. The Models folder contains two data classes (User and RegistrationData) that are passed between the server and the client. You will also find a Shared folder under the Models folder, which has a file called User.shared.vb or User.shared.cs that contains code to be shared between the server and the client projects.

As demonstrated in Chapter 22, the Silverlight and the ASP.NET projects are linked together such that the Silverlight application is copied to somewhere in the ASP.NET project when the project/solution is compiled (configured in the project properties for the ASP.NET project). However, by introducing RIA Services into the picture you will now have another link between the projects. This link is configured in the project properties of the Silverlight project to select the ASP.NET project that will be acting as the server that it will be communicating with, and from which project the RIA Services build task will generate the code based on, as shown in Figure 35-3.

Figure 35-3

Figure 35-3. Figure 35-3

This link will already be set up by using the Business Application project template; however, if you have an existing Silverlight project or web application that you want to use RIA Services with you can manually link the projects together by linking the Silverlight project to an ASP.NET project with this option.

Now you are ready to start writing some code. The example you work through in this chapter demonstrates some of the key concepts of the RIA Services pattern. Your aim will be to expose customer data and operations from the server and make the data and operations accessible from the client.

You will be using your Entity Framework model of the AdventureWorksLT database that you created back in Chapter 29. It's not necessary to pass entities from your model back and forth between the server and the client, and in many cases it's considered bad practice. The entities are essentially a model of your data layer, which conceptually is not something that the presentation layer should be exposed to. Whether or not you pass entities or POCO (Plain Old CLR Objects) objects of your own design (referred to as presentation model types in RIA Services) back and forth is a decision you will have to make, dependant on many factors. Using entities will make development much faster, but will also be less flexible than using presentation model types. RIA Services works just as well using presentation model types as it does with entities, despite more work being involved in initially creating the Domain Services. Therefore, the best practice would be to use presentation model types as the data transfer mechanism; however, we will focus on using entities in this chapter because they provide the easiest means to get started.

Domain Services

Now that you have your server and client projects connected via RIA Services, it's time to expose some data and operations from the server, which you will consume from your client at a later point.

Start by assuming that the Entity Framework model of the AdventureWorksLT database from Chapter 29 has been added to your ASP.NET project (including adding the connection string that it uses to the web.config file). If not, do so now.

Warning

Ensure that you compile the ASP.NET project before continuing on to the next step, otherwise the Domain Service Class Wizard will not display your entity model in the available DataContexts/ObjectContexts drop-down list.

To expose the customer data from your entity model you need to add a domain service to your ASP.NET project. The best place in your project to add this service is under the Services folder. Add a new item to this folder, and select Domain Service Class under the Web category as the item template (as shown in Figure 35-4). You will be using this service to serve up customer data, so call it CustomersService (.cs or .vb).

Figure 35-4

Figure 35-4. Figure 35-4

Clicking OK initiates the Domain Service Class Wizard, shown in Figure 35-5.

Figure 35-5

Figure 35-5. Figure 35-5

If it hasn't automatically selected your Entity Framework model for the AdventureWorksLT database in the Available DataContexts/ObjectContexts drop-down list, select it now. All the entities from your entity model will be displayed in the list. Here you can select one or more entities that you want to expose from your domain service. When you select an entity the wizard creates a domain operation to return a collection of that entity from the domain service. If you select the Enable Editing option for an entity, the wizard also creates Insert, Update, and Delete domain operations for that entity on the domain service.

Note

If you're using POCO/presentation model types instead of Entity Framework or LINQ-to-SQL types, you can select the <empty domain service class> option from the Available DataContexts/ObjectContexts drop-down list and implement the domain operations yourself.

You should ensure that the Enable client access checkbox is checked. This will ensure that the EnableClientAccess attribute is applied to the service when it is created, which means that code will be generated on the client by the RIA Services code generator to enable it to access the domain service.

You will note that there is also a Generate associated classes for metadata checkbox on the wizard. Metadata classes enable you to add special attributes to properties on the data class being transferred (such as how the entity should be created on the client, and data validation rules) without the need to modify the source object (which is important when, for example, you regenerate your object's code with a code generator or an ORM). Instead, you can apply attributes to the properties in a metadata class that correlate with the properties on the actual class, and these attributes will control how the associated entity is created on the client, and apply other attributes (such as validation rules) to the entity it creates on the client. You will find a more thorough explanation of metadata classes in Chapter 23 (Dynamic Data).

It's not essential to generate metadata classes for your entities, although it does provide a degree of control over the data passed between the server and the client, so it is recommended that you create them.

Select the Customer entity, select the Enable Editing checkbox for it, and ensure both the Enable client access and Generate associated classes for metadata checkboxes are selected. Clicking OK creates the domain service and metadata classes for you.

DOMAIN OPERATIONS

Domain operations are operations on a domain service that can be called from the client. The types of domain operations that exist in a domain service can each be considered to be a CRUD (Create, Read, Update, Delete) operation, an invoke operation, or a custom operation.

The names and/or the method signature of these operations are convention-based so RIA Services can implicitly determine what type of operation it is and generate the correct corresponding operation in the client project. If for some reason you don't want to use the given conventions you can decorate the operation (that is, the method) with an attribute to specify what type of operation is being represented.

Note

Some people prefer to decorate their operations even when they follow the naming/signature convention in order to explicitly define what type of operation is being represented.

Let's now take a look at what domain operations have been generated for you in your domain service by the wizard, and what other types of operations you can create.

Query Operations

When you open the CustomersService (.cs or .vb) file you will see that the basic CRUD operations have been implemented for your Customer entity. The default Read (aka Get or Query) operation returns a collection of entities with the following method signature:

VB
Public Function GetCustomers() As IQueryable(Of Customer)
C#
public IQueryable<Customer> GetCustomers()

Note how the GetCustomers operation returns an IQueryable collection of the Customer entity. This is one of the most powerful features of RIA Services, in that this feature enables you to write a LINQ query on the client that can be used to filter and shape the entities that it wants returned. This LINQ query is actually serialized and sent to the server before being executed. Where you will see the power of this feature is when you try to implement filtering/paging/grouping/sorting on the client. Instead of requiring a raft of complex operations on the server to implement these behaviors, you only need the one simple operation that returns an IQueryable collection, and then a LINQ query can be provided by the client to filter/shape the results on the server before returning them. Alternatively, you can modify the Get operation and add your own parameters to it, which the operation can use to filter and shape the results to return to the client.

Insert/Update/Delete Operations

The insert (also known as create), update, and delete operations are automatically called when you submit a change set to the server (based on the actions taken upon the results of a query operation on the client), and cannot be called explicitly from the client. These actions are covered later in the chapter, but for now take a look at the operations that have been implemented for you automatically by the Domain Service Class Wizard and how they are implemented. The operations that were created for you have the following method signatures:

VB
Public Sub InsertCustomer(ByVal customer As Customer)
Public Sub UpdateCustomer(ByVal currentCustomer As Customer)
Public Sub DeleteCustomer(ByVal customer As Customer)
C#
public void InsertCustomer(Customer customer)
public void UpdateCustomer(Customer currentCustomer)
public void DeleteCustomer(Customer customer)

Each of these accepts an entity of the given type, and performs the appropriate server-side action on that entity. They each have a convention for its naming, they must not return a value, and their method signature must accept an entity as the only parameter. The naming convention and alternative attribute is as follows:

  • The method name of insert operations must start with Insert, Create, or Add. Otherwise, apply the Insert attribute to the method.

  • The method name of update operations must start with Update, Change, or Modify. Otherwise, apply the Update attribute to the method.

  • The method name of delete operations must start with Delete or Remove. Otherwise, apply the Delete attribute to the method.

Other Operation Types

Other types of operations supported by RIA Services (but not fully detailed here) are as follows:

  • An invoke operation is essentially the same as a service operation in a standard WCF Service (that is, a method exposed by the service). Invoke operations are created as methods on the domain context on the client, and are called immediately (that is, they aren't queued until changes are submitted to the server).

  • A custom operation is one that is called at any time on the client, but whose execution is deferred on the server until a changeset is submitted. Custom operations act upon entities, and are actually created as methods on their associated entities on the client in addition to being created as methods on the domain context that is generated for the domain service.

CONSUMING A DOMAIN SERVICE IN SILVERLIGHT

Before you look at actually consuming a domain service in the Silverlight project, take a look at what RIA Services has generated for you. As mentioned earlier in the chapter, RIA Services automatically generates code in the Silverlight project to communicate with the server. This code is generated in a folder called Generated_Code, which is not added to the project, but can be seen if you select the Silverlight project in the Solution Explorer and click the Show All Files button. Code is generated by RIA Services in files under this folder (as shown in Figure 35-6), with the primary code generated file being <web project name>.g.vb (or .cs). For example, for the sample project, the primary code gen file is Chapter35Sample.Web.g.vb (or .cs).

Figure 35-6

Figure 35-6. Figure 35-6

You can open this file to inspect its contents and see what the code generator has created for you. Of particular interest, you will note that the entities (or presentation model classes) exposed by domain services in the web project will have a corresponding class generated in this file (decorated with attributes from the metadata classes or the classes themselves). You will also find that for each domain service on the client there will be a corresponding domain context class created, which handles communicating with the domain service from the client. The operations exposed by a domain service will be created on the corresponding domain context, and you call the operations on the domain context instead of attempting to reference the domain service itself.

Note

Note that corresponding operations for the Insert/Update/Delete operations on the domain service are not created on the domain context, because these operations are managed by the changeset. Changes made to a collection of entities retrieved from the server via a query operation are handled by the RIA Services framework in a changeset, and when SubmitChanges is called on the domain context, the framework will handle calling the Insert/Update/Delete operations on the domain service as required.

If you follow standard RIA Services naming conventions, a domain service called CustomersService in the web project will result in a corresponding domain context in the Silverlight project called CustomersContext.

Now, attempt to populate a data grid with a list of customers retrieved from the server. You have two primary means of doing so: either using a declarative XAML-based approach or a code-based approach. The XAML-based approach is the easiest way to get started, so this section will use that approach.

The easiest way to get started with the XAML-based approach is to simply use the Data Sources window (as detailed in Chapter 18), and drag and drop an entity exposed by a domain context from this window and onto your page. You will find that a data source has already been created in your project for each data context created by the RIA Services code generator (as shown in Figure 35-7), so you don't need to worry about creating the data sources yourself.

Figure 35-7

Figure 35-7. Figure 35-7

For this example you will be consuming the CustomersService that exposes the Customer entities from the Entity Framework model on the server, so drag and drop the Customer entity (from the CustomersContext data source, that is, the selected item in Figure 35-7) onto the page. This will create a data grid with a column for each property on the entity. Now if you look at the XAML you can see how it ties together:

<riaControls:DomainDataSource AutoLoad="True" QueryName="GetCustomersQuery"
        Name="CustomerDomainDataSource" Height="0" Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:CustomersContext />
    </riaControls:DomainDataSource.DomainContext>
</riaControls:DomainDataSource>

<data:DataGrid AutoGenerateColumns="False" Height="250"
        ItemsSource="{Binding ElementName=CustomerDomainDataSource, Path=Data}"
        Name="CustomerDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected">
    <data:DataGrid.Columns>
        <!--This code has been removed for purposes of brevity-->
    </data:DataGrid.Columns>
</data:DataGrid>

The DomainDataSource control being used is a part of the RIA Services framework, and provides the bridge to declaratively access the domain context in XAML. The DomainDataSource control is specifying that it should use the CustomersContext (which corresponds to the CustomersService on the server) as its domain context, and that the query operation that should be called on this domain context is GetCustomersQuery. The AutoLoad property on the DomainDataSource control is set to True, meaning this query will be called as soon as the page is loaded. Finally, the ItemsSource property is set on the data grid, where it uses element name binding to bind to the DomainDataSource control and use that as its source of data.

Now you can run your project, and you will find that the data grid is automatically populated with the results of the query from the server (as shown in Figure 35-8).

Figure 35-8

Figure 35-8. Figure 35-8

As discussed previously, the advantage of returning an IQueryable from a domain service operation is that RIA Services enables you to specify filtering, sorting, grouping, and paging options — all of which will be performed on the server. This is also very easy to do in XAML by specifying descriptors on the DomainDataSource — let's take a look at performing each of these in turn.

Add a textbox to the page that will automatically filter the customers by the company name, and call it searchTextBox. Now you can add a filter descriptor to the DomainDataSource that specifies the name of the property to filter (PropertyPath) and the operator specifying how the matching will be done (Operator). You can then add a ControlParameter to the filter descriptor, which links to the textbox (by providing the name of the textbox), uses the text in the textbox as the search criteria (by providing the name of the property on the textbox to get the value from), and runs the filter each time the text is changed (by providing the name of the event on the textbox that will invoke the filtering when raised).

<riaControls:DomainDataSource AutoLoad="True" QueryName="GetCustomersQuery"
                              Name="CustomerDomainDataSource" Height="0" Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:CustomersContext />
    </riaControls:DomainDataSource.DomainContext>

    <riaControls:DomainDataSource.FilterDescriptors>
        <riaControls:FilterDescriptor PropertyPath="CompanyName"
                        Operator="Contains"
                        Value="{Binding ElementName=searchTextBox, Path=Text}" />
    </riaControls:DomainDataSource.FilterDescriptors>
</riaControls:DomainDataSource>

Sorting is automatically handled by the data grid (click the column headers to sort by that column), and if the results are paged it will automatically go back to the server to get the new page of results according to the current page and sort criteria. You can, however, specify the initial sorting using sort descriptors on the DomainDataSource, by providing the name of the property to sort on, and the sort direction:

<riaControls:DomainDataSource AutoLoad="True" QueryName="GetCustomersQuery"
                              Name="CustomerDomainDataSource" Height="0" Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:CustomersContext />
    </riaControls:DomainDataSource.DomainContext>

    <riaControls:DomainDataSource.SortDescriptors>
        <riaControls:SortDescriptor PropertyPath="CompanyName" Direction="Ascending" />
    </riaControls:DomainDataSource.SortDescriptors>
</riaControls:DomainDataSource>

Grouping again is handled in a similar manner by providing group descriptors, and simply providing the name of the property to group on:

<riaControls:DomainDataSource AutoLoad="True" QueryName="GetCustomersQuery"
                              Name="CustomerDomainDataSource" Height="0" Width="0">
    <riaControls:DomainDataSource.DomainContext>
        <my:CustomersContext />
    </riaControls:DomainDataSource.DomainContext>

    <riaControls:DomainDataSource.GroupDescriptors>
        <riaControls:GroupDescriptor PropertyPath="SalesPerson" />
    </riaControls:DomainDataSource.GroupDescriptors>
</riaControls:DomainDataSource>

Paging the data in the grid (to display, for example, 20 customers at a time) is easy with the DataPager control. Add the control to your page, bind its Source property to the DomainDataSource control, and provide its PageSize property with the number of items to be displayed in the data grid:

<data:DataPager PageSize="20"
                Source="{Binding Data, ElementName=CustomerDomainDataSource}"/>

Note that the page size specifies how many items should be displayed in the grid, not how many items should be retrieved from the server. If you just set the PageSize property the entire collection will still be retrieved from the server and paged on the client instead. To retrieve just a single page of items at a time and go back to the server to retrieve more items when navigating between pages, you will need to set the LoadSize property on the DomainDataSource control. Generally, you will want to set both properties to the same value. Now, it will retrieve and display a single page of items, and it will request and display a new page of items from the server each time you navigate to a new page with the DataPager control.

In the background, any changes you make to the data in the data grid (such as adding rows, deleting rows, and updating values) will be tracked in a changeset by the RIA Services framework. Submitting these changes back to the server is a case of calling the SubmitChanges() method on the domain context. Add a button to the page called SubmitButton. In its Click event handler (in the code-behind), add the following line of code:

VB
CustomerDomainDataSource.SubmitChanges()
C#
CustomerDomainDataSource.SubmitChanges();

Clicking the button will now submit any changes you've made back to the server.

Note

You can also reject any changes made using the RejectChanges() method on the DomainDataSource control.

The final page that implements loading, filtering, sorting, grouping, paging, and saving the data is shown in Figure 35-9.

Figure 35-9

Figure 35-9. Figure 35-9

As you can see, RIA Services is an extremely powerful framework for managing data, greatly simplifying functionality that was once complex to implement, and making it very quick and easy to create very functional business applications.

Note

The DomainDataSource control makes it very easy to consume data from RIA Services in a Silverlight application; however, at times you may wish to interact with the domain service in code instead. This is possible by creating an instance of the corresponding domain context and using the methods on it. However, note that communication with the domain service is performed asynchronously, requiring your code to be structured accordingly.

SUMMARY

In this chapter you learned how WCF RIA Services can vastly simplify architecting and developing an end-to-end data-driven Silverlight application, through its combination of prescriptive design patterns, code generation, and feature-rich framework. RIA Services provides many more features than described here, including decorating classes and their properties with attributes (such as validation rules which RIA Services enforces), using metadata classes (i.e., classes associated with the entities being passed between the server and the client that attributes can be applied to and projected onto the associated entities such that the original entities don't need to be modified), sharing code between the server and the client, built-in authentication and security functionality, and much more. However, this chapter should help you get started using RIA Services to provide a means for communicating between your Silverlight application and the server.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.235.176