public sealed class TransactionAop : IMessageSink
{
private readonly IMessageSink _nextSink;
public TransactionAop(IMessageSink nextSink) { _nextSink = nextSink; }
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) => null;
public IMessageSink NextSink => _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); // 無標記,直接傳遞
// 有標記,建立交易
using (var conn = new SqlConnection(Configurations.SQLSERVER_CONNECTION_STRING))
{
conn.Open();
var trans = conn.BeginTransaction();
CallContext.SetData(ContextName, trans); // 存入上下文
IMessage retMsg = _nextSink.SyncProcessMessage(msg); // 執行目標方法
if (trans != null)
{
var returnMsg = retMsg as IMethodReturnMessage;
if (returnMsg?.Exception != null)
trans.Rollback(); // 發生例外回滾
else
trans.Commit(); // 正常提交
trans.Dispose();
}
return retMsg;
}
}
public static string ContextName => "TransactionAop";
}
try-catch-Commit/Rollback 效能稍低,但可維護性、易用性大幅提升。SqlDAL 繼承 ContextBoundObject 會增加上下文切換開銷。替代方案:SqlDAL 改為普通類別,透過屬性取得 CallContext 交易,避免繼承開銷。
private SqlDAL _sqlDao;
public SqlDAL SqlDao
{
get
{
if (_sqlDao == null)
{
_sqlDao = new SqlDAL();
object obj = CallContext.GetData(TransactionAop.ContextName);
if (obj != null && obj is SqlTransaction) _sqlDao.SqlTrans = (SqlTransaction)obj;
}
return _sqlDao;
}
}
持續探索更輕量化方案,歡迎建議。