1、通用数据导入导出操作模块回顾

在我的Winfrom开发框架里面,有一个通用的导入模块,它在默默处理这把规范的Excel数据导入到不同的对象表里面,一直用它来快速完成数据导入的工作。很早在随笔《Winform开发框架之通用数据导入导出操作》里面就很全面的介绍过它的相关功能了,在代码生成工具Database2Sharp里面,生成的Winfrom界面代码也已经把它的调用代码放进去了,因此使用起来真是很好,很开心。

在不断的项目实践中,发现使用基于Sqlite的客户端作为单机版的操作也越来越多,因此大批量的数据导入,也是经常碰到的事情,我们知道,SqlServer批量插入数据会很快,即使你没有使用事务,一条条的插入,大批量也会比较快,这个可能得益于SqlServer本身的事务优化效果。但是作为单机版的数据库,Sqlite每次操作都是单独一个事务的,插入一条数据效率可能不明显,如果操作一千条,一万条,数据的缓慢就很明显,甚至不可忍耐了。我曾经在《使用事务操作SQLite数据批量插入,提高数据批量写入速度,源码讲解》里面提到了批量插入通用字典模块的字典数据,使用事务前后批量插入数据,那个速度可是差别很大。

基于以上的因素考虑,决定对通用的数据导入模块进行事务性的优化,以便适应我频繁使用Sqlite数据库大批量导入数据的情况,提高客户的良好体验。本篇主要基于事务性操作的完善,实现基于Sqlite数据的批量快速导入操作。

2、事务性代理事件的定义

由于是通用的模块,所以我们不知道具体的数据库事务对象,但是我们能够通过定义一些事件,给调用者进行事务对象的传递,这样才能在基类中使用事务对象,首先我们定义两个委托事件,一个是SaveDataHandler,用来进行单条数据的处理委托,一个是CreateTransactionHandler,让调用者创建并传递事务对象的委托,具体代码如下所示。

    public partial class FrmImportExcelData : BaseForm
{
...............................
private DbTransaction transaction = null; /// <summary>
/// 使用事务对数据进行保存的委托,加快速度
/// </summary>
/// <param name="dr">数据行</param>
/// <param name="trans">事务对象</param>
/// <returns></returns>
public delegate bool SaveDataHandler(DataRow dr, DbTransaction trans); /// <summary>
/// 创建事务对象的委托,在导入操作初始化的时候赋值
/// </summary>
/// <returns></returns>
public delegate DbTransaction CreateTransactionHandler();

定义好委托后,我们需要创建对应委托的事件对象,作为通用模块的事件,如下所示。

        /// <summary>
/// 保存数据事件
/// </summary>
public event SaveDataHandler OnDataSave; /// <summary>
/// 刷新数据事件
/// </summary>
public event EventHandler OnRefreshData; /// <summary>
/// 让调用者创建事务并传递给通用模块
/// </summary>
public event CreateTransactionHandler OnCreateTransaction;

在实现数据导入前,我们需要使用事件来获取对应的事务对象,以便开始事务,具体代码如下所示。

            if (MessageDxUtil.ShowYesNoAndWarning("该操作将把数据导入到系统数据库中,您确定是否继续?") == DialogResult.Yes)
{
if (myDs != null && myDs.Tables[].Rows.Count > )
{
DataTable dt = myDs.Tables[];
this.progressBar1.Visible = true;
if (!worker.IsBusy)
{
if (OnCreateTransaction != null)
{
transaction = OnCreateTransaction();
}
worker.RunWorkerAsync();
}
}
}

3、事务处理逻辑及调用者使用逻辑

这样,我们在通用模块里面,获取到Excel数据后,需要遍历每行数据,然后通过事务对象实现数据提交,部分代码如下所示。

