AOP的底层已经封装好了以后,我们就要开始针对应用层写具体的业务逻辑了。

也就是说我们需要有个类继承于AopProxyBase,并且重写其After,Bofore以达到我们的拦截记录的功能。代码如下:

public class TransactionProxy : AopProxyBase
{
public TransactionProxy(MarshalByRefObject obj, Type type)
: base(obj, type)
{ } public override void Before(System.Runtime.Remoting.Messaging.IMessage requestMsg, AopMethodAttribute[] attrs)
{ } public override void After(System.Runtime.Remoting.Messaging.IMessage requestMsg, System.Runtime.Remoting.Messaging.IMessage Respond, AopMethodAttribute[] attrs)
{
foreach (var attr in attrs)
{
if (attr is LogicRollBackTransAttribute)
{
return;
}
} var args = requestMsg.Properties["__Args"] as object[];
string methodName = requestMsg.Properties["__MethodName"] as string;
CustomTransaction customTrans = null;
List<object> list = new List<object>(); customTrans = CallContext.GetData(TransKey.CustomTransKey) as CustomTransaction;
if (customTrans != null)
{
list.AddRange(args);
TransactionUnit unit = AppTransactionManage.Instance.GetRollBackInfo(methodName);
if (unit != null)
{
unit.Argments = list;
unit.Mark = customTrans.Mark;
}
customTrans.Compensation.Add(unit); TransQueueManage.Instance.Push
(
new Model.BankTransLog
{
Mark = unit.Mark,
MethodName = methodName,
ParamsConfig = JsonHelper.ToJson(unit.Argments),
Status = ,
Type =
}
); CallContext.SetData(TransKey.CustomTransKey, customTrans); var outArgs = Respond.Properties["__OutArgs"] as object[];
IDbTransaction dbTrans;
foreach (var attr in attrs)
{
if (attr is DbTransAttribute || attr is LogicTransAttribute)
{
if (outArgs != null)
{
foreach (var arg in outArgs)
{
if (arg is IDbTransaction)
{
dbTrans = arg as IDbTransaction;
if (customTrans != null)
{
customTrans.AddDbTransaction(dbTrans);
}
}
}
}
}
}
}
}
}

在After的地方,我们可以看到,我们做了一次LogicRollBackTransAttribute的判定,避免在回调的时候,又再走一次拦截和记录的流程。

同时做了DbTransAttribute和LogicTransAttribute的判定。因为我把事务分为两类,一类是db本身自己控制的,可以直接rollback的,一类是logic的,需要我们去手动通过逻辑回滚的。代码如下:

[AttributeUsage(AttributeTargets.Method)]
public class LogicTransAttribute : AopMethodAttribute
{
public string MethodName { get; set; } public LogicTransAttribute()
{ } public LogicTransAttribute(string name)
{
this.MethodName = name;
}
} [AttributeUsage(AttributeTargets.Method)]
public class DbTransAttribute : AopMethodAttribute
{ }

同时可以看到,我把每一个函数的调用作为一个单元,用TransactionUnit类来保存,代码如下:

public class TransactionUnit
{
public object InstanceObject;
/// <summary>
/// 执行的方法
/// </summary>
public MethodInfo Forward;
/// <summary>
/// 失败回滚的方法
/// </summary>
public MethodInfo Rollback;
/// <summary>
/// 参数
/// </summary>
public IList<object> Argments;
/// <summary>
/// 唯一标识
/// </summary>
public string Mark;
}

因为,一个事务里面,可能包含了多次操作redis,或者多次操作db,为了保证线程安全,同时又需要避开锁,我用了CallContext将一个线程里面的一段事务,保存在其线程上下文中。在保存一个完整的TransactionUnit的时候,不可能每一次都去通过反射去取MethodInfo,所以又增加了一段初始化和字典来保存其MethodInfo。代码如下:

