Winform开发框架里面使用事务操作的原理及介绍

简介:

在很多情况下,事务是个很有用的东西,可以把一系列的操作组合成一个原子粒度的操作,一旦组合中某个地方出错,可以整个干净的进行滚回,不会留下脏数据;除此之外,事务还能提高批量操作的效率,如在本地SQLite数据库里面,批量插入1万条数据,那么使用事务和没有使用事务,速度上至少差别几十到上百倍的差异。既然事务有完整性和速度性的差异,因此,基于上述原因,我们在很多情况下最好使用事务进行操作。本文主要介绍在开发框架中如何整合事务的操作,并介绍在各个分层中的事务使用案例。

由于我介绍的相关框架,主要是采用了微软的Enterprise Library的数据库访问模块,因此它能够很好抽象各种数据库的事务,以适应各种不同数据库的事务处理。使用微软的Enterprise Library模块,可以很好支持SQLSever、Oracle、Mysql、Access、SQLite等数据库。

1、数据访问层中的事务操作

1.1 数据访问层的事务接口定义

由于使用事务操作,因此在底层的模块里面,也就是这里的数据访问层,一般需要一个事务对象的参数。如下代码是一个数据访问层的接口定义。

    /// <summary>
    /// 数据访问层的接口
    /// </summary>
    public interface IBaseDAL<T> where T : BaseEntity
    {
        /// <summary>
        /// 插入指定对象到数据库中
        /// </summary>
        /// <param name="obj">指定的对象</param>
        /// <param name="trans">事务对象</param>
        /// <returns>执行成功返回True</returns>
        bool Insert(T obj, DbTransaction trans = null);
        
        /// <summary>
        /// 根据指定对象的ID,从数据库中删除指定对象
        /// </summary>
        /// <param name="key">指定对象的ID</param>
        /// <param name="trans">事务对象</param>
        /// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
        bool Delete(object key, DbTransaction trans = null);
        
        /// <summary>
        /// 更新对象属性到数据库中
        /// </summary>
        /// <param name="obj">指定的对象</param>
        /// <param name="primaryKeyValue">主键的值</param>
        /// <param name="trans">事务对象</param>
        /// <returns>执行成功返回<c>true</c>,否则为<c>false</c></returns>
        bool Update(T obj, object primaryKeyValue, DbTransaction trans = null);
        
        /// <summary>
        /// 查询数据库,检查是否存在指定ID的对象
        /// </summary>
        /// <param name="key">对象的ID值</param>
        /// <param name="trans">事务对象</param>
        /// <returns>存在则返回指定的对象,否则返回Null</returns>
        T FindByID(object key, DbTransaction trans = null);
                
        .....................//其他操作
        
    }

从上面的代码上,我们可以看到,里面的增删改查等操作,最后都带一个trans的事务对象参数,这个参数默认为null,也就是可选参数的做法,这个方法就提供了两个重载的方法供我们使用。

看到这里,可能有些人提出疑问,为什么查找方法也传入事务对象,这个因为事务是一个排斥性操作,一旦启动了事务,可能这个表的其他操作会被锁定,但在这个事务内操作确实可以允许的,如果你使用SQLite这种单机版的数据库,在一个地方采用事务操作一个表,在事务内部接着不使用事务进行表的任何操作将不会被允许,数据库提示出错信息的。

基于这个原因,所有表操作的接口,都应该提供事务性的操作接口,也就是提供一个事务性的对象参数。在接口的实现里面,判断事务对象是否为空,然后进行相应的处理即可。

1.2 在DAL层自定义函数的事务操作

由于在IBaseDAL里面已经定义了很多事务性的接口,因此数据访问层的基类里面也已经实现了很多相关的基础操作。

因此数据访问层DAL层里面,如自己定义的实现函数,调用这些基础函数进行处理就可以了。自定义函数对事务的操作处理,代码如下所示。

        /// <summary>
        /// 调整客户的组别
        /// </summary>
        /// <param name="customerId">客户ID</param>
        /// <param name="groupIdList">客户分组Id集合</param>
        /// <returns></returns>
        public bool ModifyCustomerGroup(string customerId, List<string> groupIdList)
        {
            bool result = false;
            DbTransaction trans = base.CreateTransaction();
            if (trans != null)
            {
                string sql = string.Format("Delete from T_CRM_CustomerGroup_Customer where Customer_ID='{0}' ", customerId);
                base.SqlExecute(sql, trans);

                foreach (string groupId in groupIdList)
                {
                    sql = string.Format("Insert into T_CRM_CustomerGroup_Customer(Customer_ID,CustomerGroup_ID) values('{0}', '{1}') ", customerId, groupId);
                    base.SqlExecute(sql, trans);
                }

                try
                {
                    trans.Commit();
                    result = true;
                }
                catch
                {
                    trans.Rollback();
                    throw;
                }
            }
            return result;
        }

2、业务逻辑层的事务操作

业务逻辑层BLL层是对数据访问层的更高一层的封装,它也相应提供相应的事务对象接口,以方便外部的调用。

它的业务类里面,自定义函数对事务的调用操作如下所示。

        /// <summary>
        /// 把报价单转换为销售订单
        /// </summary>
        /// <param name="quotationNo">报价单编号</param>
        /// <returns></returns>
        public bool ConvertToOrder(string orderNo, int userId)
        {
            bool result = false;

            DbTransaction trans = baseDal.CreateTransaction();
            if (trans != null)
            {
                SellInfo sellInfo = ConvertSellInfo(orderNo, userId, trans);
                List<OrderDetailInfo> detailList = new List<OrderDetailInfo>();
                if (sellInfo != null)
                {
                    detailList = ConvertOrderDetal(sellInfo, orderNo, trans);
                }

                bool success = BLLFactory<Sell>.Instance.Insert(sellInfo, trans);
                if (success)
                {
                    foreach (OrderDetailInfo info in detailList)
                    {
                        BLLFactory<OrderDetail>.Instance.Insert(info, trans);
                    }                    
                }

                try
                {
                    trans.Commit();
                    result = true;
                }
                catch
                {
                    trans.Rollback();
                    throw;//重新抛出异常
                }
            }
            return result;
        }

另一例子如下所示。

        /// <summary>
        /// 删除报价单及明细信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool DeleteQuotationRelated(string id)
        {
            bool result = false;
            DbTransaction trans = CreateTransaction();
            if (trans != null)
            {
                QuotationInfo info = baseDal.FindByID(id, trans);
                if (info != null)
                {
                    List<QuotationDetailInfo> detailList = BLLFactory<QuotationDetail>.Instance.FindByOrderNo(info.HandNo, trans);
                    foreach (QuotationDetailInfo detailInfo in detailList)
                    {
                        BLLFactory<QuotationDetail>.Instance.Delete(detailInfo.ID, trans);
                    }

                    //最后删除主表订单数据
                    baseDal.Delete(id, trans);

                    try
                    {
                        trans.Commit();
                        result = true;
                    }
                    catch (Exception ex)
                    {
                        trans.Rollback();
                        LogTextHelper.Error(ex);
                        throw;
                    }
                }
            }
            return result;
        }
    }

3、Winform界面层对事务的调用 

由于Winform界面层,直接调用BLL层的相应接口,进行数据的操作的,因此我们也可以在界面层创建相应的事务对象,然后在界面层操作事务。