                    #region 批量保存数据,然后事务提交
foreach (DataRow dr in dt.Rows)
{
if (OnDataSave != null)
{
try
{
bool success = OnDataSave(dr, transaction);
if (success)
{
itemCount++;
}
}
catch (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
} int currentStep = Convert.ToInt32(step * i);
worker.ReportProgress(currentStep);
i++;
}
#endregion if (transaction != null)
{
transaction.Commit();
}

我们看到,在通用的导入模块里面,我们只看到传递事务对象给OnDataSave(dr, transaction)事件,并最终提交整个事务处理而已,具体的

从以上的代码看到,我们把创建事务对象的方法留给调用者实现OnCreateTransaction事件接口,保存每行数据,也留给调用者实现数据的保存OnDataSave事件。

具体的模块调用代码如下所示。

        private string moduleName = "药品目录";
private void btnImport_Click(object sender, EventArgs e)
{
string templateFile = string.Format("{0}-模板.xls", moduleName);
FrmImportExcelData dlg = new FrmImportExcelData();
dlg.SetTemplate(templateFile, System.IO.Path.Combine(Application.StartupPath, templateFile));
dlg.OnDataSave += new FrmImportExcelData.SaveDataHandler(ExcelData_OnDataSave);
dlg.OnCreateTransaction += new FrmImportExcelData.CreateTransactionHandler(dlg_OnCreateTransaction);
dlg.OnRefreshData += new EventHandler(ExcelData_OnRefreshData);
dlg.ShowDialog();
} DbTransaction dlg_OnCreateTransaction()
{
return BLLFactory<DrugDetail>.Instance.CreateTransaction();
} void ExcelData_OnRefreshData(object sender, EventArgs e)
{
BindData();
} bool ExcelData_OnDataSave(DataRow dr, DbTransaction trans)
{
string drugNo = dr["药品编码"].ToString();
string drugName = dr["药品名称"].ToString();
if (string.IsNullOrEmpty(drugNo) && string.IsNullOrEmpty(drugName))
return false; bool success = false;
DrugDetailInfo info = new DrugDetailInfo();
info.DrugNo = drugNo;
info.DrugName = drugName;
info.Manufacture = dr["制造商"].ToString();
info.Formulations = dr["剂型"].ToString();
info.Specification = dr["规格"].ToString();
info.Unit = dr["药品单位"].ToString();
info.Note = dr["备注信息"].ToString();
info.StockQuantity = ConvertHelper.ToInt32(dr["库存量"].ToString(), ); info.EditTime = DateTime.Now;
info.Editor = Portal.gc.LoginInfo.Name;
info.Dept_ID = Portal.gc.LoginInfo.Dept_ID;
success = BLLFactory<DrugDetail>.Instance.Insert(info, trans);
return success;
}

写到这里,可能很多时候大家觉得随笔应该画上句号了吧,其实不然,还有很重要一个地方,需要提及一下,就是我们使用了事务保存数据,那么如果需要在单条记录保存的时候,需要判断检索数据,才决定插入还是更新操作呢?

如果你觉得随便写一个select语句调用不就可以了吗?那样可能就会有问题了,事务性操作会锁定当前的表,不会让你继续写入了,很快就会得到操作超时的错误异常了。

那么我们应该如何解决这种需求呢?就是你要使用事务的数据库连接对象,来实现数据的检索就可以了,如下面的代码就是OK的了。

        bool dlg_OnDataSave(DataRow dr, DbTransaction trans)
{
string PlaneModel = dr["装备型号"].ToString();
if (string.IsNullOrEmpty(PlaneModel)) return false; bool success = false;
PlaneModelInfo info = BLLFactory<PlaneModel>.Instance.FindSingle(string.Format("PlaneModel='{0}'", PlaneModel), trans);
if (info != null)
{
info.PlaneModel = PlaneModel;
info.PlaneNote = dr["保障特点"].ToString();
info.Demand = dr["保障要求"].ToString();
info.Note = dr["备注"].ToString(); info.Dept_ID = Portal.gc.LoginInfo.Dept_ID;
success = BLLFactory<PlaneModel>.Instance.Update(info, info.ID, trans);
}
else
{
info = new PlaneModelInfo();
info.PlaneModel = PlaneModel;
info.PlaneNote = dr["保障特点"].ToString();
info.Demand = dr["保障要求"].ToString();
info.Note = dr["备注"].ToString(); info.Dept_ID = Portal.gc.LoginInfo.Dept_ID;
success = BLLFactory<PlaneModel>.Instance.Insert(info, trans);
}
return success;
}

4、Winform开发框架的事务接口支持

基于此,我们很多查找的接口可能都会在事务中调用,需要重新构造我的框架基类接口了,把事务作为默认的对象参数,默认为NULL,调整我的基类,为所有的事务内操作提供支持,如数据访问接口层部分接口定义如下所示。

    /// <summary>
/// 数据访问层的接口
/// </summary>
public interface IBaseDAL<T> where T : BaseEntity
{
#region 通用操作 /// <summary>
/// 获取表的所有记录数量
/// </summary>
/// <param name="trans">事务对象</param>
/// <returns></returns>
int GetRecordCount(DbTransaction trans = null); /// <summary>
/// 获取表的指定条件记录数量
/// </summary>
/// <param name="condition">条件语句</param>
/// <param name="trans">事务对象</param>
/// <returns></returns>
int GetRecordCount(string condition, DbTransaction trans = null); /// <summary>
/// 根据condition条件,判断是否存在记录
/// </summary>
/// <param name="condition">查询的条件</param>
/// <param name="trans">事务对象</param>
/// <returns>如果存在返回True,否则False</returns>
bool IsExistRecord(string condition, DbTransaction trans = null); /// <summary>
/// 查询数据库,检查是否存在指定键值的对象
/// </summary>
/// <param name="recordTable">Hashtable:键[key]为字段名;值[value]为字段对应的值</param>
/// <param name="trans">事务对象</param>
/// <returns>存在则返回<c>true</c>,否则为<c>false</c>。</returns>
bool IsExistKey(Hashtable recordTable, DbTransaction trans = null); ...................................

BaseBLL业务基类的部分接口实现如下所示

    /// <summary>
/// 业务基类对象
/// </summary>
/// <typeparam name="T">业务对象类型</typeparam>
public class BaseBLL<T> where T : BaseEntity, new()
{
............................ #region 对象添加、修改、查询接口 /// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <param name="trans">事务对象</param>
/// <returns>执行操作是否成功。</returns>
public virtual bool Insert(T obj, DbTransaction trans = null)
{
CheckDAL();
return baseDal.Insert(obj, trans);
} /// <summary>
/// 插入指定对象到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <param name="trans">事务对象</param>
/// <returns>执行成功返回新增记录的自增长ID。</returns>
public virtual int Insert2(T obj, DbTransaction trans = null)
{
return baseDal.Insert2(obj, trans);
} /// <summary>
/// 更新对象属性到数据库中
/// </summary>
/// <param name="obj">指定的对象</param>
/// <param name="primaryKeyValue">主键的值</param>
/// <param name="trans">事务对象</param>
/// <returns>执行成功返回<c>true</c>,否则为<c>false</c>。</returns>
public virtual bool Update(T obj, object primaryKeyValue, DbTransaction trans = null)
{
CheckDAL();
return baseDal.Update(obj, primaryKeyValue, trans);
}
......................

基于事务性的调整,优化了整个基类接口和实现类的类库,以方便在框架中更好整合事务性操作的支持。

Winform开发框架之通用数据导入导出操作的事务性操作完善的更多相关文章

  1. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  2. Hive常用操作之数据导入导出

    一.Hive数据导入导出 1.hive数据导出 很多时候,我们在hive中执行select语句,希望将最终的结果保存到本地文件或者保存到hdfs系统中或者保存到一个新的表中,hive提供了方便的关键词 ...

  3. Oracle 数据导入导出操作 (转)

    Oracle数据导入导出imp/exp 功能:Oracle数据导入导出imp/exp就相当与oracle数据还原与备份. 大多情况都可以用Oracle数据导入导出完成数据的备份和还原(不会造成数据的丢 ...

  4. Winform开发框架之通用Windows摄像头调用拍照--SNF快速开发平台3.3-Spring.Net.Framework

    今天做了一个windows系统下调用摄像头.进行开启.关闭.拍照.设置等等功能演示. 进行源码贡献,欢迎大家下载使用 一.DEMO效果如下: 二.DEMO演示代码如下: using SNF.Utili ...

  5. Winform开发框架之通用自动更新模块(转)

    在网络化的环境中,特别是基于互联网发布的Winform程序,程序的自动更新功能是比较重要的操作,这样可以避免挨个给使用者打电话.发信息通知或者发送软件等,要求其对应用程序进行升级.实现程序的自动更新, ...

  6. Winform开发框架之通用附件管理模块 --SNF快速开发平台3.3-Spring.Net.Framework

    最近项目太多都没有时间写文章了,实际项目需求一,CS端和windows平板都需要附件上传管理功能.以前做的都是BS的附件管理和上传功能.本来计划在Winform上嵌套一个浏览器直接用bs的附件上传功能 ...

  7. Winform开发框架之通用高级查询模块--SNF快速开发平台3.3-Spring.Net.Framework

    最近项目确实忙,但也是一直忙于有关项目和框架技术的事情,也一直致力于改善我的WInform开发框架.使得自己及客户使用起来更加方便,更加友好,更加高效. 在很多程序模块中都很常见,也是给客户扩展查询的 ...

  8. ITTC数据挖掘平台介绍(五) 数据导入导出向导和报告生成

    一. 前言 经过了一个多月的努力,软件系统又添加了不少新功能.这些功能包括非常实用的数据导入导出,对触摸进行优化的画布和画笔工具,以及对一些智能分析的报告生成模块等.进一步加强了平台系统级的功能. 马 ...

  9. 从零自学Hadoop(16):Hive数据导入导出,集群数据迁移上

    阅读目录 序 导入文件到Hive 将其他表的查询结果导入表 动态分区插入 将SQL语句的值插入到表中 模拟数据文件下载 系列索引 本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并 ...

随机推荐

  1. android studio logcat 换行(日志换行)

    起因 今天突然要调试网络数据,调试一大截那个xml数据. 解决思路 一开始去setting哪里看一下logcat 是否有line break,类似的字眼,可惜没有. 我猜如果没有在设置的话,估计就在“ ...

  2. [转] Visual Studio Code behind a proxy

    http://www.tuicool.com/articles/jyyIBf3 http://blog.majcica.com/2016/04/07/visual-studio-code-behind ...

  3. Android 5.1 AOSP 源码获取

    本文已同步更新至:http://dxjia.cn/2015/08/android-aosp-code-sync/ Android 5.1源码开放有一个多月啦,但由于城墙的关系,每次想着更新最新源码学习 ...

  4. 使用sphinx生成Python文档

    发现找不到matplotlib.sphinxext.mathmpl: 可以直接easy_install matplotlib,也可以去这里下载安装包 发现exception: matplotlib r ...

  5. iOS 实现快速切换主题详细教程(附上源码)

    前言 iOS 实现主题切换,相信在未来的app里也是会频繁出现的,尽管现在只是出现在主流的APP,如(QQ.新浪微博.酷狗音乐.网易云音乐等),但是现在是看颜值.追求个性的年代,所以根据用户喜好自定义 ...

  6. Android-NDK编译:cocos2d-x(三) eclipse 导入工程

    NDK 编译后,用eclipse导入cocos2d-x工程 菜单[File]-->[New]-->[Project] ,弹出New Project 对话框 窗口下方 选 [Android] ...

  7. JS/React 判断对象是否为空对象

    JS一般判断对象是否为空,我们可以采用: if(!x)的方式直接判断,但是如果是一个空对象,比如空的JSON对象,是这样的:{},简单的判断是不成功的,因为它已经占用着内存了,如果是JQuery的话, ...

  8. 【资源下载】Ext4.1.0_Doc中文版_V1.0.0_Beta正式提供下载!

    *************************************************重要提示: 在2014年1月1日前一天,历时两年左右的时间,翻译小组终于完成了该API的翻译.可喜可贺 ...

  9. QPaintDevice: Cannot destroy paint device that is being painted

    在paintEvent中,使用QPainter * 绘制图像出现此问题.解决: 1.改为不使用QPainter指针. 2.添上begin(), end() QPainter * painter = n ...

  10. win7下IE主页无法修改,IE设置无法保存解决方案

    转自:http://www.myhack58.com/Article/48/65/2012/34411.htm 经测,有效! 现象如下: 1.开启后,首先总是指向http://go.microsoft ...