Understanding the WCF in ‘WCF RIA Services’

At PDC 2009 we (RIA Services Team) announced the re-branding of Microsoft .NET RIA Services to Windows Communication Foundation (WCF) RIA Services.   We backed this branding change with a new Preview release, that has RIA Services built heavily on the WCF stack.

In this post I plan to talk briefly about the motivation behind aligning RIA Services with WCF and then dig deep into how exactly RIA Services consumes WCF.

RIA Services on WCF: Best of both worlds

Ever since we announced RIA Services at MIX '09, we have heard strong customers feedback that they would like a consolidated services story from Microsoft. Acting on that feedback, over the last few months RIA Services has spent a significant amount of effort aligning closely with WCF. The Data Services team at their end has been working on a similar alignment with WCF as well.

By centering all our service offerings around WCF we are maximizing developer knowledge transfer and skill reuse, both in the short and the long term.

For more details on the alignment and the motivation behind it please also check out this post by the WCF team.

image

I strongly believe that with WCF RIA Services our users get the best of both worlds -

  • They get all the simplicity and productivity of the RIA Services Prescriptive Programming Model and Tooling
  • And if need be, they can dig deep into our services infrastructure and harness all the power and flexibility that WCF has to offer 

Understanding RIA Services use of WCF

To help users better understand how RIA Services uses WCF, in the following section I walkthrough what happens under the covers when a user creates a simple DomainService and then communicated with it.

1. ‘Add new DomainService Class’

Lets assume an application developer opens up the ‘Add new Domain Service Class’ item template and adds a new Domain Service.

The item template, besides producing a skeletal Domain Service Class, adds the right assembly references and registers an Http modules in the Web.Config. By default it registers an httpModule for Cassini (for Visual Studio F5 experience) and one for IIS, as shown below.

 <?xml version="1.0"?>
 <configuration>
  
   <system.web>
      <httpModules> 
        <add name="DomainServiceModule"  
 type ="System.Web.Ria.Services.DomainServiceHttpModule,  
 System.Web.Ria"   /> 
      </httpModules> 
     <compilation debug="true" targetFramework="4.0" /> 
   </system.web>
   
   <system.webServer>
     <validation validateIntegratedModeConfiguration="false"/>
     <modules runAllManagedModulesForAllRequests="true">
        <add name="DomainServiceModule" preCondition="managedHandler" 
 type ="System.Web.Ria.Services.DomainServiceHttpModule,  
 System.Web.Ria"   /> 
     </modules>
     <validation validateIntegratedModeConfiguration="false" />
   </system.webServer>
  
 

2. Domain Service Code

The developer then goes ahead and adds Business logic to his DomainService.The methods exposed via the DomainService can be broken into two broad categories –

CRUD operations – Query, Update, Named Update and Delete operations. These operations follow the RIA Services prescriptive guideline and rely on the RIA Services framework. This is added functionality that RIA Services introduces on top of WCF and is not available to Core WCF Services.

Service Operation/ Invoke Operations - These are [Invoke] operation in the RIA Services terminology and Service Operations in WCF terminology. These methods are independent of the RIA Services concept of ChangeSet (ChangeSet applies only to the CRUD operations above) and are ‘Online/Direct’ methods that communicate with the Server immediately when invoked.

Below is the code for the OrganizationService DomainService we use in our canonical RIA Services walkthrough.

 namespace HRApp.Web
 {
     [EnableClientAccess()]
     public class OrganizationService : 
         LinqToEntitiesDomainService<AdventureWorks_DataEntities>
     {
         #region CRUD    
         public IQueryable<Employee> GetEmployee(){…}        
         public void InsertEmployee(Employee employee) {…}        
         public void UpdateEmployee(Employee currentEmployee) {…}        
         public void DeleteEmployee(Employee employee) {…}
         public IQueryable<Employee> GetSalariedEmployee(){…}
         [RequiresAuthentication()]
         public void ApproveSabbatical(Employee current) {…}   
         #endregion
     
        #region ServiceOperations
         public string Echo(string msg) {…}
         public DateTime GetServerDateTime(){…}
        #endregion
     }
 }

3. WCF Channel on Client

The RIA Services Framework on the Client contains a WebDomainClient:DomainClient whose purpose is to help the SL client communicate with a WCF service represention of the Domain Service. The WebomainClient uses a WCF Client Chanel for this cummunication.

The Channel is created by using WCF’s ChannelFactory and by passing to it a WCF Service Contract that was generated from the Domain Service (more on the contract creation later). The ChanelFactory creates a WCF client proxy based on the supplied contract. The generated proxy takes care of communication/(de)serialization between Client and Server.

