現在有這樣一個需求:Excel中存放的是上萬條甚至更多商品數據,包括數量、單價,分類等等很多屬性,以樹形展示,多級分類。我需要將Excel數據導入到TreeList中,不着急提交到後臺,我還要改一些商品價格和數量,對應的分類要顯示下面所有商品總金額,處理完要改的數據再提交後臺。
思路:1、先將Excel轉DataTable(兩種方式,推薦第二種)
2、將DataTable和現有列表數據合併作爲新的數據源綁定到TreeList(或者與源列表數據進行對比,已存在的商品更新信息,不存在的添加進來。),這裏導入數據到界面不要對父節點做任何處理,只更新商品信息。
3、重新計算父節點分類的金額(遞歸),導入數據,界面展示就算完成
4、提交後臺採用 創建數據庫臨時表(#temp),先將所有數據存放在臨時表,通過update select | insert select方式保存到數據庫。
代碼實現:
/// <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();
}