首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

Enterprise Library 深入解析與靈活應用(6):自己動手打造迷你版 AOP 框架

     編輯:關於.NET

前言

公司專案廣泛使用 PIAB,但舊專案基於 EntLib 2.0 無法直接使用 PIAB(3.0 才推出)。為解決此問題,我以方法攔截原理實作一個簡易 AOP 框架,編程模式參考 PIAB。

一、使用方式

建立自訂 CallHandler,透過 Attribute 套用至目標方法:

public class UserManager : IUserManager
{
    [TransactionScopeCallHandler(Ordinal = 1)]
    [ExceptionCallHandler(Ordinal = 2, MessageTemplate = "遇到錯誤:\nMessage:{Message}")]
    public void CreateDuplicateUsers(string userID, string userName)
    {
        UserUtility.CreateUser(userID, userName);
        UserUtility.CreateUser(userID, userName); // 故意重複以觸發交易回滾
    }
}

執行結果顯示交易生效(重複寫入導致回滾)與異常訊息依模板輸出。執行結果

二、設計概要

核心類型:

  • ICallHandler:所有處理器介面
  • CallHandlerBase:抽象基類
  • HandlerAttribute:Attribute 基類
  • CallHandlerPipeline:有序處理器管道
  • InterceptingRealProxy<T>:繼承 RealProxy 實現攔截
  • InvocationContext:方法執行上下文
  • InstanceBuilder:透明 Proxy 建立工廠

UML 類圖

三、核心實作細節

1. InvocationContext

public class InvocationContext
{
    public IMethodCallMessage Request { get; set; }
    public ReturnMessage Reply { get; set; }
    public IDictionary<object, object> Properties { get; set; }
}

2. ICallHandler、CallHandlerBase、HandlerAttribute

public interface ICallHandler
{
    object PreInvoke(InvocationContext context);
    void PostInvoke(InvocationContext context, object correlationState);
    int Ordinal { get; set; }
    bool ReturnIfError { get; set; }
}

public abstract class CallHandlerBase : ICallHandler
{
    public abstract object PreInvoke(InvocationContext context);
    public abstract void PostInvoke(InvocationContext context, object correlationState);
    public int Ordinal { get; set; }
    public bool ReturnIfError { get; set; }
}

public abstract class HandlerAttribute : Attribute
{
    public abstract ICallHandler CreateCallHandler();
    public int Ordinal { get; set; }
    public bool ReturnIfError { get; set; }
}

3. CallHandlerPipeline

依序執行 PreInvoke、呼叫目標方法、依序執行 PostInvoke,支援異常中斷與相關狀態傳遞。

4. InterceptingRealProxy<T>

public class InterceptingRealProxy<T> : RealProxy
{
    private IDictionary<MemberInfo, CallHandlerPipeline> _callHandlerPipelines;

    public InterceptingRealProxy(object target, IDictionary<MemberInfo, CallHandlerPipeline> pipelines)
        : base(typeof(T))
    {
        this._callHandlerPipelines = pipelines;
    }

    public override IMessage Invoke(IMessage msg)
    {
        InvocationContext context = new InvocationContext { Request = (IMethodCallMessage)msg };
        this._callHandlerPipelines[context.Request.MethodBase].Invoke(context);
        return context.Reply;
    }
}

5. InstanceBuilder

掃描類別與方法上的 HandlerAttribute,建立對應 Pipeline 字典,產生透明 Proxy。

四、自訂 CallHandler 範例

1. TransactionScopeCallHandler

public class TransactionScopeCallHandler : CallHandlerBase
{
    public override object PreInvoke(InvocationContext context) => new TransactionScope();

    public override void PostInvoke(InvocationContext context, object correlationState)
    {
        var scope = (TransactionScope)correlationState;
        if (context.Reply.Exception == null) scope.Complete();
        scope.Dispose();
    }
}

2. ExceptionCallHandler

public class ExceptionCallHandler : CallHandlerBase
{
    public string MessageTemplate { get; set; } = "{Message}";
    public bool Rethrow { get; set; }

    public override object PreInvoke(InvocationContext context) => null;

    public override void PostInvoke(InvocationContext context, object correlationState)
    {
        if (context.Reply.Exception != null)
        {
            string message = this.MessageTemplate
                .Replace("{Message}", context.Reply.Exception.InnerException.Message)
                .Replace("{Source}", context.Reply.Exception.InnerException.Source)
                .Replace("{StackTrace}", context.Reply.Exception.InnerException.StackTrace)
                .Replace("{HelpLink}", context.Reply.Exception.InnerException.HelpLink)
                .Replace("{TargetSite}", context.Reply.Exception.InnerException.TargetSite.ToString());
            Console.WriteLine(message);
            if (!this.Rethrow)
                context.Reply = new ReturnMessage(null, null, 0, context.Request.LogicalCallContext, context.Request);
        }
    }
}

完整原始碼可供下載參考。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved