您的位置:首页 > 其它

Extending WCF(四)—一个统一处理异常、日志的解决方案

2011-06-23 16:23 337 查看
Extending WCF(四)—一个统一处理异常、日志的解决方案

在实际的应用程序中,可能经常会遇到在Service端如何统一处理异常,记Log等的问题。这些问题可能很多AOP框架已经给出了解决方案。其实对于WCF来说简单的做一下扩展就可以解决这个问题了。

由于处理异常,记日志主要针对的是Operation级别,所以我选择的扩展点是IOperationInvoker
接口,这个接口提供在Operation调用时的拦截。先看一下它的定义:



public

interface
IOperationInvoker



{



//
Methods



object
[] AllocateInputs();



object
Invoke(
object
instance,
object
[] inputs,
out

object
[] outputs);



IAsyncResult InvokeBegin(
object
instance,
object
[] inputs, AsyncCallback callback,
object
state);



object
InvokeEnd(
object
instance,
out

object
[] outputs, IAsyncResult result);





//
Properties



bool
IsSynchronous
{
get
; }



}





它提供了同步和异步调用Operation的方法。调用Operation的过程当然还是由WCF本身来完成,我们要做的就是在调用前和调用后来做日志和处理异常。下面是我的实现类:



public

class
MyInvoker : IOperationInvoker



{



IOperationInvoker m_OldInvoker;



InterceptionType m_InteType;





public
MyInvoker(IOperationInvoker oldInvoker, InterceptionType inteType)



{



Debug.Assert(oldInvoker
!=

null
);





m_OldInvoker
=
oldInvoker;



m_InteType
=
inteType;



}



public

virtual

object
[] AllocateInputs()



{



return
m_OldInvoker.AllocateInputs();



}





protected

void
PreInvoke(
object
instance,
object
[] inputs)



{



if
(m_InteType
==
InterceptionType.None)



{



}



else

if
(m_InteType
==
InterceptionType.LogInvoke)



{



}





}





protected

void
PostInvoke(
object
instance,
object
returnedValue,
object
[] outputs, Exception err)



{



if
(m_InteType
==
InterceptionType.None)



{



}



else

if
(m_InteType
==
InterceptionType.LogInvoke)



{



}



else

if
(m_InteType
==
InterceptionType.LogException)



{



}



else

if
(m_InteType
==
InterceptionType.LogExceptionAndMail)



{



}



}





public

object
Invoke(
object
instance,
object
[] inputs,
out

object
[] outputs)



{



PreInvoke(instance, inputs);



object
returnedValue
=

null
;



object
[] outputParams
=

new

object
[]
{ }

;



Exception exception
=

null
;



try



{



returnedValue
=
m_OldInvoker.Invoke(instance, inputs,
out
outputParams);



outputs
=
outputParams;



return
returnedValue;



}



catch
(Exception err)



{



outputs
=

null
;



exception
=
err;



return

null
;



}



finally



{



PostInvoke(instance, returnedValue, outputParams, exception);





}



}





public
IAsyncResult InvokeBegin(
object
instance,
object
[] inputs, AsyncCallback callback,
object
state)



{



PreInvoke(instance, inputs);



return
m_OldInvoker.InvokeBegin(instance, inputs, callback, state);



}





public

object
InvokeEnd(
object
instance,
out

object
[] outputs, IAsyncResult result)



{



object
returnedValue
=

null
;



object
[] outputParams
=

{ }

;



Exception exception
=

null
;



try



{



returnedValue
=
m_OldInvoker.InvokeEnd(instance,
out
outputs, result);



outputs
=
outputParams;



return
returnedValue;



}



catch
(Exception err)



{



outputs
=

null
;



exception
=
err;



return

null
;



}



finally



{



PostInvoke(instance, returnedValue, outputParams, exception);



}



}



public

bool
IsSynchronous



{



get



{



return
m_OldInvoker.IsSynchronous;



}



}



}

在PreInvoke和PostInvoke方法中可以加入我们自己的代码。InterceptionType
是一个枚举,里面定义了拦截的类型:



public

enum
InterceptionType



{



None,





LogInvoke,





LogException,





LogExceptionAndMail



}

这个枚举可以根据需要来进行扩展。如何让WCF使用我们自己的这个OperationInvoker呢,可以通过加入Operation Behavior来完成:



[AttributeUsage(AttributeTargets.Method)]



public

class
MyOperationInterceptorAttribute : Attribute, IOperationBehavior



{



private
InterceptionType m_InteType
=
InterceptionType.None;





public
MyOperationInterceptorAttribute()
{ }





public
MyOperationInterceptorAttribute(InterceptionType inteType)



{



this
.m_InteType
=
inteType;



}





protected
MyInvoker CreateInvoker(IOperationInvoker oldInvoker)



{



return

new
MyInvoker(oldInvoker, m_InteType);



}





public

void
AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)



{ }





public

void
ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)



{ }





public

void
ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)



{



IOperationInvoker oldInvoker
=
dispatchOperation.Invoker;



dispatchOperation.Invoker
=
CreateInvoker(oldInvoker);



}





public

void
Validate(OperationDescription operationDescription)



{ }



}

在ApplyDispatchBehavior中换掉原来的Invoker就可以了。所以我们只要在Operation上打上这个标签就可以了,并且在打标签的同时可以指定拦截的类型。

这样可能还比较麻烦,因为我要挨个方法都去打上标签,那么我们可以往更高层次的Behavior去扩展。这里可以使用Service Behavior:



[AttributeUsage(AttributeTargets.Class)]



public

class
MyServiceInterceptorAttribute : Attribute,IServiceBehavior



{



protected
MyOperationInterceptorAttribute CreateOperationInterceptor()



{



return

new
MyOperationInterceptorAttribute();



}





public

void
ApplyDispatchBehavior(ServiceDescription serviceDescription,ServiceHostBase host)



{



foreach
(ServiceEndpoint endpoint
in
serviceDescription.Endpoints)



{



foreach
(OperationDescription operation
in
endpoint.Contract.Operations)



{



bool
checkresult
=

false
;



foreach
(IOperationBehavior behavior
in
operation.Behaviors)



{



if
(behavior
is
MyOperationInterceptorAttribute)



{



checkresult
=

true
;



break
;



}



}



if
(
!
checkresult)



{



operation.Behaviors.Add(CreateOperationInterceptor());



}



}



}



}



public

void
AddBindingParameters(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase,Collection
<
ServiceEndpoint
>
endpoints,BindingParameterCollection bindingParameters)



{}





public

void
Validate(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)



{}



}

在ApplyDispatchBehavior
这个方法中会去给没有打上标签的Operation都打上标签。所以我们只需要给Service打个标签就可以了。

通过这个简单的实例,我们就可以对Operation级别的异常和日志进行统一的处理了,代码中的try catch和记录日志的代码就可以通通省去了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: