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

C# 利用 Attribute 實作 AOP 交易管理

     編輯:關於C#

前言

傳統交易管理需在每個方法手寫 try-catch、Commit/Rollback,侵入性強且易遺漏。本文展示以 ContextBoundObject、CallContext 與自訂 Attribute 實作非侵入式 AOP 交易。

核心架構

1. 資料存取基底類別 SqlDAL

public abstract class SqlDAL : ContextBoundObject {
    private SqlTransaction _sqlTrans;
    public SqlTransaction SqlTrans {
        get {
            if (_sqlTrans == null) {
                object obj = CallContext.GetData(TransactionAop.ContextName);
                if (obj != null && obj is SqlTransaction) _sqlTrans = (SqlTransaction)obj;
            }
            return _sqlTrans;
        }
        set { _sqlTrans = value; }
    }
    // ExecuteNonQuery 多載:若 SqlTrans 不為 null 則使用交易執行
    public int ExecuteNonQuery(string cmdText) {
        return SqlTrans == null 
            ? SqlHelper.ExecuteNonQuery(ConnStr, CommandType.Text, cmdText)
            : SqlHelper.ExecuteNonQuery(SqlTrans, CommandType.Text, cmdText);
    }
    // ... 其他多載省略
}

關鍵:繼承 ContextBoundObject,透過 CallContext 取得當前交易。

2. 業務邏輯類別標記 Attribute

[Transaction]
public class UserInfoAction : SqlDAL {
    [TransactionMethod]
    public void Add(UserInfo user) {
        // 僅撰寫業務 SQL,無交易程式碼
        ExecuteNonQuery(sql);
    }
}

3. 類別層級 Attribute:TransactionAttribute

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public sealed class TransactionAttribute : ContextAttribute, IContributeObjectSink {
    public TransactionAttribute() : base("Transaction") { }
    public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next) {
        return new TransactionAop(next);
    }
}

4. 方法層級 Attribute:TransactionMethodAttribute

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class TransactionMethodAttribute : Attribute { }

兩個 Attribute 必須搭配使用:類別標記啟用攔截器,方法標記指定哪些方法納入交易。

5. 交易攔截器 TransactionAop

public sealed class TransactionAop : IMessageSink {
    private IMessageSink _nextSink;
    public TransactionAop(IMessageSink next) { _nextSink = next; }
    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) { return null; }
    public IMessageSink NextSink { get { return _nextSink; } }

    public IMessage SyncProcessMessage(IMessage msg) {
        IMethodCallMessage call = msg as IMethodCallMessage;
        // 無標記或非目標方法直接傳遞
        if (call == null || Attribute.GetCustomAttribute(call.MethodBase, typeof(TransactionMethodAttribute)) == null)
            return _nextSink.SyncProcessMessage(msg);

        // 建立交易並存入 CallContext
        using (SqlConnection conn = new SqlConnection(ConnStr)) {
            conn.Open();
            SqlTransaction trans = conn.BeginTransaction();
            CallContext.SetData(ContextName, trans);
            IMessage retMsg = _nextSink.SyncProcessMessage(msg); // 執行實際業務方法
            IMethodReturnMessage ret = retMsg as IMethodReturnMessage;
            if (ret.Exception != null) trans.Rollback();
            else trans.Commit();
        }
        return retMsg;
    }
    public static string ContextName { get { return "TransactionAop"; } }
}

補充:效能與架構優化

  • AOP 方式比手寫交易稍低效能,但大幅提升可維護性與使用便利性。
  • 避免 SqlDAL 直接繼承 ContextBoundObject 造成效能損耗,可改為組合模式:在業務類別中持有 SqlDAL 實例,於屬性存取時從 CallContext 取得交易指派。

結語

此模式實現「想要交易加兩個標記,不想要就移除」的非侵入式管理,同樣原理可延伸至權限控制、日誌記錄等横切關注點。

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