Using WCF 4.0 XAML Services in the real world: Activating WCF Services from a central repository

Declarative Services is one of the new features included in the .NET framework 4.0. In a nutshell, declarative services are WCF services modeled and implemented entirely in XAML. This feature is fundamentally enabled by extending Windows Workflow Foundation (WF) 4.0 with the capability of modeling WCF contracts using XAML. You can read more details about declarative services on the MSDN documentation. Even if you are not entirely familiar with WF as a technology, I am sure you can see the advantages of implementing services in a declarative languages.

Some of the most challenging aspects of Service Oriented (SO) solutions such as versioning, management and governance are notably increased by the fact that most enterprise web services are developed entirely using imperative languages such as C# or Java. Why is that? Well, to give you an example, think about the process that we have to go through in order to change a WCF services that has been running on a production system. In the best case, this process will involve deploying some assemblies to the GAC or to a particular directory, changing some references and restarting some service hosts. Now multiply that order of complexity by the number of services you have on your enterprise and the best you are going to get is a huge headache :)

In order to address some of these challenges, many developers have turned to dynamic languages such as Ruby or Python looking for a more declarative approach to implement services. Although none of the WS stacks built on dynamic languages is nearly as powerful as WCF they are certainly easier to modify and maintain. Declarative services represent an evolution of this paradigm that leverages the benefits of the XAML WF dialect.

XAML-only workflows has become one of the most popular features of the first two versions of WF. The ability of writing logic using an XML-based language drastically improves the versioning, management and other important aspects of software solutions based on WF. Declarative services can be seen as an attempt to extend the same benefits onto the world of WCF services.

In medium to large environments, one of the great benefits of declarative services would be the ability of centrally store the service definition and implementation in a database or any other data repository. This approach will improve the ability to version, modify, document or query services while still leveraging all the capabilities of the WCF runtime. For instance, we could envision a solution on which a custom WCF service host that loads declarative services dynamically from a database. The following diagram illustrates that approach.

 

 

It turns out that there is a simple but elegant solution for implementing this pattern using WCF-WF 4.0. Let's start with the following database that can be used host XAML service definitions as well as part of the endpoint address configuration.

 

Fundamentally, a declarative service definition will be stored in the XAMLService table. Essentially the service column will store the XAML implementation and contract of the WCF service. Additionally some of the details associated with the service host will be kept in the HostConfig table. This is not a real world examples by any stretch of the imagination. On the contrary, we are trying to keep things as simple as possible in order to correctly illustrate the fundamental concepts.

We will abstract the communication with the database using an ADO.NET Entity Model that maintains an almost one to one mapping with the physical database.

 

 

Having completed the ADO.NET EF model we can implement a custom WCF service host that dynamically loads the service definition from a database and initializes it using the existing XAML services functionalities.

   1: public class WorkflowRepositoryServiceHost: 
   2:             System.ServiceModel.Activities.WorkflowServiceHost
   3:     {
   4:         private Guid serviceID;
   5:         private static XAMLService currentDBService;
   6:  
   7:         public WorkflowRepositoryServiceHost(Guid serviceid):

8: this(LoadServiceDefinition(serviceid),

BuildServiceEndpoints(serviceid))

   9:         {
  10:  
  11:         }
  12:  

13: public WorkflowRepositoryServiceHost(Service serviceDefinition,

params Uri[] baseAddresses) :

  14:             base(serviceDefinition, baseAddresses) 
  15:         {
  16:             ServiceMetadataBehavior mtdBehavior = new ServiceMetadataBehavior();
  17:             mtdBehavior.HttpGetEnabled = true;
  18:             this.Description.Behaviors.Add(mtdBehavior);           
  19:         }
  20:  
  21:  
  22:         protected override void OnOpening()
  23:         {
  24:             base.OnOpening();
  25:         }
  26:  
  27:         private  static Service LoadServiceDefinition(Guid serviceid)
  28:         {
  29:             XAMLDBEntities entities= new XAMLDBEntities();

30: IEnumerable<XAMLService> query= from s in entities.XAMLServices

where s.id == serviceid select s;

  31:             if (query.Count<XAMLService>() > 0)
  32:             {
  33:                 List<string> endpoints = new List<string>();
  34:                 MemoryStream stream = new MemoryStream();
  35:                 currentDBService= query.First<XAMLService>();

36: XElement.Parse(currentDBService.Service, LoadOptions.None).

Save(stream);

  37:                 stream.Seek(0, SeekOrigin.Begin);
  38:  
  39:                 Service service = (Service)XamlServices.Load(stream);
  40:  
  41:                 return service;
  42:             }
  43:             else
  44:                 return null;
  45:  
  46:         }
  47:  
  48:         private static Uri[] BuildServiceEndpoints(Guid serviceid)
  49:         {
  50:             if (null == currentDBService)
  51:                 LoadServiceDefinition(serviceid);
  52:  
  53:             List<Uri> uris = new List<Uri>();
  54:             currentDBService.HostConfigs.Load();
  55:             var endpointQuery = from e in currentDBService.HostConfigs select e;
  56:             foreach (HostConfig c in endpointQuery)

57: uris.Add(new Uri(String.Format(http://localhost:{0}/{1},

c.Port, c.Path)));

  58:             return uris.ToArray<Uri>();
  59:         }
  60:     }

Essentially, our custom service host extends the default workflow services (lines 1-2) and dynamically instantiate a XAML service (line 39) together with parts of the endpoint configuration from the database (lines 48-59). In this model, the service implementation could be updated real time in the database without any further deployment actions. The changes will be effective the next time the host starts.

Even though this is a very simplistic scenario, hopefully it will give you a starting point to realize the benefits of declarative services in traditional service oriented environments.

3 Comments

Comments have been disabled for this content.