程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Winform開發框架之通用數據導入導出操作的事務性操作完善

Winform開發框架之通用數據導入導出操作的事務性操作完善

編輯:關於.NET

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[0].Rows.Count > 0)
                {
                    DataTable dt = myDs.Tables[0];
                    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(), 0);
    
            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);
        }
......................

基於事務性的調整,優化了整個基類接口和實現類的類庫,以方便在框架中更好整合事務性操作的支 持。

伍華聰  http://www.iqidi.com

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved