2009-01-14 46 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;