WCF サービス内部で発生した例外は、1ヶ所でまとめてハンドルして、ログ出力などのエラー処理を行いたいですよね。
WCF では catch されなかった例外をハンドルするために、IErrorHandler インタフェースが提供されています。このインタフェースを実装したクラスを、ChannelDispatcher クラスの ErrorHandler プロパティに設定してやる事で、例外をまとめてハンドル可能になります。
文章で説明するよりも、コードを見た方が分かり易いかな。
まずはカスタム ErrorHandler を作成します。
public class LogErrorHandler : IErrorHandler { public bool HandleError(Exception error) { // TODO: ここにログを出力するコードを記述する Console.WriteLine(error.Message); return true; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { // 今回は何もしない。 // 本当は、ここで fault にフォールトメッセージを設定します。 } }
次に、IErrorHandler 実装クラスをエラーハンドラとして登録するために、ServiceBehavior を作成します。
public sealed class LogErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior { // エラーハンドラ private LogErrorHandler _errorHandler; public LogErrorHandlerBehaviorAttribute() { _errorHandler = new LogErrorHandler(); } public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase) { } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { // 作成したカスタム ErrorHandler を設定 foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers) { ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher; if (channelDispatcher != null) { channelDispatcher.ErrorHandlers.Add(_errorHandler); } } } }
あとは、作成したカスタム ServiceBehavior を、サービスクラスに指定すれば OK。
[ServiceContract] public interface IFooService { [OperationContract] void Foo(); } [LogErrorHandlerBehaviorAttribute] public class FooService : IFooService { public void Foo() { // わざと例外を投げる throw new Exception("Test"); } }