界面层调用事务处理,操作代码如下所示。

            using (DbTransaction trans = BLLFactory<Function>.Instance.CreateTransaction())
            {
                try
                {
                    if (trans != null)
                    {
                        bool sucess = BLLFactory<Function>.Instance.Insert(mainInfo, trans);
                        if (sucess)
                        {
                            FunctionInfo subInfo = null;
                            int sortCodeIndex = 1;

                            #region 子功能操作
                            if (chkAdd.Checked)
                            {
                                subInfo = CreateSubFunction(mainInfo);
                                subInfo.SortCode = (sortCodeIndex++).ToString("D2");
                                subInfo.ControlID = string.Format("{0}/Add", mainInfo.ControlID);
                                subInfo.Name = string.Format("添加{0}", mainInfo.Name);

                                BLLFactory<Function>.Instance.Insert(subInfo, trans);
                            }
                            if (chkDelete.Checked)
                            {
                                subInfo = CreateSubFunction(mainInfo);
                                subInfo.SortCode = (sortCodeIndex++).ToString("D2");
                                subInfo.ControlID = string.Format("{0}/Delete", mainInfo.ControlID);
                                subInfo.Name = string.Format("删除{0}", mainInfo.Name);
                                BLLFactory<Function>.Instance.Insert(subInfo, trans);
                            }
                            ......................//其他事务操作
#endregion trans.Commit(); ProcessDataSaved(this.btnSave, new EventArgs()); //this.DialogResult = System.Windows.Forms.DialogResult.OK; MessageDxUtil.ShowTips("保存成功"); } else { MessageDxUtil.ShowTips("保存失败"); } } } catch (Exception ex) { if (trans != null) { trans.Rollback(); } LogTextHelper.Error(ex); MessageDxUtil.ShowError(ex.Message); } }

以上就是我对不同分层中使用事务对象进行各种操作的处理,由于事务对象会对表进行某种锁定操作,因此数据库的整体性能可能有所降低,但是在保证某种组合操作的原子性,以及批量数据库操作这两种方式,该出手时还是要出手,这样能更好提高完整性和处理的高效性。

本文转自博客园伍华聪的博客,原文链接:微信小程序实现博客园文章阅读功能,如需转载请自行联系原博主。



 

目录
相关文章
|
3月前
|
Java 关系型数据库 MySQL
(二十七)舞动手指速写一个Seata-XA框架解决棘手的分布式事务问题
相信大家对于事务问题都不陌生,在之前《MySQL事务篇》中曾详解过MySQL的事务机制,在传统的单库环境下开发,咱们可依赖于MySQL所提供的事务机制,来确保单个事务内的一组操作,要么全部执行成功,要么全部执行失败。
|
6月前
|
数据库 对象存储
状态机的原理简析及重要用途
状态机的原理简析及重要用途
77 1
|
Java 测试技术 数据库连接
MyBatis基本用法 && 什么是自动化测试 && Spring事务和事务传播机制 && 性能测试概念和术语 && Loadrunner安装
MyBatis基本用法 && 什么是自动化测试 && Spring事务和事务传播机制 && 性能测试概念和术语 && Loadrunner安装
83 0
|
设计模式 存储 开发框架
C++ 插件机制的实现原理、过程、及使用
C++ 插件机制的实现原理、过程、及使用
|
存储 SQL NoSQL
事务功能使用及原理介绍(二)|学习笔记
快速学习事务功能使用及原理介绍
195 0
事务功能使用及原理介绍(二)|学习笔记
|
SQL 存储 NoSQL
事务功能使用及原理介绍(一)|学习笔记
快速学习事务功能使用及原理介绍
310 0
事务功能使用及原理介绍(一)|学习笔记
|
存储 SQL 关系型数据库
第九章《事务》
第九章《事务》
第九章《事务》
|
Java Maven SDN
带你读《ODL技术内幕:架构设计与实现原理》之一:阅读源代码前的准备
ODL不仅仅是一个SDN控制器平台,它还是一个优秀的模型驱动架构实现,以及一个典型的分布式系统设计范例。通过ODL,我们能学习的不仅仅是SDN,也能学到其通用的编程技术及软件架构设计,其分布式系统设计实现也非常值得我们借鉴。
|
Java 数据格式 XML
带你读《ODL技术内幕:架构设计与实现原理》之三:ODL基本对象的设计与实现
ODL不仅仅是一个SDN控制器平台,它还是一个优秀的模型驱动架构实现,以及一个典型的分布式系统设计范例。通过ODL,我们能学习的不仅仅是SDN,也能学到其通用的编程技术及软件架构设计,其分布式系统设计实现也非常值得我们借鉴。
14052 0