2009-01-14 47 views
5

我试图想出一个简单易用的设计模式,用于在.net wcf服务(特别是启用silverlight的wcf服务)中进行错误处理。如果在服务方法中抛出一个异常,silverlight应用程序将看到一个CommunicationException,声明“远程服务器返回一个错误:NotFound --->”,并且可能是一个堆栈跟踪,这取决于您的设置,这是完全没有用的,因为它没有“告诉你实际的错误,通常真正的错误不是“NotFound”。.net WFC/Web服务异常处理设计模式

阅读Web服务和wcf服务和异常,您需要抛出soap/wcf标准异常(如FaultException或SoapException)。因此,对于wcf服务,您需要将每个方法包装在try catch中,捕获每个异常,并将其包装在FaultException中并将其引发。至少这是我的理解,如果我错了,请纠正我。

所以,我已经建立了我的设计模式:

[ServiceContract(Namespace = "http://MyTest")] 
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 
public class DataAccess 
{ 
    /// <summary> 
    /// Error class, handle converting an exception into a FaultException 
    /// </summary> 
    [DataContractAttribute] 
    public class Error 
    { 
     private string strMessage_m; 
     private string strStackTrace_m; 

     public Error(Exception ex) 
     { 
      this.strMessage_m = ex.Message; 
      this.strStackTrace_m = ex.StackTrace; 
     } 

     [DataMemberAttribute] 
     public string Message 
     { 
      get { return this.strMessage_m; } 
      set { this.strMessage_m = value; } 
     } 

     [DataMemberAttribute] 
     public string StackTrace 
     { 
      get { return this.strStackTrace_m; } 
      set { this.strStackTrace_m = value; } 
     } 

     //Convert an exception into a FaultException 
     public static void Throw(Exception ex) 
     { 
      if (ex is FaultException) 
      { 
       throw ex; 
      } 
      else 
      { 
       throw new FaultException<Error>(new Error(ex)); 
      } 
     } 
    } 

    [OperationContract] 
    [FaultContract(typeof(Error))] 
    public void TestException() 
    { 
     try 
     { 
      throw new Exception("test"); 
     } 
     catch (Exception ex) 
     { 
      Error.Throw(ex); 
     } 
    } 
} 

因此,为了使长话短说,我仍然没有得到正确的错误在我的Silverlight应用程序。我检查了AsyncCompletedEventArgs.Error对象,它仍然包含带有一般错误的CommunicationException对象。帮助我想出一个不错的简单设计模式,以便让我轻松地从服务中抛出正确的异常,并轻松地将其应用于应用程序中。

回答

5

好吧,我看着IErrorHandler的想法。我不知道你可以这样做,它是完美的,因为它可以让你避免尝试每种方法。你也可以在标准的Web服务中做到这一点吗?我实现了以下方法:

/// <summary> 
/// Services can intercept errors, perform processing, and affect how errors are reported using the 
/// IErrorHandler interface. The interface has two methods that can be implemented: ProvideFault and 
/// HandleError. The ProvideFault method allows you to add, modify, or suppress a fault message that 
/// is generated in response to an exception. The HandleError method allows error processing to take 
/// place in the event of an error and controls whether additional error handling can run. 
/// 
/// To use this class, specify it as the type in the ErrorBehavior attribute constructor. 
/// </summary> 
public class ServiceErrorHandler : IErrorHandler 
{ 
    /// <summary> 
    /// Default constructor 
    /// </summary> 
    public ServiceErrorHandler() 
    { 
    } 

    /// <summary> 
    /// Specifies a url of the service 
    /// </summary> 
    /// <param name="strUrl"></param> 
    public ServiceErrorHandler(string strUrl, bool bHandled) 
    { 
     this.strUrl_m = strUrl; 
     this.bHandled_m = bHandled; 
    } 

    /// <summary> 
    ///HandleError. Log an error, then allow the error to be handled as usual. 
    ///Return true if the error is considered as already handled 
    /// </summary> 
    /// <param name="error"></param> 
    /// <returns></returns> 
    public virtual bool HandleError(Exception exError) 
    { 
     System.Diagnostics.EventLog evt = new System.Diagnostics.EventLog("Application", ".", "My Application"); 
     evt.WriteEntry("Error at " + this.strUrl_m + ":\n" + exError.Message, System.Diagnostics.EventLogEntryType.Error); 

     return this.bHandled_m; 
    } 

    /// <summary> 
    ///Provide a fault. The Message fault parameter can be replaced, or set to 
    ///null to suppress reporting a fault. 
    /// </summary> 
    /// <param name="error"></param> 
    /// <param name="version"></param> 
    /// <param name="msg"></param> 
    public virtual void ProvideFault(Exception exError, 
     System.ServiceModel.Channels.MessageVersion version, 
     ref System.ServiceModel.Channels.Message msg) 
    { 
     //Any custom message here 
     /* 
     DataAccessFaultContract dafc = new DataAccessFaultContract(exError.Message); 

     System.ServiceModel.FaultException fe = new System.ServiceModel.FaultException<DataAccessFaultContract>(dafc); 
     System.ServiceModel.Channels.MessageFault fault = fe.CreateMessageFault(); 

     string ns = "http://www.example.com/services/FaultContracts/DataAccessFault"; 
     msg = System.ServiceModel.Channels.Message.CreateMessage(version, fault, ns); 
     */ 
    } 

