Assume we have a service method that performs some security checks, retrieves data from DB and third-party web service, constructs MyDataDTO
, writes an audit entry back into DB.
And we want well structured, granular error codes, don't we? We're good boys and follow standard WCF error handling guidelines:
[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
MyDataDTO GetData(string key);
Now we're adding a new method that updates the data. The method calls GetData()
internally (or major part of it), performs validation add updates the data. So it must have all faults of GetData()
duplicated plus add its own faults:
[FaultContract(typeof(InvalidState))]
[FaultContract(typeof(DataNotValid))]
[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
void UpdateData(MyDataDTO data);
So far so good. This allows us even to xml generate documentation we can provide for consumers of our service so they know which error codes they can expect.
Now imagine we have 10 services with 10 methods like above (or even more complex) each. And defining all those fault contracts becomes nightmare as this is quite error-prone process:
Let's not take into account interface versioning here :)
So you got the picture if you support WCF services in production. Should we abandon fault contracts at all and use good old C-style (like having base DTOBase
class with ErrorCode property)? Reduce error granularity? How to make sure the documentation is correct/up to date? I'm interested in some best practices.
One problem with your original approach is that you are trying to replicate the multitude of system errors/exceptions that could occur. Since the complexity of the system increases with each new function, the number of possible problems you have to account for rises exponentially (or greater!)
I would suggest the following approach: Since you are creating a "system" of services and access calls, only define FaultContracts relating to that system. The clients should only be interested in the following questions:
With a little rework, you can cut down on the number of fault contracts you have to provide. For example (and this is off the top of my head):
//Base class to define general problems
[DataContract]
public class SysFault
{
//MyDataDTO-specific general error.
[DataMember]
public string SysMsg {get;set;}
//boolean for "this is a problem with me or the hosting system?"
[DataMember]
public bool IsSystemic {get;set;}
}
//Subclass to expose synchronization issues--if that's one you want to define
[DataContract]
public class SyncFault : SysFault
{
[DataMember]
public string SyncMsg { get;set; }
}
//Subclass to expose validation issues
[DataContract]
public class ValFault : SysFault
{
[DataMember]
public string ValMsg { get;set; }
}
Now, you can use the fault types to separate out what is going on with your services. For example:
[ServiceContract]
public interface IRecordSys
{
[OperationContract]
[FaultContract(typeof(SysFault))] //Raised for underlying problem
[FaultContract(typeof(ValFault))] //Raised if there is an issue with the key value
MyDataDTO getData(string key);
[OperationContract]
[FaultContract(typeof(SysFault))] //Raised for underlying problem elsewhere
//Raised for some issue, such as unable to get two subsystems to update properly
//with the given data
[FaultContract(typeof(SyncFault))]
void update(MyDataDTO data);
}
Your particular implementation will differ, but the idea is to pass along messages that concern your system, not every little systemic problem that may come along.
Well, you can implement this:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
In that case you'll have one place, where you will switch by exception type/message and provide own faults.
http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.providefault.aspx
or better yet with samples:
http://blogs.msdn.com/b/pedram/archive/2008/01/25/wcf-error-handling-and-some-best-practices.aspx
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With