【问题标题】:WCF fault contracts in real world现实世界中的 WCF 故障合约
【发布时间】: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 种上述方法(甚至更复杂)。并且定义所有这些错误合约成为一场噩梦,因为这是一个非常容易出错的过程:

  1. 无法为整个服务定义一般故障(如 DbFault)
  2. 您不能保证操作合同中定义的故障会真正返回(复制粘贴问题)
  3. 您不能保证您没有错过添加到操作合同中的某些错误

我们不要在这里考虑接口版本:)

因此,如果您在生产中支持 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你不能这样做

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


【解决方案1】:

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

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

  1. 这是我想要的数据的问题,还是我询问的方式有问题?
  2. 如果不是,这是我的问题,还是 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?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-23
    • 2010-09-15
    相关资源
    最近更新 更多