public class AppTransactionManage
{
private Dictionary<string, TransactionUnit> _transMaps; static AppTransactionManage() { }
private AppTransactionManage()
{
if (this._transMaps == null)
{
this._transMaps = new Dictionary<string, TransactionUnit>();
}
} private static AppTransactionManage _instance;
public static AppTransactionManage Instance
{
get
{
if (_instance == null)
{
_instance = new AppTransactionManage();
}
return _instance;
}
} public TransactionUnit GetRollBackInfo(string methodName)
{
if (this._transMaps == null) throw new ArgumentNullException("not init");
if (this._transMaps.ContainsKey(methodName))
{
return this._transMaps[methodName];
}
return null;
} public void Init(params string[] assembly)
{
this.Init(, assembly);
}
public void Init(int threadNum, params string[] assembly)
{
if (assembly != null)
{
foreach (string s in assembly)
{
var ass = Assembly.Load(s);
if (ass != null)
{
var types = ass.GetTypes();
foreach (var type in types)
{
var transAttr = type.GetCustomAttribute(typeof(TransactionAttribute), false) as TransactionAttribute;
if (transAttr != null)
{
var methods = type.GetMethods();
foreach (var method in methods)
{
var forwardTrans = method.GetCustomAttribute(typeof(LogicTransAttribute), false) as LogicTransAttribute;
var rollbackTrans = method.GetCustomAttribute(typeof(LogicRollBackTransAttribute), false) as LogicRollBackTransAttribute; TransactionUnit unit;
if (forwardTrans != null)
{
if (!this._transMaps.TryGetValue(forwardTrans.MethodName, out unit))
{
unit = new TransactionUnit();
}
unit.Forward = method;
unit.InstanceObject = Activator.CreateInstance(type);
this._transMaps[forwardTrans.MethodName] = unit;
} if (rollbackTrans != null)
{
if (!this._transMaps.TryGetValue(rollbackTrans.MethodName, out unit))
{
unit = new TransactionUnit();
}
unit.Rollback = method;
unit.InstanceObject = Activator.CreateInstance(type);
this._transMaps[rollbackTrans.MethodName] = unit;
}
}
}
}
}
}
} TransQueueManage.Instance.Init(
(t) =>
{
BankTransLogBLL.Instance.Add(t);
},
threadNum
);
}
}

为了友好开发者的调用,可以让其像使用SqlTransaction一样来使用,我又对外公开了一个CustomTranstion,将调用方式封装在这个类里面,代码如下:

public class CustomTransaction : IDisposable
{
private List<IDbTransaction> _dbTransactions; private bool _isRollBack = true; /// <summary>
/// 补偿机制
/// </summary>
public List<TransactionUnit> Compensation; public void Commit()
{
if (this._dbTransactions != null)
{
this._dbTransactions.ForEach((t) => t.Commit());
}
this._isRollBack = false;
} public void RollBack()
{
if (this.Compensation != null)
{
this.Compensation.ForEach((t) =>
{
object[] paramsArray = t.Argments == null ? null : t.Argments.ToArray();
t.Rollback.Invoke(t.InstanceObject, paramsArray);
});
}
if (this._dbTransactions != null)
{
this._dbTransactions.ForEach((t) => t.Rollback());
}
} private bool _isRetry = true; public CustomTransaction(bool isRetry = true)
{
this._isRetry = isRetry;
if (this._dbTransactions == null)
{
this._dbTransactions = new List<IDbTransaction>();
}
if (this.Compensation == null)
{
this.Compensation = new List<TransactionUnit>();
}
CallContext.SetData(TransKey.CustomTransKey, this);
} public void AddDbTransaction(IDbTransaction transaction)
{
this._dbTransactions.Add(transaction);
} public void Dispose()
{
if (this._isRollBack)
{
this.RollBack();
}
CallContext.FreeNamedDataSlot(TransKey.CustomTransKey);
}
}

这个时候,你就可以像是用SqlTransaction一样去Using(var trans = new CustomTranstion()){}然后在using里面去写trans.Commit();来提交所有的事务操作,如果不做Commit操作的话,在CustomTranstion里面,会自动去调用其rollback()操作。

但是这并没有完,所有的只是记录下来了,但是并没有保存到DB去做持久化。这个时候就需要增加一个队列,来不断的去将TransactionUnit来保存到db,同时又需要把队列去做持久化,避免一些意外原因,导致队列数据丢失,而缺失了这部分的日志记录(虽然我个人认为这一部分可以省略)。代码如下:

 [Serializable]
