Previously on this site I've demonstrated a couple of different ways to have a WCF service implement a custom behavior. Configuration allows service behaviors to be added and managed at runtime by system administrators via configuration files. Adding the service in your service host code ensures that in a given hosting environment a service will use a specific behavior in a specific way.
But as service developers, there are going to be times when we want to ensure a particular service or service operation employees a behavior. We can't rely on our hosting environment to know about our requirement for this custom service. Leaving it to configuration opens us to more issues as we are pinning our hopes on a pile of documentation that probably hasn't been read since it was written.
Keep things close
In the course of your WCF-travels, you've probably seen something like this:
[ServiceBehavior(IncludeExceptionDetailInFaults = false,
TransactionTimeout = "00:05:00")]
public class HelloWCF : IHelloWCF
{
public string SayHello(string name)
{
return string.Format("Hello {0} from WCF{1}", name);
}
}
In this case the developer of this service is using a ServiceBehaviour attribute to set some service behaviors at development time (providing the underlying hosting environment supports the service behavior options you've specified). This code is the equivalent of the following configuration:
<behaviors>
<serviceBehaviors>
<behavior name="CustomBehavior.Service1Behavior">
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceTimeouts transactionTimeout="00:05:00" />
</behavior>
</serviceBehaviors>
</behaviors>
Wouldn't be great if we could have our custom behavior implemented as an attribute?
Wish no more
.NET developers have had the ability to create custom Attributes since .NET 1.0, simply by inheriting from the System.Attribute class.
Consider this example:
class errorServiceBehavior : IServiceBehavior
{
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
return;
}
public void AddBindingParameters(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
if (dispatcher != null)
{
dispatcher.ErrorHandlers.Add(new customErrorHandler());
}
}
}
}
Here we have a standard, run-of-the-mill service behavior that adds an instance of a custom error handler to our service. More on that custom error handler later. To implement this in our service as-is we would need to have the host process attach this behavior at runtime. If we wanted to have it added and managed via configuration at runtime we would need an instance of BehaviorExtensionElement and override the BehaviorType property and CreateBehavior method.
Let's make a little change to the class declaration here:
[AttributeUsage(AttributeTargets.Class)]
class errorServiceBehavior : Attribute, IServiceBehavior
{
By inheriting from Attribute, our behavior now has the ability to be used just like an other .NET attribute. The AttrbuteUsage attribute is optional, but is a good idea to use. In this case we are telling the .NET runtime that this attribute can only be applied at the class level. If we wanted to create an operation behavior attribute (yes! you can do this with operation behaviors too!) you would set the AttributeUsage to AttributeTarget.Method
From here it's a simple matter of implementing our custom behavior by applying our custom service behavior attribute to our service class:
[errorServiceBehavior]
public class HelloWCF : IHelloWCF
{
public string SayHello(string name)
{
return string.Format("Hello {0} from WCF{1}", name);
}
public void ThrowAnError()
{
throw new FaultException<Behavior.CustomFault>(new CustomFault("This is a test."));
}
}
This custom behavior implements a custom error handler:
public class customErrorHandler : IErrorHandler
{
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
var customFault = new FaultException<CustomFault>(new CustomFault(error.Message),
"You did _something_ wrong.");
var msgFault = customFault.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, customFault.Action);
}
public bool HandleError(Exception error)
{
return true;
}
}
To keep things simple, this handler simple adds some custom message text to the fault exception before the fault is returned to the client. When running the services client we can see the custom error handler in action:
This is just a simple example. With some experience working with classes inherited from Attribute you can put a lot of power in the hands of your service developer to control how their services are used. Have fun!
Print | posted on Monday, October 13, 2008 10:21 PM