我们正在使用优秀的 埃尔玛 处理 ASP.NET 3.5 Web 应用程序中未处理的异常。这对于除使用 REST 功能使用的 WCF 服务之外的所有站点都非常有效。当操作方法中发生应用程序代码未处理的异常时,WCF 根据服务契约和配置设置以各种方式进行处理。这意味着异常最终不会触发 ASP.NET HttpApplication.Error 事件 埃尔玛 用途。我知道处理这个问题的两个解决方案是:

第一个选项非常简单,但并不完全是 干燥. 。第二个选项只需要您在实现属性和 ErrorHandler 后使用自定义属性来装饰每个服务。我这样做是基于 威尔的 工作,但我想验证这是 正确的做法 在发布代码之前。

有没有更好的方法我错过了?

MSDN 文档 错误处理程序处理错误 方法是进行日志记录的地方,但是 埃尔玛 访问 HttpContext.Current。应用程序实例, ,即使 HttpContext.Current 可用,该方法中的值仍为 null。在 ProvideFault 方法中调用 Elmah 是一种解决方法,因为已设置 ApplicationInstance,但这与 API 文档中描述的意图不符。 我在这里错过了什么吗? 该文档确实指出您不应依赖在操作线程上调用的 HandleError 方法,这可能就是 ApplicationInstance 在此范围内为 null 的原因。

有帮助吗?

解决方案

我的博客文章中的解决方案(在 OP 中引用)基于我们曾经/正在用来在错误状态期间更改 HTTP 响应代码的现有解决方案。

因此,对于我们来说,将异常传递给 ELMAH 只需一行更改。如果有更好的解决方案,我也很想知道。

对于后代/参考和潜在的改进 - 这是当前解决方案的代码。

HttpErrorHandler 和 ServiceErrorBehaviourAttribute 类

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
{
  // ...
}

其他提示

创建BehaviorExtensionElement时,甚至可以使用配置激活行为:

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 服务结合使用!

我已经根据Will的工作来完成此操作,但我想验证这是发布代码之前的正确方法。

我认为这是一个很好的方法(感谢威尔的这篇文章!)。我想威尔或者你都没有错过任何事情。实现 IErrorHandler 是捕获所有可能的服务器端异常的首选方法,否则可能会导致通信通道出现故障(被破坏),因此它是挂钩某些日志记录(如 ELMAH)的自然位置。

马克

这对某些人来说可能是显而易见的,但我花了相当长的时间试图弄清楚为什么我的 HttpContext.Current 为空,尽管遵循了 Will Hughes 的所有出色答案。尴尬的是,我意识到这是因为我的 WCF 服务是由 MSMQ 消息激活的。

我最终重写了 ProvideFault() 方法:

if (HttpContext.Current == null)
{
    ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
    ErrorSignal.FromCurrentContext().Raise(error);
}

我无法使用 WCF 数据服务获得建议的答案。我连接了行为属性等,但仍然没有记录任何错误。相反,我最终将以下内容添加到服务实现中:

protected override void HandleException(HandleExceptionArgs args)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
    base.HandleException(args);
}

我还没有尝试使用 REST 明确地执行此操作,并且自己也没有使用 ELMAH,但值得研究的另一个选项可能是使用 IDispatchMessageInspector 而不是 IErrorHandler。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top