public class TransQueue : IDisposable
{
public Queue<Model.BankTransLog> _transQueue;
public Action<Model.BankTransLog> ExecuteAction;
private Thread _thread;
private bool _isDispose; public delegate void PersistenceHandler(Model.BankTransLog[] models); PersistenceHandler persistenceHandler; private readonly object _syncObject = new object();
public TransQueue()
{
if (_transQueue == null)
{
_transQueue = new Queue<Model.BankTransLog>();
}
if (persistenceHandler == null)
{
persistenceHandler = PersistenceToDisk;
} if (_thread == null)
{
_thread = new Thread(Thread_Work)
{
IsBackground = true
};
}
_thread.Start();
} public void Push(Model.BankTransLog model)
{
if (_transQueue == null) throw new ArgumentNullException("transQueue is not init"); lock (_syncObject)
{
_transQueue.Enqueue(model);
}
} public void Thread_Work()
{
while (!_isDispose)
{
Model.BankTransLog[] items = null;
if (_transQueue != null && _transQueue.Count > )
{
lock (_syncObject)
{
items = new Model.BankTransLog[_transQueue.Count];
_transQueue.CopyTo(items, );
_transQueue.Clear();
}
} if (items != null && items.Length > )
{
persistenceHandler.BeginInvoke(items, PersistenceHandlerCallBack, persistenceHandler);
foreach (var item in items)
{
if (ExecuteAction != null)
{
ExecuteAction.Invoke(item);
}
}
}
Thread.Sleep();
}
} public void PersistenceHandlerCallBack(IAsyncResult result)
{
try
{
(result.AsyncState as PersistenceHandler).EndInvoke(result);
}
catch (Exception e)
{
}
} public void PersistenceToDisk(Model.BankTransLog[] items)
{
try
{
BinaryHelper.SaveToFile(items);
}
catch (Exception e)
{ }
} public void Dispose()
{
_isDispose = true;
_thread.Join();
} } public class TransQueueManage
{
private int _threadNumber = ;
private TransQueue[] _transQueue;
Random random = new Random(); public Action<Model.BankTransLog> ExecuteAction;
private TransQueueManage()
{ } static TransQueueManage()
{ } public void Init(Action<Model.BankTransLog> action, int threadNum = )
{
if (_transQueue == null)
{
this._threadNumber = threadNum;
_transQueue = new TransQueue[threadNum];
for (var i = ; i < threadNum; i++)
{
_transQueue[i] = new TransQueue();
_transQueue[i].ExecuteAction = action;
}
}
} private static readonly object _syncObject = new object();
private static TransQueueManage _instance;
public static TransQueueManage Instance
{
get
{
if (_instance == null)
{
lock (_syncObject)
{
if (_instance == null)
{
_instance = new TransQueueManage();
}
}
}
return _instance;
}
} public void Push(Model.BankTransLog model)
{
var index = GetRandomThreadIndex();
_transQueue[index].Push(model);
} public int GetRandomThreadIndex()
{
return random.Next(, this._threadNumber);
}
}

