在实际的应用程序中,可能经常会遇到在Service端如何统一处理异常,记Log等的问题。这些问题可能很多AOP框架已经给出了解决方案。其实对于WCF来说简单的做一下扩展就可以解决这个问题了。
由于处理异常,记日志主要针对的是Operation级别,所以我选择的扩展点是IOperationInvoker接口,这个接口提供在Operation调用时的拦截。先看一下它的定义:
public
interface
IOperationInvoker
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
// 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);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// Properties
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
bool IsSynchronous
{ get; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
它提供了同步和异步调用Operation的方法。调用Operation的过程当然还是由WCF本身来完成,我们要做的就是在调用前和调用后来做日志和处理异常。下面是我的实现类:
public
class
MyInvoker : IOperationInvoker
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
IOperationInvoker m_OldInvoker;
InterceptionType m_InteType;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public MyInvoker(IOperationInvoker oldInvoker, InterceptionType inteType)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
Debug.Assert(oldInvoker != null);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
m_OldInvoker = oldInvoker;
m_InteType = inteType;
}
public virtual object[] AllocateInputs()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return m_OldInvoker.AllocateInputs();
}
protected void PreInvoke(object instance, object[] inputs)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (m_InteType == InterceptionType.None)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
else if (m_InteType == InterceptionType.LogInvoke)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
protected void PostInvoke(object instance, object returnedValue, object[] outputs, Exception err)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (m_InteType == InterceptionType.None)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
else if (m_InteType == InterceptionType.LogInvoke)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
else if (m_InteType == InterceptionType.LogException)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
else if (m_InteType == InterceptionType.LogExceptionAndMail)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public object Invoke(object instance, object[] inputs, out object[] outputs)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
PreInvoke(instance, inputs);
object returnedValue = null;
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
object[] outputParams = new object[]
{ };
Exception exception = null;
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
returnedValue = m_OldInvoker.Invoke(instance, inputs, out outputParams);
outputs = outputParams;
return returnedValue;
}
catch (Exception err)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
outputs = null;
exception = err;
return null;
}
finally
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
PostInvoke(instance, returnedValue, outputParams, exception);
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
PreInvoke(instance, inputs);
return m_OldInvoker.InvokeBegin(instance, inputs, callback, state);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
object returnedValue = null;
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
object[] outputParams =
{ };
Exception exception = null;
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
returnedValue = m_OldInvoker.InvokeEnd(instance, out outputs, result);
outputs = outputParams;
return returnedValue;
}
catch (Exception err)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
outputs = null;
exception = err;
return null;
}
finally
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
PostInvoke(instance, returnedValue, outputParams, exception);
}
}
public bool IsSynchronous
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
get
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return m_OldInvoker.IsSynchronous;
}
}
}
在PreInvoke和PostInvoke方法中可以加入我们自己的代码。InterceptionType是一个枚举,里面定义了拦截的类型:
public
enum
InterceptionType
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
None,
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
LogInvoke,
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
LogException,
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
LogExceptionAndMail
}
这个枚举可以根据需要来进行扩展。如何让WCF使用我们自己的这个OperationInvoker呢,可以通过加入Operation Behavior来完成:
[AttributeUsage(AttributeTargets.Method)]
public
class
MyOperationInterceptorAttribute : Attribute, IOperationBehavior
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
private InterceptionType m_InteType = InterceptionType.None;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
public MyOperationInterceptorAttribute()
{ }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public MyOperationInterceptorAttribute(InterceptionType inteType)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
this.m_InteType = inteType;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
protected MyInvoker CreateInvoker(IOperationInvoker oldInvoker)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new MyInvoker(oldInvoker, m_InteType);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{ }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{ }
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
IOperationInvoker oldInvoker = dispatchOperation.Invoker;
dispatchOperation.Invoker = CreateInvoker(oldInvoker);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void Validate(OperationDescription operationDescription)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{ }
}
在ApplyDispatchBehavior中换掉原来的Invoker就可以了。所以我们只要在Operation上打上这个标签就可以了,并且在打标签的同时可以指定拦截的类型。
这样可能还比较麻烦,因为我要挨个方法都去打上标签,那么我们可以往更高层次的Behavior去扩展。这里可以使用Service Behavior:
[AttributeUsage(AttributeTargets.Class)]
public
class
MyServiceInterceptorAttribute : Attribute,IServiceBehavior
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
protected MyOperationInterceptorAttribute CreateOperationInterceptor()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return new MyOperationInterceptorAttribute();
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,ServiceHostBase host)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
foreach(ServiceEndpoint endpoint in serviceDescription.Endpoints)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
foreach(OperationDescription operation in endpoint.Contract.Operations)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
bool checkresult = false;
foreach (IOperationBehavior behavior in operation.Behaviors)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (behavior is MyOperationInterceptorAttribute)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
checkresult = true;
break;
}
}
if (!checkresult)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
operation.Behaviors.Add(CreateOperationInterceptor());
}
}
}
}
public void AddBindingParameters(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase,Collection<ServiceEndpoint> endpoints,BindingParameterCollection bindingParameters)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void Validate(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{}
}
在
ApplyDispatchBehavior这个方法中会去给没有打上标签的Operation都打上标签。所以我们只需要给Service打个标签就可以了。
通过这个简单的实例,我们就可以对Operation级别的异常和日志进行统一的处理了,代码中的try catch和记录日志的代码就可以通通省去了。