业务逻辑层的设计(五)——事务脚本模式介绍
经过了几个月的奋斗,我终于又可以坐下来写点东西了。
业务逻辑层设计到此为止已经有第五篇了。
1、学习曲线低,可以说会C#和SQL的程序员就可以上手了,和领域驱动设计相比很少有改各BUG另一边又出问题的情况。
2、与ASP.NET WebForm完美契合,事件驱动和事务脚本的理解契合,快速开发,是其他开源界没有的,如Java。没错,这就是ASP.NET WebForm下正确的打开方式。
3、上限很高,在高级的架构师手里一样可以利用AOP切面编程做日志、权限等,目前的MVC结合其他框架,其实已经包含了很多设计模式,在设计师几乎没有什么空间可以用设计模式,而在事务脚本里却可以体现一个设计师的水平,设计师要造很多框架的轮子,这也是上限很高的原因。
事务脚本模式的思路:
通俗一点来说,就是你点了一个按钮触发一个事件时,这个事件中将处理一系列的命令,这些命令组合在一起形成一个事务,然后又可以在另一个按钮事件中重新将他们组合起来,形成一个新的事务。
比如,我的保存事件中执行一个事务,1检查一些状态;2保存一些数据;
然后我在另一个提交事件中也执行一个事务,1检查一些状态;2保存一些数据;3流程提交;
伪代码表示
public void Save(){
Dao1 dao1=new Dao1(); dao1.IsExists();//检查一些东西
Dao2 dao1=new Dao2(); dao2.Save();//保存一些数据 } public void Submit(){ try{ BeginTran(); Dao1 dao1=new Dao1(); dao1.IsExists();//检查一些东西 Dao2 dao1=new Dao2(); dao2.Save();//保存一些数据 Dao3 dao3=new Dao3(); dao3.GoToNextFlow();//流程提交 Commit(); }catch(){ Rollback(); } }
看完以上伪代码我们发现保存按钮的时候我们没有启用事务,
而在提交按钮的时候我们开启了事务,需要保证数据的保存和提交在一个事务中。
事务管理的实现原理:
将事务管理从数据访问层上提,放到业务层,并且消除ADO.NET的事务参数。
如何实现?我们需要将一个数据库连接贯穿整个事件(或者是每个WEB请求),在事件中所有DAO都将使用同一个数据库连接。
我们只要利用CallContext类把数据库连接放到一个请求上下文中,DAO需要操作数据库时从CallContext中取出连接即可。
WEB应用程序中也可以直接使用HttpContext。
在老项目中最好是自己对DBHelper作扩展,简单地使用继承就能在不修改原来代码的基础上扩展出新的DBHelper。
如果你准备新开发一个系统,那么可以选择使用Spring或者Spring.NET,参看事务管理的那部分,将事务托管给Spring来管理。
事务脚本模式实战:
public void Appiont(decimal? schemeid,decimal? aogtype) { try { BeginTransaction(); GetCurrToProviderFlowCMD getcurrToProviderCMD = new GetCurrToProviderFlowCMD(schemeid); getcurrToProviderCMD.Run(); //验证流程1是否已开始 if (getcurrToProviderCMD.CurrFlow > 0) throw new Exception("流程1已经开始,无法重新指定!"); GetCurrToStorageFlowCMD getcurrToStorageCMD = new GetCurrToStorageFlowCMD(schemeid); getcurrToStorageCMD.Run(); //验证流程2是否已开始 if (getcurrToStorageCMD.CurrFlow > 0) throw new Exception("流程2已经开始,无法重新指定!"); AOGTypeFlowDeleteCMD aogtypeflowdelCMD = new AOGTypeFlowDeleteCMD(schemeid); aogtypeflowdelCMD.Run(); //删除流程任务 AOGTypeAppointCMD aogtypeappointCMD = new AOGTypeAppointCMD(schemeid, aogtype); if (aogtypeappointCMD.Run() == 0) throw new DataBaseExecuteInvalidException(); //指定到货方式 if (aogtype != null) { IAOGTypeFlowCreateCMD aogtypeflowcreateCMD = AOGTypeFlowCreate.Init(schemeid, aogtype); if (aogtypeflowcreateCMD.Run() == 0) throw new DataBaseExecuteInvalidException(); //插入新的流程任务 } Commit(); } catch (Exception ex) { RollBack(); throw new Exception(ex.Message); } }
public class GetCurrToProviderFlowCMD : BaseCommand { private decimal? _schemeid; private int _currFlow; public int CurrFlow { get { return _currFlow; } } public GetCurrToProviderFlowCMD(decimal? schemeid) { _schemeid = schemeid; } public int Run() { StringBuilder builder = new StringBuilder(); builder.Append("select currflow from oproviderflow where schemeid=:schemeid"); OracleParameter prm = new OracleParameter(":schemeid", OracleDbType.Decimal); prm.Value = _schemeid; string res = database.ExecuteScalar(builder.ToString(), prm); _currFlow = res != "" ? Convert.ToInt32(res) : 0; return 1; } }
下面是使用微软企业库实现的版本
public class FirstCommand:BaseCommand { private DataTable _table; private int _id; public DataTable Table { get { return _table; } } public FirstCommand(int id) { this._id = id; } public int Run() { IDbCommand cmd=dbHelper.GetSqlStringCommand("select * from SYS_RESOURCE where ID=:resid"); dbHelper.AddInParameter(cmd, ":resid", DbType.Int32, _id); _table = dbHelper.ExecuteDataTable(cmd); return 1; } }