    private string strUrl_m; 
    /// <summary> 
    /// Specifies a url of the service, displayed in the error log 
    /// </summary> 
    public string Url 
    { 
     get 
     { 
      return this.strUrl_m; 
     } 
    } 

    private bool bHandled_m; 
    /// <summary> 
    /// Determines if the exception should be considered handled 
    /// </summary> 
    public bool Handled 
    { 
     get 
     { 
      return this.bHandled_m; 
     } 
    } 
} 

/// <summary> 
/// The ErrorBehaviorAttribute exists as a mechanism to register an error handler with a service. 
/// This attribute takes a single type parameter. That type should implement the IErrorHandler 
/// interface and should have a public, empty constructor. The attribute then instantiates an 
/// instance of that error handler type and installs it into the service. It does this by 
/// implementing the IServiceBehavior interface and then using the ApplyDispatchBehavior 
/// method to add instances of the error handler to the service. 
/// 
/// To use this class specify the attribute on your service class. 
/// </summary> 
public class ErrorBehaviorAttribute : Attribute, IServiceBehavior 
{ 
    private Type typeErrorHandler_m; 

    public ErrorBehaviorAttribute(Type typeErrorHandler) 
    { 
     this.typeErrorHandler_m = typeErrorHandler; 
    } 

    public ErrorBehaviorAttribute(Type typeErrorHandler, string strUrl, bool bHandled) 
     : this(typeErrorHandler) 
    { 
     this.strUrl_m = strUrl; 
     this.bHandled_m = bHandled; 
    } 

    public virtual void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) 
    { 
     return; 
    } 

    public virtual void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters) 
    { 
     return; 
    } 

    protected virtual IErrorHandler CreateTypeHandler() 
    { 
     IErrorHandler typeErrorHandler; 

     try 
     { 
      typeErrorHandler = (IErrorHandler)Activator.CreateInstance(this.typeErrorHandler_m, this.strUrl_m, bHandled_m); 
     } 
     catch (MissingMethodException e) 
     { 
      throw new ArgumentException("The ErrorHandler type specified in the ErrorBehaviorAttribute constructor must have a public constructor with string parameter and bool parameter.", e); 
     } 
     catch (InvalidCastException e) 
     { 
      throw new ArgumentException("The ErrorHandler type specified in the ErrorBehaviorAttribute constructor must implement System.ServiceModel.Dispatcher.IErrorHandler.", e); 
     } 

     return typeErrorHandler; 
    } 

    public virtual void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase) 
    { 
     IErrorHandler typeErrorHandler = this.CreateTypeHandler();    

     foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) 
     { 
      ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher; 
      channelDispatcher.ErrorHandlers.Add(typeErrorHandler); 
     } 
    } 

    private string strUrl_m; 
    /// <summary> 
    /// Specifies a url of the service, displayed in the error log 
    /// </summary> 
    public string Url 
    { 
     get 
     { 
      return this.strUrl_m; 
     } 
    } 

    private bool bHandled_m; 
    /// <summary> 
    /// Determines if the ServiceErrorHandler will consider the exception handled 
    /// </summary> 
    public bool Handled 
    { 
     get 
     { 
      return this.bHandled_m; 
     } 
    } 
} 

服务:

[ServiceContract(Namespace = "http://example.come/test")] 
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] 
[ErrorBehavior(typeof(ServiceErrorHandler),"ExceptonTest.svc",false)] 
public class ExceptonTest 
{ 
    [OperationContract] 
    public void TestException() 
    { 
     throw new Exception("this is a test!"); 
    } 
} 
+5

就像一个建议,请不要填写你的代码和评论。由于SO允许代码和文本混合使用,因此人们不必滚动浏览相对较小的代码块即可查看您正在执行的操作。 – 2010-05-21 17:39:17

6

我建议你集中你的WCF服务的错误处理,而不是把try/catch放在每个方法上。要做到这一点,你可以实现IErrorHandler接口:

public class ErrorHandler : IErrorHandler 
{ 
    public bool HandleError(Exception error) 
    { 
     return true; 
    } 

    public void ProvideFault(Exception error, MessageVersion version, ref Message msg) 
    { 
     DataAccessFaultContract dafc = new DataAccessFaultContract(error.Message); 
     var fe = new FaultException<DataAccessFaultContract>(dafc); 
     Message fault = fe.CreateMessageFault(); 
     string ns = "http://www.example.com/services/FaultContracts/DataAccessFault"; 
     msg = Message.CreateMessage(version, fault, ns); 
    } 
} 

ProvideFault方法被调用时,您的OperationContract之一抛出异常。它会将异常转换为自定义的FaultContract并将其发送给客户端。这样您就不再需要在每种方法中都放入try/catch。根据抛出的异常,您也可以发送不同的FaultContract

在客户端,每次调用Web服务的方法时都需要捕获FaultException<DataAccessFaultContract>

+7

好了,你怎么用的ErrorHandler类,然后呢?你如何将它与你的服务联系起来? – Jeremy 2009-01-15 16:10:46

-6

对于懒惰(像我一样):

using System.ServiceModel; 
using System.ServiceModel.Dispatcher; 
using System.ServiceModel.Description;