Posted on May 14, 2008 09:45 by Dan Haligas

 

 

Service extensions inside Aware Server v3 can be used to build services that can have external algorithms or code attached to them by application administrators in a Lego type fashion.  The blog refers to the Extensions sample that ships with the Aware SDK.

Imagine the following service contract:

 

   1:  [ServiceContract(Namespace = "urn:mynamespace", Name = "InsuranceClaimsService")]
   2:  public interface IInsuranceClaimsService
   3:  {
   4:      [OperationContract]
   5:      Double ComputeTotalClaim(String claimNumber, String state, Double carValue);
   6:  } 

 

Now lets define a service extension point for our service.  Imagine we want to be able to plug different tax calculators into different instances of our service without having to recompile or touch our insurance claims service implementation.  So lets define a TaxCalculator extension that our insurance engine will use to compute the tax associated to a car.

 

   1:  /// <summary>
   2:  /// This interface will be our extension point for allowing others to provide
   3:  /// TaxCalculatorExtension implementations
   4:  /// </summary>
   5:  [DataContract]
   6:  [Serializable]
   7:  public abstract class TaxCalculatorExtension : ServiceExtension
   8:  {
   9:      /// <summary>
  10:      /// 
  11:      /// </summary>
  12:      /// <param name="manifestReference"></param>
  13:      /// <returns></returns>
  14:      public override sealed Boolean CanBeAssociatedTo(ManifestReference manifestReference)
  15:      {
  16:          //
  17:          // We can open the .aware file as xml in the ExtenableServices project to get the guid for that
  18:          // service.  
  19:          //
  20:          // We only want to be associated to that type of service.  This limits what the management console
  21:          // shows to the user for associating extensions to your service.
  22:          //
  23:          return manifestReference.Id == new Guid("c7ff37eb-d286-475f-ad78-097e963a5d68") && manifestReference.Version == "1.0.0.0";
  24:      } 
  25:   
  26:      /// <summary>
  27:      /// 
  28:      /// </summary>
  29:      /// <param name="state"></param>
  30:      /// <param name="carValue"></param>
  31:      /// <returns></returns>
  32:      public virtual Double ComputeTax(String state, Double carValue)
  33:      {
  34:          return 0.0;
  35:      } 
  36:   
  37:      /// <summary>
  38:      /// 
  39:      /// </summary>
  40:      public override sealed Boolean AllowMultiple
  41:      {
  42:          get
  43:          {
  44:              //
  45:              // We only want one of these at most attached to our service
  46:              //
  47:              return false;
  48:          }
  49:      }
  50:  } 

 

The CanBeAssociatedTo routine allows you to ensure that derived versions of this extension type are only applied to your service (guid and version make up a unique manifest inside Aware).  The Aware Enterprise Manager will use this routine to limit the extensions that can be applied to a service.

 

The AllowMultiple allows you state that multiple of this extension type may be applied or just one.  In our sample we want at most one tax calculator extension attached to our service.

 

The ComputeTax routine and any other virtual routines in this class define our extension points for tax calculators.

For good measure let's provide a default implementation of our tax extension.

 

   1:  /// <summary>
   2:  /// 
   3:  /// </summary>
   4:  internal class DefaultTaxCalulator : TaxCalculatorExtension
   5:  {
   6:      /// <summary>
   7:      /// 
   8:      /// </summary>
   9:      /// <param name="state"></param>
  10:      /// <param name="carValue"></param>
  11:      /// <returns></returns>
  12:      public override Double ComputeTax(String state, Double carValue)
  13:      {
  14:          return carValue * .07;
  15:      }
  16:  } 


In our host file for our service we can override the AddHostExtensions event and attach our default extension if one has not been added to us already.

 

   1:  /// <summary>
   2:  /// 
   3:  /// </summary>
   4:  /// <param name="sender"></param>
   5:  /// <param name="e"></param>
   6:  private void InsuranceClaimsServiceHost_AddHostExtensions(object sender, HostExtensionsEventArgs e)
   7:  {
   8:      //
   9:      // Check for an extension added from the outside otherwise add our default one
  10:      //
  11:      if (e.Extensions.Find<TaxCalculatorExtension>() == null)
  12:      {
  13:          e.Extensions.Add(new DefaultTaxCalulator()); 
  14:   
  15:          //
  16:          // Extensions are accessible in any operation using OperationContext.Current.Host.Extensions.Find<TaxCalculatorExtension>
  17:          //
  18:      }
  19:  } 

 

Now take a look at the implementation of our IInsuranceClaimsService.

 

   1:  /// <summary>
   2:  /// 
   3:  /// </summary>
   4:  /// <returns></returns>
   5:  /// 
   6:  /// <summary>
   7:  /// 
   8:  /// </summary>
   9:  Double IInsuranceClaimsService.ComputeTotalClaim(String claimNumber, String state, Double carValue)
  10:  {
  11:      //
  12:      // We know at least our DefaultTaxCalculator will be here
  13:      //
  14:      TaxCalculatorExtension taxCalculator = OperationContext.Current.Host.Extensions.Find<TaxCalculatorExtension>(); 
  15:   
  16:      //
  17:      // Use the tax calculator to finish the computation.  this could be our default one or 
  18:      // one provided from the outside
  19:      //
  20:      return taxCalculator.ComputeTax(state, carValue) + 15000;
  21:  } 

 

