假设我们有一个服务方法执行一些安全检查,从DB和第三方Web服务检索数据,构造MyDataDTO
,将审计条目写回DB。
我们想要结构良好,细粒度的错误代码,不是吗?我们是好孩子,并遵循标准的WCF错误处理指南:
[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
MyDataDTO GetData(string key);
现在我们正在添加一种更新数据的新方法。该方法在内部(或其主要部分)调用GetData()
,执行验证添加更新数据。所以它必须具有GetData()
重复的所有错误并加上自己的错误:
[FaultContract(typeof(InvalidState))]
[FaultContract(typeof(DataNotValid))]
[FaultContract(typeof(AccessDenied))]
[FaultContract(typeof(KeyNotFound))]
[FaultContract(typeof(WsFault))]
[FaultContract(typeof(DbFault))]
void UpdateData(MyDataDTO data);
到目前为止一切顺利。这使我们甚至可以为xml生成我们可以为我们服务的消费者提供的文档,以便他们知道他们可以期待哪些错误代码。
现在假设我们有10个服务,每个服务有10个方法,比如上面的(甚至更复杂的)。并且定义所有这些错误合同变成了噩梦,因为这是一个非常容易出错的过程:
我们不要在这里考虑界面版本控制:)
如果您在生产中支持WCF服务,那么您就可以了解情况。我们应该放弃故障合同并使用良好的旧C风格(比如具有ErrorCode属性的基础DTOBase
类)?减少错误粒度?如何确保文档正确/最新?我对一些最佳实践感兴趣。
答案 0 :(得分:9)
原始方法的一个问题是您正在尝试复制可能发生的大量系统错误/异常。由于系统的复杂性随着每个新功能的增加而增加,因此您必须考虑的可能问题的数量呈指数级增长(或更高!)
我建议采用以下方法:由于您正在创建服务和访问调用的“系统”,因此只定义与该系统相关的FaultContracts。客户应该只对以下问题感兴趣:
通过一些返工,您可以减少必须提供的故障合同的数量。例如(这不是我的头脑):
//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; }
}
现在,您可以使用故障类型来分离服务的内容。 例如:
[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);
}
您的具体实施方式会有所不同,但其目的是传递与您的系统相关的消息,而不是可能出现的每一个小系统问题。
答案 1 :(得分:1)
嗯,你可以实现这个:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
在这种情况下,您将拥有一个地方,您将按异常类型/消息切换并提供自己的错误。
或更好的样品:
http://blogs.msdn.com/b/pedram/archive/2008/01/25/wcf-error-handling-and-some-best-practices.aspx