在很多情况下,事务是个很有用的东西,可以把一系列的操作组合成一个原子粒度的操作,一旦组合中某个地方出错,可以整个干净的进行滚回,不会留下脏数据;除此之外,事务还能提高批量操作的效率,如在本地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 = ; #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);
}
}

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

Winform开发框架里面使用事务操作的原理及介绍的更多相关文章

  1. Winform开发框架之附件管理应用

    在很多常规的管理系统里面,都可能有附件管理的需求,在我做的一系列医疗行业的病人信息管理系统里面,都希望能很好的管理病人的相关资料,分门别类的进行存储,需要的时候可以预览查看,那么这个功能我们可以把它独 ...

  2. Winform开发框架之通用数据导入导出操作的事务性操作完善

    1.通用数据导入导出操作模块回顾 在我的Winfrom开发框架里面,有一个通用的导入模块,它在默默处理这把规范的Excel数据导入到不同的对象表里面,一直用它来快速完成数据导入的工作.很早在随笔< ...

  3. Winform开发框架之权限管理系统改进的经验总结(4)-一行代码实现表操作日志记录

    在前面介绍了几篇关于我的权限系统改进的一些经验总结,本篇继续这一系列主体,介绍如何一行代码实现重要表的操作日志记录.我们知道,在很多业务系统里面,数据是很敏感的,特别对于一些增加.修改.删除等关键的操 ...

  4. Winform开发框架中工作流模块之审批会签操作(2)

    前面随笔介绍了请假申请单和报销申请单两个不同的业务表单的流程处理,一个是单表信息,一个包含明细的主从表信息,后者包含了条件流程的处理,在流程审批中,一般还有一种流程处理就是会签的操作,会签处理是几个审 ...

  5. Winform开发框架中工作流模块之审批会签操作

    在前面介绍了框架中工作流的几个开发过程,本篇随笔重点介绍一下日常审批环节中的具体处理过程,从开始创建表单,以及各个审批.会签过程的流转过程,希望大家对其中流程的处理有一个大概的印象. 1.请假申请表单 ...

  6. Winform开发框架之存储过程的支持--存储过程的实现和演化提炼(2)

    本篇继续上篇<Winform开发框架之存储过程的支持--存储过程的实现和演化提炼(1)>来对Winform开发框架之存储过程的支持进行介绍,上篇主要介绍了SQLServer和Oracle两 ...

  7. 使用事务操作SQLite数据批量插入,提高数据批量写入速度,源码讲解

    SQLite数据库作为一般单机版软件的数据库,是非常优秀的,我目前单机版的软件产品线基本上全部替换Access作为优选的数据库了,在开发过程中,有时候需要批量写入数据的情况,发现传统的插入数据模式非常 ...

  8. 在Winform开发框架中,利用DevExpress控件实现数据的快速录入和选择

    在实际的项目开发过程中,有好的控件或者功能模块,我都是想办法尽可能集成到我的WInform开发框架中,这样后面开发项目起来,就可以节省很多研究时间,并能重复使用,非常高效方便.在我很早之前的一篇博客& ...

  9. Winform开发框架之简易工作流设计(转自 伍华聪博客)

    Winform开发框架之简易工作流设计 一讲到工作流,很多人第一反应就是这个东西很深奥,有时候又觉得离我们较为遥远,确实完善的工作流设计很多方面,而正是由于需要兼顾很多方面,一般通用的工作流都难做到尽 ...

随机推荐

  1. Guava-Optional可空类型

    接上篇Guava之Joiner和Splitter,本篇将介绍Guava的另外一个有用的对象Optional,这在Java中Google Guava首先给我们提出可空对象模型的.在其他语言如c#这是已经 ...

  2. C#可扩展编程之MEF学习笔记(三):导出类的方法和属性

    前面说完了导入和导出的几种方法,如果大家细心的话会注意到前面我们导出的都是类,那么方法和属性能不能导出呢???答案是肯定的,下面就来说下MEF是如何导出方法和属性的. 还是前面的代码,第二篇中已经提供 ...

  3. 换个角度理解云计算之MapReduce

    上一篇简单讲了一下HDFS,简单来说就是一个叫做“NameNode”的大哥,带着一群叫做“DataNode”的小弟,完成了一坨坨数据的存储,其中大哥负责保存数据的目录,小弟们负责数据的真正存储,而大哥 ...

  4. 消息队列-rabbitMQ

    消息队列两个用处:服务间解耦,缓解压力(削峰平谷),以前用过ZMQ.狼厂内部的NMQ,现在接触了java开源的kafka和RabbitMQ.目前先不求甚解,有个大概的认识. RabbitMQ的安装和入 ...

  5. java系统性能分析

    netstat -ano | findstr 31900 注意最后是pid 堆栈的作用: 线程死锁分析 辅助CPU过高分析 线程资源不足分析 性能瓶颈分析 关键线程异常退出 Windows:在运行ja ...

  6. 我心中的核心组件(可插拔的AOP)~分布式文件上传组件~基于FastDFS

    回到目录 一些概念 在大叔框架里总觉得缺点什么,在最近的项目开发中,终于知道缺什么了,分布式文件存储组件,就是缺它,呵呵,对于分布式文件存储来说,业界比较公认的是FastDFS组件,它自己本身就是集群 ...

  7. 说说设计模式~适配器模式(Adapter)

    返回目录 之前和大家一起谈了工厂模式和单例模式,今天来看一下另一种非常常用的模式,它就是适配器模式,第一次看到这个模式是通过“张逸”老师的“设计之道”这篇文章,在这里表adapter讲的很透彻,今天把 ...

  8. NodeJS系列~第三个小例子,NodeJs与Redis实现高并发的队列存储

    返回目录 众所周知 redis量个强大的缓存组件,可以部署在win32和linux环境之上,它有五大存储结构,其中有一种为列表list,它可以实现quene和stack的功能,即队列和堆栈的功能. r ...

  9. atitit.js浏览器环境下的全局异常捕获

    atitit.js浏览器环境下的全局异常捕获 window.onerror = function(errorMessage, scriptURI, lineNumber) { var s= JSON. ...

  10. VS2012 MVC4 学习笔记-概览

    1. 访问请求过程 访问收到后路由(Router)根据路径由分配给对应的控制器(Control),然后由控制器返回页面视图(View) 路由设置一个默认的控制器,类似 主页的样子吧 <未完待续& ...