业务逻辑层的设计(五)——事务脚本模式介绍

经过了几个月的奋斗,我终于又可以坐下来写点东西了。

    业务逻辑层设计到此为止已经有第五篇了。

 

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;
        }
    }

 

posted @ 2013-11-20 21:19  十三燕  阅读(6105)  评论(1编辑  收藏  举报