现实世界中的 WCF 故障合约
Posted
技术标签:
【中文标题】现实世界中的 WCF 故障合约【英文标题】:WCF fault contracts in real world 【发布时间】:2012-02-04 05:42:30 【问题描述】:假设我们有一个服务方法执行一些安全检查,从数据库和第三方 Web 服务检索数据,构造 MyDataDTO
,将审计条目写回数据库。
而且我们需要结构良好、细粒度的错误代码,不是吗?我们是好孩子,遵循标准的 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 种上述方法(甚至更复杂)。并且定义所有这些错误合约成为一场噩梦,因为这是一个非常容易出错的过程:
-
无法为整个服务定义一般故障(如 DbFault)
您不能保证操作合同中定义的故障会真正返回(复制粘贴问题)
您不能保证您没有错过添加到操作合同中的某些错误
我们不要在这里考虑接口版本:)
因此,如果您在生产中支持 WCF 服务,您就会明白这一点。我们是否应该完全放弃故障契约并使用良好的旧 C 风格(例如拥有带有 ErrorCode 属性的基础 DTOBase
类)?减少错误粒度?如何确保文档正确/最新?我对一些最佳实践感兴趣。
【问题讨论】:
要问自己的一件事是,您的客户是否真的会使用这些细粒度的错误代码。他们实际上会根据返回的故障采取不同的操作,还是只会显示错误消息? 主要显示。这是为了更快地诊断问题而不是采取不同的行动。 那么您只需要一个包含Message
字符串的FaultContract。
throw new FaultException("You are not allowed to do it", new FaultCode("AccessDenied")) 并且我不需要在操作中定义类型的 FaultException 和 FaultContract。解决了您所描述的问题,并且仍然 100% 兼容 SOAP。
这是信封中的结果: 原始方法的一个问题是您试图复制可能发生的大量系统错误/异常。由于系统的复杂性随着每个新功能的增加而增加,因此您必须解决的可能问题的数量呈指数级增长(或更多!)
我建议采用以下方法:由于您正在创建服务和访问调用的“系统”,因此只需定义与该系统相关的 FaultContracts。客户应该只对以下问题感兴趣:
-
这是我想要的数据的问题,还是我询问的方式有问题?
如果不是,这是我的问题,还是 IT 相关问题(系统崩溃、网络错误、数据库问题等)?
稍作返工,您就可以减少必须提供的错误合同的数量。例如(这不是我的想法):
//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);
您的具体实现会有所不同,但我们的想法是传递与您的系统相关的信息,而不是可能出现的所有系统性小问题。
【讨论】:
【参考方案2】:好吧,你可以实现这个:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
在这种情况下,您将有一个地方,您可以根据异常类型/消息切换并提供自己的错误。
http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler.providefault.aspx
或者更好的样品:
http://blogs.msdn.com/b/pedram/archive/2008/01/25/wcf-error-handling-and-some-best-practices.aspx
【讨论】:
这是否会使用每个操作要返回的故障来更新 WSDL?以上是关于现实世界中的 WCF 故障合约的主要内容,如果未能解决你的问题,请参考以下文章