C# winform實現導入大量Excel數據到TreeList界面中且批量提交後臺

       現在有這樣一個需求:Excel中存放的是上萬條甚至更多商品數據,包括數量、單價,分類等等很多屬性,以樹形展示,多級分類。我需要將Excel數據導入到TreeList中,不着急提交到後臺,我還要改一些商品價格和數量,對應的分類要顯示下面所有商品總金額,處理完要改的數據再提交後臺。

思路:1、先將Excel轉DataTable(兩種方式,推薦第二種)

           2、將DataTable和現有列表數據合併作爲新的數據源綁定到TreeList(或者與源列表數據進行對比,已存在的商品更新信息,不存在的添加進來。),這裏導入數據到界面不要對父節點做任何處理,只更新商品信息。

           3、重新計算父節點分類的金額(遞歸),導入數據,界面展示就算完成

           4、提交後臺採用 創建數據庫臨時表(#temp),先將所有數據存放在臨時表,通過update select | insert select方式保存到數據庫。

代碼實現:

DataTable 轉 List 參考

        /// <summary>
        /// 導入操作  T表示商品信息類,ToDataTable Excel轉DataSet方法
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btImport_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
        {
            openFileDialog1.Filter = "Excel Files|*.xls;*.xlsx;*.xlsm";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                List<TreeListNode> lstAllNode = treelist.GetNodeList();
                List<T> sourceList= treelist.DataSource as List<T>;
                DataTable ImportTable = ToDataTable(openFileDialog1.FileName).Tables[0];
                //驗證文檔格式是否正確
                if (ImportTable == null || ImportTable.Rows.Count == 0) 
                    throw new Exception("無導入數據"); 
                bool validityFormat = true;
                if (!ImportTable.Columns.Contains("編號")) 
                    validityFormat = false; 
                if (!ImportTable.Columns.Contains("數量")) 
                    validityFormat = false; 
                if (!validityFormat) 
                    throw new Exception("文檔格式不正確,請重新導入"); 
                List<T> dtToList = ConvertToModel(ImportTable);
                dtToList.Foreach(m=>{
                    if(sourceList.Exists(x=>x.Code=m.Code))
                        {
                            //更新商品信息
                        }
                    else
                        {
                            sourceList.Add(m);
                        }
                }); 
                treelist.RefreshDataSource(); 
                //遍歷所有子節點,遞歸計算父節點。
                foreach (TreeListNode node in treelist.GetNodeList().FindAll(x => !x.HasChildren))
                {
                     //調用遞歸方法(子節點的父節點只計算一次,如果父節點下有1W個子節點,按照這種遍歷子節點方式會重複計算這個父節點1W次,顯然效率會很低,認定這1W個子節點有相同的父節點,至計算一次即可)
                     CalculationNodeValue();   
                } 
                treelist.RefreshDataSource();
                XtraMessageBox.Show("數據成功導入到程序中,保存後生效!");
            }
        }
        /// <summary>
        /// 導入的數據在遞歸計算父節點數據是用於存儲已計算過的父節點
        /// </summary>
        List<TreeListNode> ExistParentNode = new List<TreeListNode>();
        private void CalculationNodeValue(TreeListNode node)
        { 
            if (node.ParentNode != null && !ExistParentNode.Exists(x=>(tlDetail.GetDataRecordByNode(x) as T).ItemCode == (tlDetail.GetDataRecordByNode(node.ParentNode) as T).Code))
            {
                if(!node.HasChildren)
                    ExistParentNode.Add(node.ParentNode); 
                T parententity = tlDetail.GetDataRecordByNode(node.ParentNode) as T;
                T entity = tlDetail.GetDataRecordByNode(node) as T;
                if (entity.Amount.HasValue)
                {
                    decimal totalthisamount = 0; 
                    foreach (TreeListNode childnode in node.ParentNode.Nodes)
                    {
                        if ((tlDetail.GetDataRecordByNode(childnode) as T).Amount.HasValue)
                            totalthisamount = totalthisamount + (tlDetail.GetDataRecordByNode(childnode) as T).Amount.Value; 
                    }
                    parententity.Amount = totalthisamount; 
                }
                CalculationNodeValue(node.ParentNode);
            }

        }

本文展示皆爲關鍵代碼,看懂邏輯。直接copy是沒法運行的。

創建臨時表批量更新數據

        private void BulkUpdate(List<T> lstDetails, string operationBy, SqlTransaction tran)
        {
            if (lstDetails == null || lstDetails.Count == 0)
                return; 
            List<string> lstColumnNames = new List<string>()  //數據表列名
            {
                "Code",
                "Price",
                "Qty", 
                "Amount" 
            };
            //創建數據表
            DataTable dtStakes = ConvertToDataTable<T>(lstDetails);
            //去除多餘列
            for (int index = dtStakes.Columns.Count - 1; index >= 0; index--)
            {
                if (!lstColumnNames.Exists(m => m == dtStakes.Columns[index].ColumnName))
                {
                    dtStakes.Columns.RemoveAt(index);
                }
            }

            //創建SqlCommand對象
            SqlCommand cmd = tran.Connection.CreateCommand();
            cmd.Transaction = tran;
            cmd.CommandType = CommandType.Text;

            //獲取Create臨時表的SQL
            string sql = GetCreateTableSql(dtStakes, "#tmp", "被複制的表");
            cmd.CommandText = sql;
            cmd.ExecuteNonQuery();

            //

            //批量將數據插入臨時表
            SqlBulkCopy bulkCopy = new SqlBulkCopy(tran.Connection, SqlBulkCopyOptions.Default, tran);
            using (bulkCopy)
            {
                bulkCopy.BatchSize = dtStakes.Rows.Count;
                #region 數據表和數據庫中臨時表的字段映射
                bulkCopy.ColumnMappings.Add("Code", "Code");
                bulkCopy.ColumnMappings.Add("Price", "Price"); 
                bulkCopy.ColumnMappings.Add("Qty", "Qty"); 
                bulkCopy.ColumnMappings.Add("Amount", "Amount"); 
                 
                #endregion

                bulkCopy.DestinationTableName = "#tmp";
                bulkCopy.WriteToServer(dtStakes);

            }

            //批量更新數據
            cmd.CommandText = String.Format(@"
            USE 庫
            UPDATE Table A
			   SET A.Price= b.Price
				  ,A.Qty= b.Qty
				  ,A.Amount= b.Amount 
				  FROM Table A
				  INNER JOIN #tmp b on a.Code=b.Code " );
            cmd.CommandTimeout = 660;
            cmd.ExecuteNonQuery();
        }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章