现实世界中的 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。 这是信封中的结果: s:AccessDenied你不能这样做 【参考方案1】:

原始方法的一个问题是您试图复制可能发生的大量系统错误/异常。由于系统的复杂性随着每个新功能的增加而增加,因此您必须解决的可能问题的数量呈指数级增长(或更多!)

我建议采用以下方法:由于您正在创建服务和访问调用的“系统”,因此只需定义与该系统相关的 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 故障合约的主要内容,如果未能解决你的问题,请参考以下文章

区块链技术如何有效应对气候变化

我意识中的未来世界

现实世界中的ORM

现实世界中哪些地方用到了Java?

增强现实——用虚拟光照亮现实世界的物体

如何从现实世界中的多个队列中读取?