Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Routing with Windows Communication Foundation : Part 2 - Extending web.config

0.00/5 (No votes)
13 Mar 2008 1  
Implementing a custom configuration for routing rules

Introduction

Sometimes in .NET programming, the general <appSettings> section of configuration is not enough to meet your needs. Fortunately, the .NET Framework provides a rich interface to create custom configuration sections for use in an application's configuration.

Background

This is the second of several articles walking through a WCF Router for an enterprise WCTP service. The sections are:

Part 1 - Overview
Part 2 - Extending web.config
Part 3 - Defining the Service Contracts
Part 4 - Implementing the Router
Part 5 - An Asynchronous Service Implementation
Part 6 - A Synchronous Service Implementation
Part 7 - Advanced Services

In this section, we show how a custom configuration section is created to store settings for all of the services that our Router will send information to.

Design

In the end, what we would like to have is a section in the XML configuration with something like the following:

<wctp>
  <receivers>
    <addReceiver name="Receiver1" intProperty="77" boolProperty="true" />
    <addReceiver name="Receiver2" intProperty="114" boolProperty="false" >
  </receivers>
</wctp>

This is done by using three custom classes which derive from base classes in the System.Configuration namespace. These are:

  1. A class derived from System.Configuration.ConfigurationSection, representing the <wctp> element.
  2. A class derived from System.Configuration.ConfigurationElementCollection, which represents the <receivers> and the ability to have the <addReceiver> elements.
  3. A class derived from System.Configuration.ConfigurationElement, which represents the property information contained within the <addReceiver> elements.

Let's look at these in detail.

WctpConfig

The WctpConfig class is our class which is derived from ConfigurationSection, and represents the root XML element of our custom configuration. Since it only contains a single collection, the code if quite small:

[ConfigurationProperty("receivers", IsDefaultCollection = false)]
[ConfigurationCollection(typeof(ReceiverCollection), AddItemName="addReceiver",
        ClearItemsName="clearReceivers", RemoveItemName="removeReceiver")]
public ReceiverCollection Receivers
{
  get
  {
    return (ReceiverCollection)(base["receivers"]);
  }
}

Having the ability to use <clearReceivers> and <removeReceiver> elements allows use to machineToApplication settings, where some things could be defined in the machine.config file, or in other configuration files.

ReceiverCollection

The ReceiverCollection class is the class derived from ConfigurationElementCollection. It contains methods to get elements in the collection via integral index or string key, and also to add, remove, and clear the child elements.

This class is fairly generic and with minor modifications could easily be used in other projects.

ReceiverConfig

This is the class derived from ConfigurationElement. It contains the really custom code for our configuration tree. So let's look at what this is going to do for us.

In this class, we will have the information necessary to decide which possible Receiver to route WCTP messages to. So we would like it to have some very specific information:

  • Name - This serves as the key for the ReceiverCollection element collection, but also must match a configured WCF endpoint.
  • Priority - When multiple services otherwise match to receive a message, there must be some way to decide which one to send the message to. Prioritization is a good way to achieve this.
  • Recipient Filter - This is a filter against the URI which the message is sent to. For example, a specific service might want everything sent to electric@contoso.com.
  • Sender Filter - This is a filter against the URI of the radio device which sent the message. For example, if we wanted to insert a test service used for a group of test devices which all started with the address 602555121, we could use the filter "^602555121." to limit a service to only receive these messages.
  • Format Handling - WCTP suppports two general formats, binary and text. Some services might only support one or the other of these two formats. So having configuration to specify this is a good idea.
  • Exclusive Handling - It is possible to send a message to more than one service. This setting allows the configuration to specify whether or not sending to other, lower priority services should be done.
  • One Way - WCF supports a myriad of lower layer protocols. Some services may not be available for synchronous communications, instead relying on specific lower layer functionality to hold the messages, such as MSMQ. This setting allows the configuration to specify whether or not a WCF Service Contract with (IsOneWay=true) operations.

Implementing these is fairly straightforward. Here is an example of the HandlesText property.

/// 
/// Whether or not the service supports text messages.  True by default.
/// 
[ConfigurationProperty("text", IsRequired = false, DefaultValue = true)]
public bool HandlesText
{
  get
  {
    return (bool)this["text"];
  }
  set
  {
    this["text"] = value;
  }
}

Using the Collection

Skipping ahead a bit to the implementation of the WCTP HTTP Application, making use of the configuration is very easy. When deciding which WCF service to route a particular mesage to, a simple LINQ query can be executed to return an ordered list of prioritized potential services.

//Create a LINQ query to get the order of services to send the message to
WctpConfig config = (WctpConfig)(ConfigurationManager.GetSection("WCTP"));
IEnumerable<WctpConfig> receivers;
if (payload.GetType() == typeof(string))
{
  receivers = config.Receivers.OfType()
      .Where(s => s.HandlesText)
      .Where(s => Regex.Match(originator.senderID, s.SenderFilter).Success)
      .Where(s => Regex.Match(recipient.recipientID, s.RecipientFilter).Success)
      .OrderByDescending(s => s.Priority)
      .ThenByDescending(s => s.ExclusiveHandler)
      .ThenByDescending(s => s.OneWay);
}
else
{
  receivers = config.Receivers.OfType()
      .Where(s => s.HandlesBinary)
      .Where(s => Regex.Match(originator.senderID, s.SenderFilter).Success)
      .Where(s => Regex.Match(recipient.recipientID, s.RecipientFilter).Success)
      .OrderByDescending(s => s.Priority)
      .ThenByDescending(s => s.ExclusiveHandler)
      .ThenByDescending(s => s.OneWay);
}

Isn't that nice!

Points of Interest

I find myself in awe more and more over the functionality contained within LINQ.

History

Mar 13, 2008 - Original Document

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here