The power of WCF Behaviors 3 – Contract Behavior

For unhandledDatabaseException, we don’t really need to catch it at client side, but it would be nice to let the global exception handler at client app know about this actual database exception type. To implement that, unhandledDatabaseException must be added to Contract, yes, all the methods/operations in Contract.

No worry, we can use Contract Behavior to automatically add it. ContractBehavior


   public class MyStandardFaultContractAttribute : Attribute, IContractBehavior
    {
        // this is a list of our standard fault detail classes.
        private static readonly Type[] Faults = new[]
                                                   {
                                                        typeof (UnhandledDatabaseException),
                                                    };

        public void AddBindingParameters(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint,
            DispatchRuntime dispatchRuntime)
        {
        }

        public void Validate(
            ContractDescription contractDescription,
            ServiceEndpoint endpoint)
        {
            foreach (OperationDescription op in contractDescription.Operations)
            {
                foreach (Type fault in Faults)
                {
                    if (op.Faults.Any(f => f.DetailType == fault))
                    {
                        Console.WriteLine( fault + " already defined in " + op.Name);
                        continue;
                    }
                    Console.WriteLine("adding fault " + fault + " to " + op.Name);
                    op.Faults.Add(MakeFault(fault));
                }
            }

        }

        private FaultDescription MakeFault(Type detailType)
        {
            string action = detailType.Name;
            DescriptionAttribute description = (DescriptionAttribute)
                                               GetCustomAttribute(detailType, typeof (DescriptionAttribute));
            if (description != null)
                action = description.Description;
            FaultDescription fd = new FaultDescription(action);
            fd.DetailType = detailType;
            fd.Name = detailType.Name;
            return fd;
        }
    }

Watch out, don’t add too many exceptions to contract behind the scene. This is violating DesingByContract principle.

One thing I noticed is, if the exception is thrown by behavior, this works, but if the operation method throw the exception directly, this doesn’t. Using fiddler look at the soap message, all the elements in the exception block has an empty xmlns! Switch to NetDataContractSerializer in errorhandler, I can only control the namespace at the root level, all those child elements still has empty namespace.

The code from http://typesafe.be/2009/02/17/flexible-wcf-exception-shielding/ has the same result.

Anyway, the exception message can reach down to client in this case, even I lost the type, will look back it later.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s