轉帖|使用教程|編輯:龔雪|2021-06-11 10:24:30.613|閱讀 275 次
概述:本文探討在客戶關系管理系統中,對于單個Excel表格中,集合了客戶基礎數據及相關數據的導入和導出操作的處理。DevExpress v21.1新版已發布,歡迎下載最新版體驗新功能~
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關鏈接:
DevExpress擁有.NET開發需要的所有平臺控件,包含600多個UI控件、報表平臺、DevExpress Dashboard eXpressApp 框架、適用于 Visual Studio的CodeRush等一系列輔助工具。屢獲大獎的軟件開發平臺DevExpress 近期正式發布了v21.1,最新版擁有眾多新產品和數十個具有高影響力的功能,可為桌面、Web和移動應用提供直觀的解決方案,全面解決各種使用場景問題。
在很多系統,我們都知道,Excel數據的導入導出操作是必不可少的一個功能,這種功能能夠給使用者和外部進行數據交換,也能批量迅速的錄入數據到系統中;但在一些系統中,為了方便,可能把很多個基礎表或者相關的數據綜合到一個Excel表格文件里面,然后希望通過接口進行導入,這種需求處理就顯得比較復雜一點了。本文探討在我的客戶關系管理系統中,對于單個Excel表格中,集合了客戶基礎數據及相關數據的導入和導出操作的處理。
本隨筆主要介紹如何在系統中,導入單一文件中的數據到系統中,這個文件包含了基礎數據和相關數據的導入和導出操作,一般來說這樣的操作對于導入數據已經足夠簡便了,但是,有時候數據很多的情況下,我們可能需要每次選定文件也是一個麻煩的事情。因此指定目錄進行批量數據的導入操作也是一個好的需求,可以進一步簡化用戶的數據導入操作。
下面我們就來介紹,導入、批量導入和導出的三個重要的操作,如圖所示。
導入的數據,是一個Excel,它要求包含幾個不同表的數據,導入操作一次性完成數據的導入,Excel文件的格式如下所示。
我們知道,要一次性導入幾個表的數據,需要先讀取Excel獲取各個Sheet(工作表)的數據,然后把它轉換為DataTable的數據對象,這樣我們就可以根據它的字段賦值給對應的實體類,然后調用業務邏輯處理將數據寫入數據庫即可。
為了直觀的給使用者查看將要導入的數據,我們把需要導入到數據庫的數據,展現在界面上,供客戶確認,如果沒有問題,就可以進行導入操作。由于我們需要操作多個數據表,因此有效讀取Excel里面的Sheet就是第一步工作。
查看Excel數據的操作代碼如下所示,主要的邏輯就是調用Apose.Cell的封裝類進行處理。
AsposeExcelTools.ExcelFileToDataSet(this.txtFilePath.Text, out myDs, out error);
把Excel文件里面多個Sheet的數據轉換為DataSet,然后每個進行依次的處理,展示代碼如下所示。
private void ViewData()
{
if (this.txtFilePath.Text == "")
{
MessageDxUtil.ShowTips("請選擇指定的Excel文件");
return;
}
try
{
myDs.Tables.Clear();
myDs.Clear();
this.gridCustomer.DataSource = null;
string error = "";
AsposeExcelTools.ExcelFileToDataSet(this.txtFilePath.Text, out myDs, out error);
this.gridCustomer.DataSource = myDs.Tables[0];
this.gridView1.PopulateColumns();
this.gridFollow.DataSource = myDs.Tables[1];
this.gridView2.PopulateColumns();
this.gridContact.DataSource = myDs.Tables[2];
this.gridView3.PopulateColumns();
this.gridSupplier.DataSource = myDs.Tables[3];
this.gridView4.PopulateColumns();
}
catch (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}
由于導入過程中需要耗費一定的時間,因此我們可以通過后臺線程結合進度條的方式提示用戶,界面設計效果如下效果所示。
剛才說到,保存數據,我們把它放到后臺線程BackgroudWorker進行處理即可,處理代碼如下所示。
private void btnSaveData_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
return;
if (this.txtFilePath.Text == "")
{
MessageDxUtil.ShowTips("請選擇指定的Excel文件");
return;
}
if (MessageDxUtil.ShowYesNoAndWarning("該操作將把數據導入到系統數據庫中,您確定是否繼續?") == DialogResult.Yes)
{
if (myDs != null && myDs.Tables[0].Rows.Count > 0)
{
this.progressBar1.Visible = true;
worker.RunWorkerAsync();
}
}
}
后臺線程操作的主要業務邏輯代碼如下所示,就是依次把不同的數據進行解析,并保存即可。
void worker_DoWork(object sender, DoWorkEventArgs e)
{
if (myDs != null && myDs.Tables.Count >= 4 && myDs.Tables[0].Rows.Count > 0)
{
try
{
ImportCustomerDataHelper helper = new ImportCustomerDataHelper();
helper.LoginUserInfo = LoginUserInfo;
//寫入或更新客戶信息
string customerID = helper.UpdateCustomer(myDs.Tables[0]);
if (!string.IsNullOrEmpty(customerID))
{
helper.AddFollow(customerID, myDs.Tables[1], worker);
helper.AddContact(customerID, myDs.Tables[2], worker);
helper.AddSupplier(customerID, myDs.Tables[3], worker);
e.Result = "操作完成";
}
else
{
e.Result = "操作失敗";
}
}
catch (Exception ex)
{
e.Result = ex.Message;
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.ToString());
}
}
else
{
e.Result = "請檢查數據記錄是否存在";
}
}
雖然上面可以一次性導入客戶和其相關數據,但是還是一次性導入一個Excel,如果對于客戶數據比較多的情況下,一次次導入操作也是很繁瑣的事情,因此客戶提出,需要按照目錄把所有相關的Excel數據一次性導入,這種導入有個問題就是我們不能再中途干預導入操作,因此為了數據的安全性,我提供一個界面讓客戶選擇目錄,然后把目錄里面的Excel文件列出來,然后在讓客戶確認是否進一步導入。
上面操作的實現代碼我逐一介紹,首先第一步是需要遞歸列出目錄下面的Excel文件,然后顯示出來供用戶確認導入的清單。
private void btnSelectPath_Click(object sender, EventArgs e)
{
string mydocDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string selectPath = FileDialogHelper.OpenDir(mydocDir);
if (!string.IsNullOrEmpty(selectPath))
{
//清空就記錄
this.lstPath.Items.Clear();
string[] fileArray = Directory.GetFiles(selectPath, "*.xls", SearchOption.AllDirectories);
if (fileArray != null && fileArray.Length > 0)
{
foreach (string file in fileArray)
{
string fileName = Path.GetFileName(file);
this.lstPath.Items.Add(new CListItem(fileName, file));
}
}
}
}
當用戶確認操作的時候,提示客戶確認是否進行,確認后將統一批量導入列表里面的文件,這個地方也是為了方便,使用后臺線程進行數據的導出操作,并在過程中提供進度條的指示。
private void btnConfirm_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
return;
if (this.lstPath.Items.Count > 0)
{
if (MessageDxUtil.ShowYesNoAndTips("您確認導入列表的Excel文件嗎?") == System.Windows.Forms.DialogResult.Yes)
{
List<string> fileList = new List<string>();
foreach (object item in this.lstPath.Items)
{
CListItem fileItem = item as CListItem;
if (fileItem != null)
{
fileList.Add(fileItem.Value);
}
}
this.progressBar1.Visible = true;
worker.RunWorkerAsync(fileList);
}
}
}
這個后臺線程的處理邏輯和單個文件導入的操作差不多,只不過這里需要增加一個文件列表的遍歷處理而已,具體代碼如下所示。
void worker_DoWork(object sender, DoWorkEventArgs e)
{
List<string> fileList = e.Argument as List<string>;
if (fileList == null || fileList.Count == 0) return;
bool hasError = false;
ImportCustomerDataHelper helper = new ImportCustomerDataHelper();
helper.LoginUserInfo = LoginUserInfo;
foreach (string file in fileList)
{
DataSet myDs = new DataSet();
string error = "";
AsposeExcelTools.ExcelFileToDataSet(file, out myDs, out error);
if (myDs != null && myDs.Tables.Count >= 4 && myDs.Tables[0].Rows.Count > 0)
{
try
{
//寫入或更新客戶信息
string customerID = helper.UpdateCustomer(myDs.Tables[0]);
if (!string.IsNullOrEmpty(customerID))
{
helper.AddFollow(customerID, myDs.Tables[1], worker);
helper.AddContact(customerID, myDs.Tables[2], worker);
helper.AddSupplier(customerID, myDs.Tables[3], worker);
}
}
catch (Exception ex)
{
hasError = true;
LogTextHelper.Error(ex);
}
}
}
string msg = "操作完成";
if (hasError)
{
msg += ",導入出現錯誤。具體可以查看log.txt日志記錄。";
}
e.Result = msg;
和上面的單個文件導入一樣,我們這里使用了一個封裝類ImportCustomerDataHelper,用來對數據進行轉換實體類,然后保存到數據庫的操作過程,下面我們來簡單看看里面的處理代碼:
/// <summary>
/// 客戶數據的批量導入和普通導入的操作邏輯代碼
/// </summary>
public class ImportCustomerDataHelper
{
/// <summary>
/// 登陸用戶信息
/// </summary>
public LoginUserInfo LoginUserInfo { get; set; }
/// <summary>
/// 寫入或更新客戶數據,如果成功更新返回ID值
/// </summary>
/// <param name="dataTable">客戶數據表</param>
/// <returns></returns>
public string UpdateCustomer(DataTable dataTable)
{
bool success = false;
bool converted = false;
DateTime dtDefault = Convert.ToDateTime("1900-01-01");
DateTime dt;
string result = "";
DataRow dr = dataTable.Rows[0];
if (dr != null)
{
string customerName = dr["客戶名稱"].ToString();
CustomerInfo info = CallerFactory<ICustomerService>.Instance.FindByName(customerName);
bool isNew = false;
if (info == null)
{
info = new CustomerInfo();
isNew = true;
}
info.Name = customerName;
info.HandNo = dr["客戶編號"].ToString();
info.SimpleName = dr["客戶簡稱"].ToString();
..........................
info.IsPublic = dr["公開與否"].ToString().ToBoolean();
info.Satisfaction = dr["客戶滿意度"].ToString().ToInt32();
info.TransactionCount = dr["交易次數"].ToString().ToInt32();
info.TransactionTotal = dr["交易金額"].ToString().ToDecimal();
info.Creator = dr["客戶所屬人員"].ToString();
converted = DateTime.TryParse(dr["創建時間"].ToString(), out dt);
if (converted && dt > dtDefault)
{
info.CreateTime = dt;
}
info.Editor = LoginUserInfo.ID.ToString();
info.EditTime = DateTime.Now;
if (isNew)
{
info.Dept_ID = LoginUserInfo.DeptId;
info.Company_ID = LoginUserInfo.CompanyId;
success = CallerFactory<ICustomerService>.Instance.Insert(info);
}
else
{
success = CallerFactory<ICustomerService>.Instance.Update(info, info.ID);
}
if (success)
{
result = info.ID;
}
}
return result;
}
...........................
導出操作,我們根據用戶的選擇,可以一次性導出多個Excel文件,每個Excel文件包含客戶的基礎信息,也包含相關數據,它們的格式和導入的格式保持一致即可,這樣方便數據的交換處理。
導出操作,我們需要把客戶的選擇信息轉換為需要導出的對象列表數據,然后綁定到Excel里面即可,因此我們的Excel里面,可以通過自定義模板,指定列的數據屬性就可以綁定好數據了。
獲取選擇的客戶信息的代碼如下所示。
List<CustomerInfo> list = new List<CustomerInfo>();
foreach (int iRow in rowSelected)
{
string ID = this.winGridViewPager1.GridView1.GetRowCellDisplayText(iRow, "ID");
CustomerInfo info = CallerFactory<ICustomerService>.Instance.FindByID(ID);
if (info != null)
{
list.Add(info);
}
}
前面介紹了,我們將使用自定義模板,在模板文件里面的對應字段下面,綁定一個參數屬性就可以了,通過Aspose.Cell的操作處理,我們就很方便把數據導出到Excel里面了,而里面的字段還可以很方便實現自由的裁剪操作。
自定義模板文件效果如下所示。
導出客戶以及相關信息的主要核心代碼如下所示。
#region 導出操作
//依次每個客戶數據導出一個文件
string ownerUserName = CallerFactory<IUserService>.Instance.GetFullNameByID(customerInfo.Creator.ToInt32());
string filePath = Path.Combine(selectPath, ownerUserName);
DirectoryUtil.AssertDirExist(filePath);
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("Customer", new List<CustomerInfo>() { customerInfo });//需要構造一個列表綁定
List<FollowInfo> followList = CallerFactory<IFollowService>.Instance.Find(string.Format("Customer_ID ='{0}' ", customerInfo.ID));
dict.Add("Follow", followList);
List<ContactInfo> contactList = CallerFactory<IContactService>.Instance.FindByCustomer(customerInfo.ID);
dict.Add("Contact", contactList);
PagerInfo pagerInfo = null;
List<SupplierInfo> supplierList = CallerFactory<ISupplierService>.Instance.FindByCustomer(customerInfo.ID, "", ref pagerInfo);
dict.Add("Supplier", supplierList);
string templateFile = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "客戶綜合資料-導出模板.xls");
if (!File.Exists(templateFile))
{
throw new ArgumentException(templateFile, string.Format("{0} 文件不存在,", Path.GetFileName(templateFile)));
}
string saveFileName = string.Format("{0}.xls", customerInfo.Name);
string saveFilePath = Path.Combine(filePath, saveFileName);
WorkbookDesigner designer = new WorkbookDesigner();
designer.Workbook = new Workbook(templateFile);
foreach (string key in dict.Keys)
{
designer.SetDataSource(key, dict[key]);
}
designer.Process();
designer.Workbook.Save(saveFilePath, SaveFormat.Excel97To2003);
#endregion
這樣利用Aspose.Cell的處理操作,通過綁定相關的數據對象,我們就很容易實現數據導出到符合我們預期格式的Excel里面去了,這樣操作高效、代碼干凈,Excel格式也非常符合我們的要求。
以上就是在客戶關系管理系統里面碰到特殊的數據導入導出需求的介紹和實現,希望大家相互交流,共同把軟件開發過程中,數據導入導出操作的使用體驗做到最好,更符合我們客戶使用的習慣和需求。
本文轉載自:
DevExpress技術交流群3:700924826 歡迎一起進群討論
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@ke049m.cn
文章轉載自: