Making WCF "Behave" - Part Four

Over the past month or so I've demonstrated how to create a custom behavior for WCF. I explained how behaviors fit into the WCF stack, created one based on the Message Dispatcher and then added it to the service via the configuration file.

The last piece is enabling our behavior to take property information from the configuration file.

Currently, our behavior intercepts the output message a replaces it with some text that has been hard coded into the behavior class. This is great, but how often do we hard code data like that in the code and never have to change it? That's right; almost never.

The plan then is to make some alteration so our behavior so that it can accept the message to insert from our configuration file:

<CustomBehaviorElement customMessage="This is a test message"/>

In part three I changed our class to implement the BehaviorExtensionElement so that it could be added to the service runtime via configuration. I did this for the sake of simplicity, but in general this is not a good idea. You will want to keep the behavior logic separate from the logic that allows it to be managed via the configuration. You'll see why in a few minutes.

This also gives me an opportunity to back up a little and explain what BehaviorExtensionElement does. Last time when we set up the behavior to be added via configuration, we changed the class to inherit from BehaviorExtensionElement and we referenced the behavior in the configuration like such (remember, the "type" information has to be on one line, no breaks please):

<add name="CustomBehaviorElement" 
    type="CustomBehaviorSample.MyCustomMessageFormatter, Behavior, 
        Version=1.0.0.0, 
        Culture=neutral, 
        PublicKeyToken=null" />

The WCF runtime looked in the behavior assembly for a class that derives from BehaviorExtensionElement named MyCustomMessageFormatter. This trick is, it doesn't necessarily expect that class to be the behavior. It merely expects the class to know how to create the behavior.

This abstraction presents us with some interesting possibilities. For example, with the behavior code in a separate class we have the ability to influence how the behavior is created in different circumstances by creating different classes that implement BehaviorExtensionElement.

Some key benefits of this separation of duties is the ability to pass runtime information to the behavior via the configuration and to control how the actual behavior class is created.

The first step in making our change is to add a member variable to hold the message we are going to be sending. Simply stated....

private string _message;

The next step is creating  (or if you already have one, changing) the constructor for the behavior class (MyCustomMessageFormatter) to take a string from the config, which just so happens to be our message:

public MyCustomMessageFormatter(string serviceMessage)
            : base()
        {
            this._message = serviceMessage;
        }

You can have any initialization code in this constructor you want. Need four different parameters for your constructor? Not a problem. Want to make some database calls to perform initialization? You can do that too. Want to delegate to some other class or service? Just write the code.

A change also needs to be made to the actual code that inserts the new message. Instead of using the hard coded message, it will now use the value that is being passed in to the constructor:

xmlDictionary.WriteString(this._message);

Now we have to make a new class that derives from BehaviorExtensionElement. We'll go ahead and call this BehaviorExtensionElementSample. Once we've done that we need to change the behavior class (MyCustomMessageFormatter) and remove it's inheritance reference to this class. In doing this, we will need to move the BehaviorType property and the CreateBehavior method we previously created in out behavior to the new class:

class BehaviorExtensionElementSample : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get 
        {
            return typeof(MyCustomMessageFormatter);
        }
    }

    protected override object CreateBehavior()
    {
        return new MyCustomMessageFormatter(this.CustomMessage);
    }
}

As you can see, a change was also made to the CreateBehavior method that calls the constructor of the Behavior class. It's going to pass in a string that will be the message text from the config file. But it doesn't even need to do that. You have a lot of flexibility at this point in what kind of behavior gets created. For example, you could use the incoming configuration information to determine what kind of behavior to create (Strategy pattern anyone?) including not creating a behavior at all.

Again, the WCF runtime doesn't necessarily expect the class that inherits from BehaviorExtensionElement to provide the behavior logic, but it does expect it to be able to handle the creation of the behavior.

So now that we know how the information gets from BehaviorExtensionElement to our behavior class, how does the information get from the config file to the BehaviorExtensionElement?

To answer that, we need to learn a little bit about some of the classes in the System.Configuration namespace. Specifically ConfigurationProperty and ConfigurationPropertyCollection.

To our BehaviorExtensionElementSample, we add the following:

private ConfigurationPropertyCollection _prop = null;

protected override System.Configuration.ConfigurationPropertyCollection Properties
{
    get
    {
        if (this._prop == null)
        {
            this._prop = new System.Configuration.ConfigurationPropertyCollection();
            this._prop.Add(new System.Configuration.ConfigurationProperty("customMessage", typeof(string), 
                "Default Message", System.Configuration.ConfigurationPropertyOptions.IsRequired));
        }
        return this._prop;
    }
}

A ConfigurationProperty is an abstraction of configuration information, like the kind we have in our config files. It stores the value along with various meta-data like type, default value, type conversion rules and a description of the property. The ConfigurationProperyCollection is (surprise!) a collection of these ConfigurationProperty objects. By adding this code, we are overriding the implementation in ConfigurationElement, which BehaviorExtensionElement inherits from.

So, in the code above our class derived from BehaviorExtensionElement creates a private instance of the ConfigurationPropertyCollection and exposes a protected property called "Properties." Within our override of "Properties" we need to create ConfigurationProperty elements for out custom attributes. We only have one, the "customMessage" so that's all we are creating here. A base class will call this property on our implementation, and over write it's instance of the ConfigurationProperyCollection with ours.

Now that we've got to get our custom message in and out of our BehaviorExtensionElement. We add this code to our BehaviorExtensionElementSample:

[System.Configuration.ConfigurationProperty("customMessage", DefaultValue = "Default message.",
    IsRequired = true)]
public string CustomMessage
{
    get
    {
        return (string)base["customMessage"];               
    }
    set
    {
        base["customMessage"] = value;
    }
}

The first thing you'll notice is the ConfigurationProperty attribute. This mirrors the information we used to create our ConfigurationProperty element for our ConfigurationPropertyCollection above. You'll notice the name "customMessage" is the same identifier that we used to identify our value in the config file. This is very important because it tells the WCF runtime how to map the information from the config file to a property on our class. When the runtime collects the configuration information from the config file and creates an instance of our extension element, it will populate all of our properties automatically for us.

The rest of this is pretty simple "store a value, return a value" type code; storing the incoming value in the ConfigurationPropertyCollection we created ("Properties" is our base classes default indexer property) and returning a value (in our case, cast to a string).

So, to tie all this fun configuration stuff back into our behavior, the code that actually creates the behavior class:

protected override object CreateBehavior()
{
    return new MyCustomMessageFormatter(this.CustomMessage);            
}

Passes in our message to our behavior:

private string _message;

public MyCustomMessageFormatter(string serviceMessage)
    : base()
{
    this._message = serviceMessage;
}

Which in turn, injects it into our outgoing message:

xmlDictionary.WriteString(this._message);

See! It was, uh... easy? :)

In reality, this may seem complicated for something as simple as passing an initialization value into a class. But when you think about it, WCF is a communications framework whose mission to support flexibility and extensibility.  The leveraging of the ConfigurationElement and ConfigurationProperty classes allows us to leverage existing functionality in the .NET framework and the separation of duties between our behavior code and the BehaviorExtensionElement class gives us complete control over how things happen in the runtime. At any point along the way we had the ability to examine what is going on and customize our functionality based on any number of conditions. As complex as this may seem (and once you do a few, it's actually not too tough) I promise it's easier than trying to achieve this same level of configurable customization in ASMX or .NET Remoting!

Over the next few months I'll post some content on how to create/use the other types of behaviors. For now feel free to download and play with the code.

See ya later!

Print | posted on Wednesday, April 02, 2008 1:27 PM

Feedback

No comments posted yet.
Title  
Name
Email (never displayed)
Url
Comments   
Please add 8 and 2 and type the answer here: