Reflection

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

在实际的应用程序中,可能经常会遇到在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和记录日志的代码就可以通通省去了。
posted on 2008-05-23 16:56  Reflection  阅读(3674)  评论(8编辑  收藏  举报