ASP.NET 3.5 Webアプリケーションで未処理の例外を処理するために、優れた [〜#〜] elmah [〜#〜] を使用しています。これは、REST機能を使用して消費されているWCFサービス以外のすべてのサイトで非常にうまく機能します。アプリケーションコードで処理されない操作メソッド内で例外が発生すると、WCFはこれは、サービスコントラクトと構成設定に応じて、さまざまな方法で行われます。つまり、例外がASP.NET HttpApplication.Errorイベントを発生させない [〜#〜] elmah [〜#〜] これに対処するために私が知っている2つのソリューションは次のとおりです。
最初のオプションは非常に単純ですが、正確ではありません [〜#〜] dry [〜#〜] 。 2番目のオプションでは、属性とErrorHandlerを実装した後、カスタム属性で各サービスを装飾するだけで済みます。私は Will's の作業に基づいてこれを行いましたが、コードを投稿する前に、これが正しいアプローチであることを確認したいと思います。
見逃したより良い方法はありますか?
IErrorHandler のMSDNドキュメントでは、HandleErrorメソッドがロギングを行う場所であると述べていますが、 [〜# 〜] elmah [〜#〜] HttpContext.Current。ApplicationInstanceにアクセスします。これは、HttpContext.Currentが使用可能であっても、このメソッド内ではnullです。 。 ApplicationInstanceが設定されているため、ProvideFaultメソッド内でElmahを呼び出すことは回避策ですが、これはAPIドキュメントで説明されている意図と一致しません。 ここに何か不足していますか?ドキュメントには、操作スレッドで呼び出されるHandleErrorメソッドに依存するべきではないことが記載されています。この範囲で。
私のブログ投稿(OPで参照)のソリューションは、エラー状態の間にHTTP応答コードを変更するために使用していた既存のソリューションに基づいていました。
したがって、私たちにとって、例外をELMAHに渡すのは1行の変更でした。より良い解決策があれば、私もそれについて知りたいです。
後世/参照、および潜在的な改善のために-現在のソリューションからのコードは次のとおりです。
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を作成するときに、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>
そうすることで、RIAサービスと組み合わせてELMAHを使用することもできます。
これは一部の人には明らかかもしれませんが、Will Hughesの優れた回答をすべてフォローしたにもかかわらず、HttpContext.Currentがnullである理由を理解するためにかなりの時間を費やしました。恥ずかしいことに、これは私のWCFサービスがMSMQメッセージによってアクティブ化されるためであることに気付きました。
ProvideFault()
メソッドを書き直してしまいました。
if (HttpContext.Current == null)
{
ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
ErrorSignal.FromCurrentContext().Raise(error);
}
私はウィルの仕事に基づいてこれを行いましたが、コードを投稿する前にこれが正しいアプローチであることを確認したいと思います。
これは素晴らしいアプローチだと思います(この投稿ではWillへのkudos!)。ウィルかあなたはここで何かを逃したとは思わない。 IErrorHandlerの実装は、そうでなければ通信チャネルに障害が発生する(破壊される)可能性があるすべての可能なサーバー側の例外をキャプチャするための推奨される方法であり、ELMAHのようなログにフックするのは自然な場所です。
マーク
提案された回答をWCFデータサービスで機能させることができませんでした。ビヘイビアー属性などを配線しましたが、それでもエラーは記録されませんでした。代わりに、サービス実装に以下を追加することになりました。
protected override void HandleException(HandleExceptionArgs args)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
base.HandleException(args);
}
RESTのものを使用してこれを明示的に試したことはなく、ELMAHを自分で使用したことはありませんが、 IErrorHandlerの代わりにIDispatchMessageInspector 。