我试图在.net wcf服务(特别是支持Silverlight的wcf服务)中提出一个简单易用的设计模式来处理错误.如果在服务方法中抛出异常,silverlight应用程序将看到一个CommunicationException,指出"远程服务器返回错误:NotFound --->",可能还有一个堆栈跟踪,具体取决于您的设置,这完全没有用,因为它没有告诉你实际的错误,通常真正的错误不是"NotFound".
阅读Web服务和wcf服务和异常,您需要抛出soap/wcf标准异常,例如FaultException或SoapException.因此,对于wcf服务,您需要将每个方法包装在try catch中,捕获每个异常,将其包装在FaultException中并抛出它.至少这是我的理解,如果我错了,请纠正我.
所以我创建了我的设计模式:
[ServiceContract(Namespace = "http://MyTest")] [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] public class DataAccess { ////// Error class, handle converting an exception into a FaultException /// [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(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对象.帮助我想出一个漂亮的简单设计模式,让我轻松地从服务中抛出正确的异常,并轻松地在应用程序中捕获它.
我建议你集中管理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(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
根据抛出的异常发送不同的内容.
在客户端,FaultException
每次在Web服务上调用方法时都需要捕获.
好的,我查看了IErrorHandler的想法.我不知道你能用这种方式做到这一点,它是完美的,因为它可以让你避免尝试捕获每种方法.你也可以在标准的Web服务中做到这一点吗?我通过以下方式实现它:
////// 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. /// public class ServiceErrorHandler : IErrorHandler { ////// Default constructor /// public ServiceErrorHandler() { } ////// Specifies a url of the service /// /// public ServiceErrorHandler(string strUrl, bool bHandled) { this.strUrl_m = strUrl; this.bHandled_m = bHandled; } //////HandleError. Log an error, then allow the error to be handled as usual. ///Return true if the error is considered as already handled /// /// ///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; } /// ///Provide a fault. The Message fault parameter can be replaced, or set to ///null to suppress reporting a fault. /// /// /// /// 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(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; /// /// Specifies a url of the service, displayed in the error log /// public string Url { get { return this.strUrl_m; } } private bool bHandled_m; ////// Determines if the exception should be considered handled /// public bool Handled { get { return this.bHandled_m; } } } ////// 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. /// 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.Collectionendpoints, 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; /// /// Specifies a url of the service, displayed in the error log /// public string Url { get { return this.strUrl_m; } } private bool bHandled_m; ////// Determines if the ServiceErrorHandler will consider the exception handled /// 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!"); } }