Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 16: Exposing a WCF Service

I am having a blast with the series where I am updating my simple Mix 09 Business Application demo.  In this part, I wanted to consider the scenario that I hope is a common one.  The developer writes their Silverlight app using the RIA Services pattern and the application becomes wildly successful.  So successful in fact there is a demand to put a services head on top of the same application logic to facilitate writing a bunch of other clients.  This is the sort of pattern we see happening with applications like Twitter and Sharepoint.  

You can see the full series here

The demo requires (all 100% free and always free):

  1. VS2008 SP1
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview

Also, download the full demo files and check out the running application.

To start with, let’s take the application from the early parts of the series and add a simple WCF head for it.  For this example, let’s suppose you wanted to only expose the ability to get and set the “sites” for our super heros… This enables an army of people to help you spot superheor’s I guess.

image

In pervious posts, we defined a DomainService

 [EnableClientAccess]
 public class SuperEmployeeDomainService :
     LinqToEntitiesDomainService<NORTHWNDEntities>
 {
     public virtual IQueryable<SuperEmployee> GetSuperEmployees()
     {
         return this.Context.SuperEmployeeSet
                    .Where(emp=>emp.Issues>100)
                    .OrderBy(emp=>emp.EmployeeID);
     }
  
     public virtual SuperEmployee GetSuperEmployee(int employeeID)
     {
         return this.Context.SuperEmployeeSet
                    .Where(emp => emp.EmployeeID == employeeID)
                    .FirstOrDefault();
     }
  
     public virtual void InsertSuperEmployee(SuperEmployee superEmployee)
     {
         this.Context.AddToSuperEmployeeSet(superEmployee);
     }
     public override void Submit(ChangeSet changeSet)
     {
         base.Submit(changeSet);
     }
     public virtual void UpdateSuperEmployee(SuperEmployee currentSuperEmployee)
     {
         var org = this.ChangeSet.GetOriginal(currentSuperEmployee);
         this.Context.AttachAsModified(currentSuperEmployee, org);
     }

and a Silverlight client.

And built out a Silverlight application to work with it.

image

In this part, let’s start by adding a plain old WCF services to our web project. 

image

Then we can define our service contract..  Notice it can take any shape we’d like… It does not have to conform in any way to the DomainService.

 [ServiceContract]
 public interface ISuperHeroSitesService
 {
     [OperationContract]
     void SetSite(string superHeroName, string site);
     
     [OperationContract]
     string GetSite(string superHeroName);
  
     [OperationContract]
     IEnumerable<string> GetSuperHeroNames();
  
 }

Next, let’s implement the service. 

The easiest way to do that is the use the tooltip..

image

 [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
 [ServiceBehavior(IncludeExceptionDetailInFaults=true)]
 public class SuperHeroSitesService : ISuperHeroSitesService
 {
     
     SuperEmployeeDomainService domainService =   DomainServiceProxy.Create<SuperEmployeeDomainService>();

Then we begin to implement our service.  Notice I put the service in ASP.NET Compat mode, that enables us to access the session information from ASP.NET for things such as auth, etc.   Next, I include exception information to make debugging easier.  
Finally, I create an instance of our DomainService that I will use for implement this service.  Just like in the ASP.NET MVC example, we don’t just “new up” an SuperEmployeesDomainService because we want to take advantage of the validation pipeline here.  So we call a factory method to create our instance of SuperEmployeeService.   Note, with the current CTP, you will need to reference DomainServiceExtensions.dll from this sample to get this functionality.

Then we begin to implement our methods… GetSuperHeroNames() is simple enough.. we just get all SuperHero’s the DomainSerivce class allows us to access and return back just their names. 

 public IEnumerable<string> GetSuperHeroNames()
 {
     var q = domainService.GetSuperEmployees();
  
     return q.Select(emp => emp.Name);
  
 }

Very similar code for GetSite().  Here we look up the employee in question and return back their site..

 public string GetSite(string superHeroName)
 {
     var q = domainService.GetSuperEmployees();
     var employee = q.Where(emp => emp.Name.ToUpper() == superHeroName.ToUpper()).FirstOrDefault();
     return employee.Sites;
     

For SetSite, I went head and defined a method in my DomainService to handle this operation.  But I felt it was more general to have the common DomainService method take the ID rather than the name… 

 [ServiceOperation]
 public virtual void SetSite(int empId, string site)
 {
     var employee = GetSuperEmployee(empId);
  
     employee.Sites = site;
     this.Context.SaveChanges();
     
 }

Then the method in the WCF Service was pretty easy to do:

 public void SetSite(string superHeroName, string site)
 {
    var q = domainService.GetSuperEmployees();
    var employee = q.Where(emp => emp.Name.ToUpper() == superHeroName.ToUpper())
             .FirstOrDefault();
    if (employee == null) throw new Exception(String.Format("Employee named '{0}' not found.",superHeroName));
    domainService.SetSite(employee.EmployeeID, site);   
 }

Now we have a working service… 

image

Let’s build a command line application application that tests out our little service.  You could at this point build any arbitrary client from ASP.NET to WinForms to even java or PHP…  

Create a new Console Application

image

Now we need to add a reference to the service we just created

image

Then we write our client code… 

 Console.WriteLine("List of all SuperHero names");
 foreach (var name in context.GetSuperHeroNames())
 {
     Console.WriteLine(name);
 }

image

Get the site for a particular employee:

 Console.WriteLine("'{0}' site is currently: '{1}' ", args[0], context.GetSite(args[0]));

image

Setting the site for a particular employee:

 var site = string.Join(" ", args.Skip(1).ToArray());
 Console.WriteLine("siting superhero '{0}' site to '{1}' ", args[0], site);
 context.SetSite(args[0], site);

image

In this example, I showed how you can “grow up” your .NET RIA Services based Silverlight apps to offer a custom WCF based service with just the contract that you want.  This service can be used from any client. 

Enjoy!