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和记录日志的代码就可以通通省去了。
在实际的应用程序中,可能经常会遇到在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和记录日志的代码就可以通通省去了。
相关文章推荐
- Extending WCF(四)—一个统一处理异常、日志的解决方案
- Spring MVC异常统一处理(异常信息的国际化,日志记录)
- SpringMVC统一异常处理 返回JSON解决方案
- 利用spring aop统一处理异常和打日志
- 利用spring aop统一处理异常和打日志
- Spring MVC异常统一处理(异常信息的国际化,日志记录)
- STS创建Spring Boot项目实战(Rest接口、数据库、用户认证、分布式Token JWT、Redis操作、日志和统一异常处理)
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- jfinal统一的异常及日志处理的拦截器
- spring mvc 异常处理统一解决方案-最佳实践
- Spring MVC自定义统一异常处理类,并且在控制台中输出错误日志
- STS创建Spring Boot项目实战(Rest接口、数据库、用户认证、分布式Token JWT、Redis操作、日志和统一异常处理)
- SpringMVC异常统一处理(异常信息的国际化,日志记录)
- Spring MVC统一异常处理解决方案
- 【SpringBoot】web进阶——表单验证,AOP统一处理请求日志,统一异常处理,单元测试
- 在一个网站里有很多aspx页 如何统一处理项目中的所有异常?(面试题)
- 使用Spring.NET统一ASP.NET异常处理并记录日志
- 前后台JSON传值得一个问题和异常处理net.sf.json.JSONException: Unquotted string '"name"'
- 异常处理和日志