在WCF中,當我們在調用服務端的方法時,一般有兩點需要考慮:1、捕獲服務端的異常資訊,記錄日誌;2、及時關閉會話通道,當調用逾時或調用失敗時及時中斷會話通道。我們一般會像下面這樣處理(以CalculatorService為例):
using (ChannelFactory<ICalculatorService> channelFactory = new ChannelFactory<ICalculatorService>("CalculatorService")) { ICalculatorService proxy = channelFactory.CreateChannel(); var commObj = proxy as ICommunicationObject; try { Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2)); commObj.Close(); } catch (CommunicationException) { commObj.Abort(); } catch (TimeoutException) { commObj.Abort(); } catch (Exception) { //TODO:記錄異常到日誌 commObj.Abort(); throw; } }
以上這種處理方式完全可以滿足我們的需要,但存在一個問題,每次調用服務端的方法時都需要手工加上這些異常捕獲代碼,如果服務有很多方法時,這樣勢必會存在大量功能重複的代碼,可複用性很差。我們可以借鑒一下AOP的思想,具體來說就是對服務端方法調用進行攔截。對於.Net應用來說,自訂RealProxy是實現方法調用攔截最簡單的一種方式。下面是一個自訂RealProxy的例子:
public class CalculatorServiceRealProxy : RealProxy { public CalculatorServiceRealProxy():base(typeof(ICalculatorService)){} public override IMessage Invoke(IMessage msg) { IMethodReturnMessage methodReturn = null; IMethodCallMessage methodCall = (IMethodCallMessage)msg; var client = new ChannelFactory<ICalculatorService>("CalculatorService"); var channel = client.CreateChannel(); try { object[] copiedArgs = Array.CreateInstance(typeof(object), methodCall.Args.Length) as object[]; methodCall.Args.CopyTo(copiedArgs, 0); object returnValue = methodCall.MethodBase.Invoke(channel, copiedArgs); methodReturn = new ReturnMessage(returnValue, copiedArgs, copiedArgs.Length, methodCall.LogicalCallContext, methodCall); //TODO:Write log } catch (Exception ex) { var exception = ex; if (ex.InnerException != null) exception = ex.InnerException; methodReturn = new ReturnMessage(exception, methodCall); } finally { var commObj = channel as ICommunicationObject; if (commObj != null) { try { commObj.Close(); } catch (CommunicationException) { commObj.Abort(); } catch (TimeoutException) { commObj.Abort(); } catch (Exception) { commObj.Abort(); //TODO:Logging exception throw; } } } return methodReturn; } }
方法調用:
static void Main(string[] args) { ICalculatorService proxy = (ICalculatorService)new CalculatorServiceRealProxy().GetTransparentProxy(); Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2)); Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2)); Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2)); Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2)); Console.ReadKey(); }
通過自訂的RealProxy建立TransparentProxy供用戶端代碼調用,對於通過TransparentProxy的每一次調用,都會被RealProxy接管,這樣我們就可以在RealProxy中加入異常捕獲、記錄日誌等非商務邏輯代碼,這些代碼在每次調用服務端方法時都會被調用。
原始碼下載 (工程ConsoleHosting為服務端,工程Client3為用戶端)