The RIA Services DomainContext  utilizes the WebDomainClient for Client-Server communication and the context itself is WCF agnostic.

4. Dynamic .SVC generation

By default DomainServices do not have a physical .SVC file generated for them at Design Time.

However each DomainService has a virtual .SVC associated with it. The .SVC represents the WCF Service that services requests for that particular DomainService. For a given DomainService the path to its .SVC can be determined using the following convention:

[SilverlightApplicationBaseURI] + [DomainServiceFullName].svc (With all “ . ” replaced by “ - “)

So HRApp.Web.OrganizationService is exposed as – https://[ApplicationBaseURI]/HRApp-Web-OrganizationService.svc

At RunTime when the first request (within a particular Application Domain) is made for a DomainService's .SVC file , the registered httpModules intercept the call and RIA Services writes out an in memory .SVC file on the fly.

Below is what the dynamically generated svc file looks like for the OrganizationService defined above -

 <%@ ServiceHost Service=”HRApp.Web.OrganizationService” 
 Factory=”System.Web.Ria.DomainServiceHostFactory”
 %>

 

The .SVC refers to the DomainService Type and a ServiceHostFactory. The default RIA Services  HostFactory instantiates the default RIA Services ServiceHost, which in turn is responsible for extracting the WCF Service Contract from the DomainService (See the ‘Generating a WCF Contract’ paragraph below for more details) and also for hosting the service.

A request for the DomainService .SVC in any folder under the Web Application root is redirected to [WebAppRoot]/Services/[DomainService].svc using ASP.net URL rewriting. The service is thus accessible under any folder in the WebApp. Hence even if a .XAP is moved around under the hosting Web App Root, the Silverlight App's relative reference to the DomainService is not broken. 

NOTE - If a physical .svc file with the right file name (as per convention) is present in the ~/Services folder, that is used to define the Service Contract and no virtual .SVC file is generated.

5. Generating the WCF Contract:

Each WCF service needs to have a ServiceDescription and one or more ContractDescriptions for it.

ServiceHosts are responsible for extracting descriptions from a service and hosting the service. The standard ServiceHost in WCF produces descriptions based on WCF attributes such as [ServiceContract] and [OperationContract]. In RIA Services we provide a custom ServiceHost which does this based on a RIA Service Attributes and Conventions.

Here is how DomainService operations are mapped to the WCF Contract:

Query operations –

Each Query operation shows up as a ServiceOperation in the WCF Contract, but with its signature modified.

The ServiceHost creates an operation description for each query operation. The return type is changed to QueryResult<T> such that it can return additional information such as count. It also adds a QueryOperationBehavior which through a custom operation Invoker takes care of applying cache policies, validating parameters and composing queries.

Insert, Update, Delete operations –

For all CUD operations the ServiceHost generates one top-level SubmitChanges operation description. Again, it adds a custom behavior which injects a custom operation invoker. The signature of SubmitChanges is ChangeSet SubmitChanges(ChangeSet changeSet) . The returned ChangeSet contains auto-generated values from the server.

Invoke Operations –

Invoke Operations are the RIA Services equivalent of WCF ServiceOperations. All Invoke Operations show up in the WCF Contract as Service Operations.

For the OrganizationService Domain Service we had defined earlier, below is what the WCF Contract looks like to the “Add Service Reference” dialog -

image

Attached to this post is also the full generated WCF Contract for the OrganizationService (the file was produced by doing an Add Service Reference to the Domain Service's WCF endpoint)

6. Default Endpoints:

The RIA Services ServiceHost creates the following endpoints by default - 

a) For Silverlight Client: SOAP w/binary endpoint. Address = “binary”, Binding = CustomBinding consisting of HttpTransportBindingElement and BinaryMessageEncodingBindingElement.

b) For AJAX Client: JSON REST endpoint. Address = “”, Binding = WebHttpBinding w/JSON as the format.

c) For other clients : SOAP w/XML endpoint. Address = “soap”, Binding = BasicHttpBinding consisting of HttpTransportBindingElement and TextMessageEncodingBindingElement.

The blog post here describes how one can consume the SOAP XML endpoint in a WindowsForms application. Here is a Sample of the same endpoint being consumed in a WPF application.

Summary

Above we discussed in some detail how RIA Services uses WCF under the covers. Hopefully this helps folks better understand the RIA Services alignment with WCF.

In a future post I  plan to discuss how WCF extensibility (e.g. custom behaviors) can be applied to Domain Services. In the meanwhile here is a Sample that demos this.

Add Service Reference Generated WCF Contract .cs