现实世界中的WCF故障合同

时间:2012-01-06 13:47:02

标签: .net wcf design-patterns error-handling

假设我们有一个服务方法执行一些安全检查,从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个方法,比如上面的(甚至更复杂的)。并且定义所有这些错误合同变成了噩梦,因为这是一个非常容易出错的过程:

  1. 无法为整个服务定义一般故障(如DbFault)
  2. 您不能保证真正返回操作合同中定义的错误(复制粘贴问题)
  3. 您不能保证不会错过任何添加到操作合同中的错误
  4. 我们不要在这里考虑界面版本控制:)

    如果您在生产中支持WCF服务,那么您就可以了解情况。我们应该放弃故障合同并使用良好的旧C风格(比如具有ErrorCode属性的基础DTOBase类)?减少错误粒度?如何确保文档正确/最新?我对一些最佳实践感兴趣。

2 个答案:

答案 0 :(得分:9)

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

我建议采用以下方法:由于您正在创建服务和访问调用的“系统”,因此只定义与该系统相关的FaultContracts。客户应该只对以下问题感兴趣:

  1. 这是我想要的数据问题,还是我问的方式?
  2. 如果没有,这是我的问题,还是与IT相关的问题(系统崩溃,网络错误,数据库问题,无论如何)?
  3. 通过一些返工,您可以减少必须提供的故障合同的数量。例如(这不是我的头脑):

    //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://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

相关问题