🕌
WCFの集約例外と操作記録
元記事(最新はこちらを参照):
概要
- WCFサービスを利用した際に集約例外と操作記録を行う方法
- 実務ではDebug.Printの代わりにログツール(NLog, log4net)を利用する
環境
- .NETFx 4.8
- Visual Studio 202
処理概要
-
(1) 集約例外、操作記録の属性
LogErrorOperationAttribute
を作成する-
System.Attribute
を継承する -
System.Web.Services.Description.IServiceBehavior
インタフェース実装を作成する
-
-
(2) 集約例外
-
System.Web.Services.Description.IServiceBehavior
インタフェースのApplyDispatchBehavior
メソッドで、serviceHostBase.ChannelDispatchers
のエラーハンドルを利用する- 独自に作成した
System.ServiceModel.Dispatcher.IErrorHandler
を継承したLogErrorHandler
クラスを追加し、この中のHandleError
イベントで記録する
- 独自に作成した
-
-
(3) 操作記録
-
System.Web.Services.Description.IServiceBehavior
インタフェースのApplyDispatchBehavior
メソッドで、serviceDescription.Endpoints
の操作Contract.Operations
にLoggingOperationBehavior
を設定する-
System.Web.Services.Description.IOperationBehavior
実装のLoggingOperationBehavior
では、ApplyDispatchBehavior
メソッドで、パラメーターを受け取るためのユーザー定義メソッドとしてLoggingOperationInvoker
を設定する-
System.ServiceModel.Dispatcher.IOperationBehavior
実装のLoggingOperationInvoker
では、Invoke
メソッドで操作を記録する
-
-
-
-
(4) WCFサービスの実装に
LogErrorOperationAttribute
属性を設定する
呼び出し側
Dim sv = New Service1Service.Service1Client
Debug.Print(sv.DoWork("test"))
Web側
WCFメソッド
- WCFの実装クラスに作成した属性
LogErrorOperationAttribute
を設定する
' メモ: コンテキスト メニューの [名前の変更] コマンドを使用すると、コード、svc、および config ファイルで同時にクラス名 "Service1" を変更できます。
' 注意: このサービスをテストするために WCF テスト クライアントを起動するには、ソリューション エクスプローラーで Service1.svc または Service1.svc.vb を選択し、デバッグを開始してください。
<LogErrorOperationAttribute>
Public Class Service1
Implements IService1
Public Function DoWork(value1 As String) As String Implements IService1.DoWork
Return $"{value1}:{Now}"
End Function
End Class
ログ
開始 アクション名:IService1/DoWork,引数:"p1":"test",
終了 アクション名:IService1/DoWork
属性
- 操作記録は簡易的な実装
LogErrorOperationAttribute.vb
Imports System.Collections.ObjectModel
Imports System.Net
Imports System.ServiceModel
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Description
Imports System.ServiceModel.Dispatcher
Imports System.Web.Services.Description
''' <summary>
''' 集約例外、操作記録のための拡張
''' WCFサービスの属性として設定
''' </summary>
Public Class LogErrorOperationAttribute
Inherits Attribute
Implements IServiceBehavior
Private _errorHandler As LogErrorHandler
Public Sub New()
_errorHandler = New LogErrorHandler
End Sub
Public Sub Validate(serviceDescription As Description.ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.Validate
End Sub
Public Sub AddBindingParameters(serviceDescription As Description.ServiceDescription, serviceHostBase As ServiceHostBase, endpoints As Collection(Of ServiceEndpoint), bindingParameters As BindingParameterCollection) Implements IServiceBehavior.AddBindingParameters
End Sub
Public Sub ApplyDispatchBehavior(serviceDescription As Description.ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.ApplyDispatchBehavior
'集約例外
For Each channelDispatcherBase In serviceHostBase.ChannelDispatchers
Dim channelDispatcher = CType(channelDispatcherBase, ChannelDispatcher)
If channelDispatcher IsNot Nothing Then
channelDispatcher.ErrorHandlers.Add(_errorHandler)
End If
Next
'操作記録
For Each endpoint In serviceDescription.Endpoints
For Each operation In endpoint.Contract.Operations
Dim behavior = New LoggingOperationBehavior()
operation.Behaviors.Add(behavior)
Next
Next
End Sub
End Class
''' <summary>
''' 集約例外
''' </summary>
Public Class LogErrorHandler
Implements IErrorHandler
Public Sub ProvideFault([error] As Exception, version As MessageVersion, ByRef fault As Channels.Message) Implements IErrorHandler.ProvideFault
End Sub
Public Function HandleError([error] As Exception) As Boolean Implements IErrorHandler.HandleError
Debug.Print([error].ToString)
Return True
End Function
End Class
''' <summary>
''' 操作記録
''' </summary>
Public Class LoggingOperationBehavior
Implements IOperationBehavior
Public Sub Validate(operationDescription As OperationDescription) Implements IOperationBehavior.Validate
End Sub
Public Sub ApplyDispatchBehavior(operationDescription As OperationDescription, dispatchOperation As DispatchOperation) Implements IOperationBehavior.ApplyDispatchBehavior
dispatchOperation.Invoker = New LoggingOperationInvoker(dispatchOperation.Invoker, dispatchOperation)
End Sub
Public Sub ApplyClientBehavior(operationDescription As OperationDescription, clientOperation As ClientOperation) Implements IOperationBehavior.ApplyClientBehavior
End Sub
Public Sub AddBindingParameters(operationDescription As OperationDescription, bindingParameters As BindingParameterCollection) Implements IOperationBehavior.AddBindingParameters
End Sub
End Class
''' <summary>
''' 操作記録
''' </summary>
Public Class LoggingOperationInvoker
Implements IOperationInvoker
Private _baseInvoker As IOperationInvoker
''' <summary>
''' 操作のアクション。例:http://tempuri.org/IService1/DoWork
''' </summary>
Private _action As String
Public Sub New(baseInvoker As IOperationInvoker, operation As DispatchOperation)
_baseInvoker = baseInvoker
_action = operation.Action?.Replace("http://tempuri.org/", "")
End Sub
Public ReadOnly Property IsSynchronous As Boolean Implements IOperationInvoker.IsSynchronous
Get
Return _baseInvoker.IsSynchronous
End Get
End Property
Public Function AllocateInputs() As Object() Implements IOperationInvoker.AllocateInputs
Return _baseInvoker.AllocateInputs()
End Function
Public Function Invoke(instance As Object, inputs() As Object, ByRef outputs() As Object) As Object Implements IOperationInvoker.Invoke
Try
Dim argString = GetArgString(inputs)
Debug.Print($"開始 アクション名:{_action},引数:{argString}")
Catch ex As Exception
Debug.Print(ex.ToString())
Debug.Print($"開始 LOGGING_PARAMETER_EXCEPTION")
End Try
Dim result = _baseInvoker.Invoke(instance, inputs, outputs)
Debug.Print($"終了 アクション名:{_action}")
Return result
End Function
Private Function GetArgString(inputs() As Object) As String
Dim i = 0
Dim sb = New StringBuilder()
For Each item As Object In inputs
i += 1
sb.Append($"""p{i}"":")
If (TypeOf (item) Is String) OrElse (TypeOf (item) Is DateTime) Then
sb.Append($"""{item}""")
ElseIf item Is Nothing Then
sb.Append($"Nothing")
Else
Dim aryPublicProperties = item.GetType()?.GetProperties()
If aryPublicProperties Is Nothing Then
sb.Append($"""{item}""")
Else
sb.Append($"""{item}"":{{")
For Each p In aryPublicProperties
sb.Append($"""{p.Name}"":""{p.GetValue(item, Nothing)}"",")
Next
End If
End If
sb.Append(",")
Next
Return sb.ToString()
End Function
Public Function InvokeBegin(instance As Object, inputs() As Object, callback As AsyncCallback, state As Object) As IAsyncResult Implements IOperationInvoker.InvokeBegin
Return _baseInvoker.InvokeBegin(instance, inputs, callback, state)
End Function
Public Function InvokeEnd(instance As Object, ByRef outputs() As Object, result As IAsyncResult) As Object Implements IOperationInvoker.InvokeEnd
Return _baseInvoker.InvokeEnd(instance, outputs, result)
End Function
End Class
参考
Discussion