Notice how use the Extensions.Find method which is native to WCF on the current OperationContext.  We grab our extension and call its compute tax routine. 

 

Now lets implement an extension on the outside.  To do this we create a new Service Extension project (project template that ships with Aware).

 

After adding a reference to our shared library (contains both the service contracts and service extension definitions) we implement a greedy tax calculator which returns 50% tax every time.

 

   1:  /// <summary>
   2:  /// 
   3:  /// </summary>
   4:  /// </summary>
   5:  [DataContract]
   6:  [Serializable]
   7:  public sealed class GreedyTaxCalculator : TaxCalculatorExtension
   8:  {
   9:      /// <summary>
  10:      /// 
  11:      /// </summary>    
  12:      public GreedyTaxCalculator()
  13:      {
  14:      } 
  15:   
  16:      /// <summary>
  17:      /// 
  18:      /// </summary>
  19:      /// <returns></returns>
  20:      public override string ToString()
  21:      {
  22:          return Description;
  23:      } 
  24:   
  25:      /// <summary>
  26:      /// 
  27:      /// </summary>
  28:      /// <param name="state"></param>
  29:      /// <param name="carValue"></param>
  30:      /// <returns></returns>
  31:      public override Double ComputeTax(String state, Double carValue)
  32:      {
  33:          return carValue * .5;
  34:      } 
  35:   
  36:      /// <summary>
  37:      /// 
  38:      /// </summary>
  39:      [Browsable(false)]
  40:      public override String Category
  41:      {
  42:          get
  43:          {
  44:              return "Insurance Tax Calculators";
  45:          }
  46:      } 
  47:   
  48:      /// <summary>
  49:      /// 
  50:      /// </summary>
  51:      [Browsable(false)]
  52:      public override String Name
  53:      {
  54:          get
  55:          {
  56:              return "Greedy Tax Calculator";
  57:          }
  58:      } 
  59:   
  60:      /// <summary>
  61:      /// 
  62:      /// </summary>
  63:      [Browsable(false)]
  64:      public override String Description
  65:      {
  66:          get
  67:          {
  68:              //
  69:              // This will show in the notes column in the management console
  70:              //
  71:              return "Takes the car value and return 50% tax";
  72:          }
  73:      }
  74:  } 

 

The overridden properties above are only used by the tools to provide verbose descriptions for this extension.  After this we are done and ready to test.  Publish both your service and your service extensions projects into Aware.

 

image

 

As shown above they are both deployed into Aware.  Next we need to provision 2 instances of our Insurance Claims Service.  One that uses the default tax calculator and one that uses the greedy one.

 

To do this go into the Aware Enterprise Manager and go to the Service Provisioning Policies tab.  Click the 'Create New' button to create a new provisioning policy.  Enter 'Insurance Claims' for the name of the policy and switch to the Included Service Manifests tab.

 

image

 

Add our service by clicking the 'Add Service Manifest' button and enter an additional attribute in the Additional Attributes section with a key of Tax and a value of Default.  Then add our same service again. 

 

image

 

For the second instance of our service add an additional attribute with a key of Tax and a value of Greedy.  Before clicking save switch to the Extensions tab in the lower section of the screen.  Click the Add Extension and select the Greedy Tax Calculator from the popup and click Apply.

 

image

 

Click Save and switch to the Service tab in the Enterprise Manager to see that we have 2 instances of our insurance service running and that each have an attribute attached to them with a key of Tax.

 

image

 

We can then use this attribute to route requests to different instances of our service.  The client code is listed below.

 

   1:                  ServiceAttributes attributes = new ServiceAttributes();
   2:                  attributes["Tax"] = "Default";
   3:   
   4:                  using (InsuranceClaimsServiceClient client = new InsuranceClaimsServiceClient(attributes))
   5:                  {
   6:                      Double result = client.ComputeTotalClaim("122344x", "GA", 5600);
   7:                      Console.WriteLine("Claim result = {0}", result);
   8:                  }
   9:   
  10:                  ServiceAttributes attributes2 = new ServiceAttributes();
  11:                  attributes2["Tax"] = "Greedy";
  12:   
  13:                  using (InsuranceClaimsServiceClient client = new InsuranceClaimsServiceClient(attributes2))
  14:                  {
  15:                      Double result = client.ComputeTotalClaim("122344x", "GA", 5600);
  16:                      Console.WriteLine("Claim result = {0}", result);
  17:                  }

 

Above we call the insurance service twice.  The first call is directed to the default tax calculator instance and the second is directed to the greedy instance.  If you recall from our implementations above we add 15000 to whatever tax is computed.  So for the first one our default tax will be 15000 + .07 * 5600 = 15392 and for the greedy one it will be 15000 + .5 * 5600 = 17800.

 

image

 

This really shows off how we can use the same binary bits (deployed service manifest) and change the behavior of the service through configuration and extensions.  Tax algorithms can now be implemented independent of the insurance claims service and can be assembled at provisioning time depending on customer need, etc.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5


Related posts



Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

July 19. 2008 17:42