62

ASP.NET 3.5 Web アプリケーションで未処理の例外を処理するために、優れたELMAHを使用しています。これは、REST 機能を使用して消費される WCF サービスを除くすべてのサイトで非常にうまく機能します。アプリケーション コードで処理されない操作メソッド内で例外が発生した場合、WCF はサービス コントラクトと構成設定に応じてさまざまな方法で処理します。これは、 ELMAHが使用する ASP.NET HttpApplication.Error イベントを例外が発生させないことを意味します。これに対処するために私が知っている2つの解決策は次のとおりです。

  • すべてのメソッド呼び出しを try { } catch(Exception ex) { Elmah.ErrorSignal.FromCurrentContext().Raise(ex); でラップします。投げる; catch ブロック内で Elmah を明示的に呼び出します。
  • Will Hughes のブログ投稿で説明されているように、IErrorHandlerを使用して、 WCF と ELMAHをうまく組み合わせて、ELMAH の呼び出しを別の ErrorHandler に分解します。

最初のオプションは非常に単純ですが、正確にはDRYではありません。2 番目のオプションでは、属性と ErrorHandler を実装した後、カスタム属性で各サービスを装飾するだけで済みます。私はウィルの仕事に基づいてこれを行いましたが、コードを投稿する前にこれが正しいアプローチであることを確認したいと思います.

私が逃したより良い方法はありますか?

IErrorHandlerの MSDN ドキュメントによると、HandleErrorメソッドはログを記録する場所ですが、ELMAHは HttpContext.Current にアクセスします。HttpContext.Currentが使用可能であっても、このメソッド内では null です。ProviderFault メソッド内で Elmah を呼び出すことは、ApplicationInstance が設定されているため回避策ですが、これは API ドキュメントで説明されている意図と一致しません。ここで何か不足していますか?ドキュメントには、操作スレッドで呼び出される HandleError メソッドに依存してはならないことが記載されています。これが、このスコープで ApplicationInstance が null である理由である可能性があります。

4

6 に答える 6

88

私のブログ投稿 (OP で参照) の解決策は、エラー状態で HTTP 応答コードを変更するために使用していた、または使用している既存の解​​決策に基づいていました。

そのため、例外を ELMAH に渡すのは 1 行の変更でした。より良い解決策があれば、それについても知りたいです。

後世/参照、および潜在的な改善について-現在のソリューションのコードを次に示します。

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
{
  // ...
}
于 2009-05-25T12:11:14.800 に答える
9

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>

このように、RIA サービスと組み合わせて ELMAH を使用することも可能です。

于 2011-06-17T13:42:38.073 に答える
3

これは一部の人には明らかかもしれませんが、Will Hughes の優れた回答をすべて実行しているにもかかわらず、 HttpContext.Current が null である理由を理解するのにかなりの時間を費やしました。恥ずかしいことに、これは私の WCF サービスが MSMQ メッセージによってアクティブ化されているためであることに気付きました。

私はメソッドを書き直しましたProvideFault()

if (HttpContext.Current == null)
{
    ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
    ErrorSignal.FromCurrentContext().Raise(error);
}
于 2013-12-05T16:47:14.080 に答える
2

私はウィルの仕事に基づいてこれを行いましたが、コードを投稿する前にこれが正しいアプローチであることを確認したいと思います.

これは素晴らしいアプローチだと思います (この投稿については、Will に感謝します!)。ウィルもあなたもここで何も見逃していないと思います。IErrorHandler の実装は、そうしないと通信チャネルに障害が発生する (破棄される) 可能性のあるすべてのサーバー側例外をキャプチャするための推奨される方法であるため、ELMAH などのログをフックする自然な場所です。

マルク

于 2009-05-22T05:25:14.727 に答える
1

WCF Data Service で動作する提案された回答を得ることができませんでした。動作属性などを配線しましたが、それでもエラーがログに記録されませんでした。代わりに、サービスの実装に以下を追加することになりました。

protected override void HandleException(HandleExceptionArgs args)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
    base.HandleException(args);
}
于 2013-12-05T19:40:19.137 に答える