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(exssage);
}
}
由于导入过程中需要耗费一定的时间,因此我们可以通过后台线程结合进度条的方式提示用户,界面设计效果如下效果所示。
刚才说到,保存数据,我们把它放到后台线程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)
{
thisgressBar1.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=exssage;
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 fileList=new List();
foreach (object item in this.lstPath.Items)
{
CListItem fileItem=item as CListItem;
if (fileItem !=null)
{
fileList.Add(fileItem.Value);
}
}
thisgressBar1.Visible=true;
worker.RunWorkerAsync(fileList);
}
}
}
这个后台线程的处理逻辑和单个文件导入的操作差不多,只不过这里需要增加一个文件列表的遍历处理而已,具体代码如下所示。
void worker_DoWork(object sender, DoWorkEventArgs e)
{
List fileList=e.Argument as List;
if (fileListnull || fileList.Count0) 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,用来对数据进行转换实体类,然后保存到数据库的操作过程,下面我们来简单看看里面的处理代码:
///
/// 客户数据的批量导入和普通导入的操作逻辑代码
///
public class ImportCustomerDataHelper
{
///
/// 登陆用户信息
///
public LoginUserInfo LoginUserInfo { get; set; }
///
/// 写入或更新客户数据,如果成功更新返回ID值
///
/// 客户数据表
///
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.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;
infopany_ID=LoginUserInfopanyId;
success=CallerFactory.Instance.Insert(info);
}
else
{
success=CallerFactory.Instance.Update(info, info.ID);
}
if (success)
{
result=info.ID;
}
}
return result;
}
…
导出操作,我们根据用户的选择,可以一次性导出多个Excel文件,每个Excel文件包含客户的基础信息,也包含相关数据,它们的格式和导入的格式保持一致即可,这样方便数据的交换处理。
导出操作,我们需要把客户的选择信息转换为需要导出的对象列表数据,然后绑定到Excel里面即可,因此我们的Excel里面,可以通过自定义模板,指定列的数据属性就可以绑定好数据了。
获取选择的客户信息的代码如下所示。
List list=new List();
foreach (int iRow in rowSelected)
{
string ID=this.winGridViewPager1.GridView1.GetRowCellDisplayText(iRow, “ID”);
CustomerInfo info=CallerFactory.Instance.FindByID(ID);
if (info !=null)
{
list.Add(info);
}
}
前面介绍了,我们将使用自定义模板,在模板文件里面的对应字段下面,绑定一个参数属性就可以了,通过Aspose.Cell的操作处理,我们就很方便把数据导出到Excel里面了,而里面的字段还可以很方便实现自由的裁剪操作。
自定义模板文件效果如下所示。
导出客户以及相关信息的主要核心代码如下所示。
#region 导出操作
//依次每个客户数据导出一个文件
string ownerUserName=CallerFactory.Instance.GetFullNameByID(customerInfo.Creator.ToInt32());
string filePath=Pathbine(selectPath, ownerUserName);
DirectoryUtil.AssertDirExist(filePath);
Dictionary
共同学习,写下你的评论
评论加载中...
作者其他优质文章