在实际的应用程序中,可能经常会遇到在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和记录日志的代码就可以通通省去了。