我们正在使用优秀的ELMAH来处理ASP.NET 3.5 Web应用程序中的未处理异常。除了使用REST功能使用的WCF服务之外,这对于所有站点都非常有效。当应用程序代码未处理的操作方法中发生异常时,WCF会以各种方式处理它,具体取决于服务协定和配置设置。这意味着该异常不会最终触发ELMAH使用的ASP.NET HttpApplication.Error事件。我知道要处理的两个解决方案是:
第一个选项非常简单但不完全是DRY。第二个选项仅要求您在实现属性和ErrorHandler后使用自定义属性装饰每个服务。我已根据Will's工作完成此操作,但我想在发布代码之前验证这是正确的方法。
我错过了更好的方法吗?
IErrorHandler的MSDN文档说明 HandleError 方法是进行日志记录的地方,但ELMAH访问HttpContext.Current。 ApplicationInstance ,即使HttpContext.Current可用,在此方法中为null。在ProvideFault方法中调用Elmah是一种解决方法,因为ApplicationInstance已设置,但这与API文档中描述的意图不匹配。 我在这里遗漏了什么?文档确实说明你不应该依赖于在操作线程上调用的HandleError方法,这可能是ApplicationInstance在此范围内为空的原因。
答案 0 :(得分:87)
我的博客文章(在OP中引用)的解决方案基于我们在错误状态期间用于更改HTTP响应代码的现有解决方案。
所以,对我们来说,将Exception传递给ELMAH是一个单行更改。如果有更好的解决方案,我也很想知道它。
对于后人/参考,以及潜在的改进 - 这是当前解决方案的代码。
using System;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Net;
using System.Web;
using Elmah;
namespace YourApplication
{
/// <summary>
/// Your handler to actually tell ELMAH about the problem.
/// </summary>
public class HttpErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return false;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error != null ) // Notify ELMAH of the exception.
{
if (System.Web.HttpContext.Current == null)
return;
Elmah.ErrorSignal.FromCurrentContext().Raise(error);
}
}
}
/// <summary>
/// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
/// ...and errors reported to ELMAH
/// </summary>
public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
{
Type errorHandlerType;
public ServiceErrorBehaviourAttribute(Type errorHandlerType)
{
this.errorHandlerType = errorHandlerType;
}
public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler;
errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
}
使用ServiceErrorBehaviour属性:
装饰您的WCF服务[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
// ...
}
答案 1 :(得分:9)
创建BehaviorExtensionElement时,甚至可以使用config激活行为:
public class ErrorBehaviorExtensionElement : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(ServiceErrorBehaviourAttribute); }
}
protected override object CreateBehavior()
{
return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler));
}
}
配置:
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<elmah />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
这样也可以将ELMAH与RIA服务结合使用!
答案 2 :(得分:2)
我是根据威尔的工作完成的 但我想验证这是什么 发布前的正确方法 代码。
我认为这是一个很好的方法(对于这个帖子,Will对此感到荣幸!)。我不认为威尔或你在这里错过了什么。实现IErrorHandler是捕获所有可能的服务器端异常的首选方法,否则可能导致通信通道出现故障(拆除),因此它是一个自然的地方,可以挂钩某些日志记录,如ELMAH。
马克
答案 3 :(得分:2)
对于某些人来说这可能是显而易见的,但我花了很长时间试图弄清楚为什么我的HttpContext.Current是空的,尽管跟随了Will Hughes的所有优秀答案。令人尴尬的是,我意识到这是因为我的WCF服务是由MSMQ消息激活的。
我最终重写了ProvideFault()
方法:
if (HttpContext.Current == null)
{
ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
ErrorSignal.FromCurrentContext().Raise(error);
}
答案 4 :(得分:1)
我无法使用WCF数据服务获得建议的答案。我连接了行为属性等,但仍然没有记录任何错误。相反,我最终将以下内容添加到服务实现中:
protected override void HandleException(HandleExceptionArgs args)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
base.HandleException(args);
}
答案 5 :(得分:0)
我没有尝试使用REST明确地执行此操作,并且自己没有使用ELMAH,但另一个值得研究的选项可能是使用IDispatchMessageInspector而不是IErrorHandler挂钩到WCF。