利用AOP写2PC框架(二)的更多相关文章

  1. 利用AOP写2PC框架(一)

    并不是很想写这个系列,因为这个2pc单独写一个小架构有点鸡肋.不过也不知道写什么了,先写了再说吧. 整个流程如下图: 关于AOP系列的文章很多,我这里也再重复造一下轮子. 首先,我们定义了一个IAop ...

  2. (二)springMvc原理和手写springMvc框架

    我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...

  3. 关于Quartz.NET作业调度框架的一点小小的封装,实现伪AOP写LOG功能

    Quartz.NET是一个非常强大的作业调度框架,适用于各种定时执行的业务处理等,类似于WINDOWS自带的任务计划程序,其中运用Cron表达式来实现各种定时触发条件是我认为最为惊喜的地方. Quar ...

  4. Android开发之手把手教你写ButterKnife框架(二)

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...

  5. 手写DAO框架(二)-开发前的最后准备

    -------前篇:手写DAO框架(一)-从“1”开始 --------- 前言:前篇主要介绍了写此框架的动机,把主要功能点大致介绍了一下.此篇文章主要介绍开发前最后的一些准备.主要包括一些基础知识点 ...

  6. 手写MVC框架(二)-代码实现和使用示例

    --------上一篇:手写MVC框架(一)-再出发----- 背景 书接上文,之前整理了实现MVC框架需要写哪些东西.这周粗看了一下,感觉也没多少工作量,所以就计划一天时间来完成.周末的时间,哪会那 ...

  7. 手写MQ框架(二)-服务端实现

    一.起航 书接上文->手写MQ框架(一)-准备启程 本着从无到有,从有到优的原则,所以计划先通过web实现功能,然后再优化改写为socket的形式. 1.关于技术选型 web框架使用了之前写的g ...

  8. C# 使用Emit实现动态AOP框架 (二)

    目  录 C# 使用Emit实现动态AOP框架 (一) C# 使用Emit实现动态AOP框架 (二) C# 使用Emit实现动态AOP框架 (三) C# 使用Emit实现动态AOP框架 进阶篇之异常处 ...

  9. 利用神经网络算法的C#手写数字识别(二)

    利用神经网络算法的C#手写数字识别(二)   本篇主要内容: 让项目编译通过,并能打开图片进行识别.   1. 从上一篇<利用神经网络算法的C#手写数字识别>中的源码地址下载源码与资源, ...

随机推荐

  1. javascript中的this与函数讲解

    前言 javascript中没有块级作用域(es6以前),javascript中作用域分为函数作用域和全局作用域.并且,大家可以认为全局作用域其实就是Window函数的函数作用域,我们编写的js代码, ...

  2. 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文

    阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...

  3. java中的字符串相关知识整理

    字符串为什么这么重要 写了多年java的开发应该对String不陌生,但是我却越发觉得它陌生.每学一门编程语言就会与字符串这个关键词打不少交道.看来它真的很重要. 字符串就是一系列的字符组合的串,如果 ...

  4. 前端框架 EasyUI (0) 重新温习(序言)

    几年前,参与过一个项目.那算是一个小型的信息管理系统,BS 结构的,前端用的是基于 jQuery 的 EasyUI 框架. 我进 Team 的时候,项目已经进入开发阶段半个多月了.听说整个项目的框架是 ...

  5. Objective-C三种定时器CADisplayLink / NSTimer / GCD的使用

    OC中的三种定时器:CADisplayLink.NSTimer.GCD 我们先来看看CADiskplayLink, 点进头文件里面看看, 用注释来说明下 @interface CADisplayLin ...

  6. 移动先行之谁主沉浮? 带着你的Net飞奔吧!

    移动系源码:https://github.com/dunitian/Windows10 移动系文档:https://github.com/dunitian/LoTDotNet/tree/master/ ...

  7. Photoshop将普通照片快速制作二次元漫画风格效果

    今天为大家分享Photoshop将普通照片快速制作二次元漫画风格效果,教程很不错,对于喜欢漫画的朋友可以参考本文,希望能对大家有所帮助! 一提到日本动画电影,大家第一印象肯定是宫崎骏,但是日本除了宫崎 ...

  8. Anders Hejlsberg 技术理想架构开发传奇

    Anders Hejlsberg(安德斯-海森博格) 坐在自己的办公室,双眼直直的盯着前方.他要做一个决定,决定自己未来的命运和理想.这是1996年一个普通的下午,几个小时前,他刚与比尔-盖茨结束了 ...

  9. 【SAP业务模式】之ICS(四):组织单元的配置

    SAP的ICS业务后台配置主要有以下几个配置点: 1.组织单元的配置(公司代码.销售组织.工厂.采购组织等): 2.主数据的部分: 3.订单和开票的定价过程: 4.开票输出类型: 5.公司间发票的配置 ...

  10. (转)从0开始搭建SQL Server AlwaysOn 第一篇(配置域控+域用户DCADMIN)

    原文地址: http://www.cnblogs.com/lyhabc/p/4678330.html 实验环境: 准备工作 软件准备 (1) SQL Server 2012 (2) Windows S ...