您的位置:首页 > 其它

通用邮件自动发送Winform控件开发(二)

2011-03-15 11:24 267 查看
上节粗略说了一下整个系统的结构,本节将基于该结构说明Utility项目的实现,好了,废话不多说,我们先看看上节对Utility功能的定义:负责与数据载体通信,包括Access、Excel、SQL Server、MySql、TXT的数据写入和查询等操作,该项目内分别有处理上诉五种数据载体的五个类。

很简单,这五个类应分别为AccessHelper、ExcelHelper、SqlHelper、MySqlHelper、TxtHelper,因为负责数据的写入和查询,相对于Access、sql和Mysql来说,都很熟悉了,网上也有很多开源的代码,这里就不多说,看看ExcelHelper的实现。

以前在网上看到过有企业将Excel作为本企业的数据库解决方案,不知道效率怎么样,Excel确实可以保存大量的数据,大家知道,Excel2003的数据保存理论值为能保存256页,每页能保存65000行数据,理论能存储1600万行数据,在实际中,因为Excel2003处理数据的能力有限,一般达不到该理论值。在我的测试中,每行5列数据,每页存储6万行,当存储到20页时,Excel提示无法处理,也就是大约100万行数据。

好了,言归正传,先看看Excel获取数据的实现,因为Excel2003使用Microsoft.Jet.OLEDB数据引擎,所以我们可以将excel文件视为数据库直接试用查询语句查询(在处理插入的时候我曾经想逆向使用sql语句插入,但是没找到入口,也没查到资料),查询结果得到DataTable,因为中间不涉及到类的状态保存,所以我们应该使用静态方法,代码如下:

/// <summary>
/// 读操作,将给定的Excel文件读取为DataTable,默认读取Excel第一个工作簿,工作簿从1开始计数
/// </summary>
/// <param name="_filePath">Excel文件地址</param>
/// <returns></returns>
public static System.Data.DataTable ExecuteDataTable(string _filePath, int workSheet)
{
if (!System.IO.File.Exists(_filePath))
return null;
string strConn = "";
if (System.IO.Path.GetExtension(_filePath) == ".xls")
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + _filePath + ";Extended Properties='Excel 8.0;HDR=Yes'";
else
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + _filePath + ";Extended Properties='Excel 12.0 Xml;HDR=Yes'";
System.Data.OleDb.OleDbConnection conn = new System.Data.OleDb.OleDbConnection(strConn);
try
{
conn.Open();
System.Data.DataTable Dt = conn.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables, null);
string tableName = Dt.Rows[workSheet - 1][2].ToString().Trim();
string strSql = "select * from [" + tableName + "]";
OleDbDataAdapter sda = new OleDbDataAdapter(strSql, conn);
System.Data.DataTable resault = new System.Data.DataTable();
sda.Fill(resault);
return resault;
}
catch (Exception e)
{
throw e;
}
finally
{
if (conn.State == System.Data.ConnectionState.Open)
conn.Close();
}
}


在Excel中,每页工作表成为一个WorkSheet,相当于数据库中的一张表,所以需要为方法传入Excel文件地址(数据库连接)和WorkSheet编号(表名称),注意连接语句中“HDR=Yes”部分,它表示是否将WorkSheet内的第一行数据当作表头,yes表示是,那么第一行数据则为返回的DataTable的表头,No表示不是,则返回的DataTable标头还是为默认值。

以上是从Excel读取数据,下面谈谈向Excel写入数据,在开始向Excel写入数据前,我们先分析一下应该分为哪几个步骤?

首先是打开Excel文件,然后是向Excel文件写入数据,最后关闭Excel文件,好,下面我们来分析一下按照这个步骤我们可能会遇到哪些问题?

在打开阶段我们可能遇到:

1.显而易见的,Excel文件可能不存在

2.因为我们并不清楚用户使用的Excel的哪个版本(2003还是2007?),所以需要判断

3.因为用户是间断的向Excel写入数据,那么我们在处理数据时是每次都执行打开写入关闭的流程还是打开后一直等待写入,最后应用程序注销的时候关闭Excel?根据我的测试,程序在启动Excel和注销Excel时,会出现延时等待的现象,如果写入数据的时间间隔小于5秒,将出现Excel还未关闭时下一次数据写入已经开始请求Excel打开,出现冲突报错,所以这里我选择在程序启动时启动Excel,程序关闭时才注销Excel(前提是用户选择的数据载体为Excel)。

在数据写入阶段我们可能遇到:

1.Excel中已经存在数据,我们需要首先找到可以写入数据的位置(第几个WorkSheet?第几行开始写入数据?)

2.准备写入的数据行数过多(比方准备写入的行数有10万行,需要分开处理)

3.找到的WorkSheet已经没有足够的行数存储我们准备的数据(准备写入1万行,实际WorkSheet最多只剩余5000行空间)

4.整个Excel已经达到理论饱和值(256个WorkSheet),无法再创建新的WorkSheet提供写入

5.写入过于频繁,当上一次写入操作未完成时,下一次写入操作已经开始(肯定会报错)

6.写入时需要注意Excel2003和Excel2007的最大存储容量是不一样的,excel2007的每个WorkSheet最大能存储10万行数据

7.当写入的数据达到Excel的存储最大值时,Excel会弹窗报告无法处理,此时咱们系统该如何处理?

在注销Excel资源时我们可能遇到:

1.数据正在写入,此时注销Excel会报错

好,综合上面的问题,我们首先应该相对应提出解决方法,然后开始正是编写代码,我现在的解决方法是这样的,可能问题和解决方法都有所缺陷,请大家指正。

打开阶段问题的解决方法:

1.打开Excel方法需要Excel保存文件夹地址,检查该文件夹地址,如果不存在,新建该文件夹,在文件夹内查找适合写入数据的Excel(通过检查命名),如果不存在,新建一个Excel文件

2.在查找文件夹内文件时,分别查找后缀为“.xls”和“.xlsx”,然后维护一个变量,该变量说明用户使用的excel版本

3.系统在启动时加载Excel文件,然后一直占用该资源,直到程序关闭时释放

在数据写入阶段问题的解决方法:

1.在查找Excel可以写入数据位置时,我最开始是在WorkSheet里面查找,后来发现效率奇低,所以现在在该项目内添加了一个配置文件,文件内保存excel的版本,可以写入数据的WorkSheet编号和开始位置的行数,每次数据写入时读取文件,写入完成后将最新的数据写进文件以便下次读取

2.当需要写入的数据在当前的WorkSheet无法一次写入时,将数据分割,前部分写入当前WorkSheet,后部分写入下一个WorkSheet,中间则需要在新建WorkSheet

3.与2的解决办法一致

4.当达到理论值时,关闭当前的Excel,新建一个Excel继续写入,并且每天新建一个Excel,将当天的数据写入该Excel,一则方便管理,二则避免Excel内存储数据过多,造成应用程序响应迟缓

5.将需要写入的数据放入队列,维护一个标记变量表示数据写入是否完成,然后使用一个计时器检查该标记和队列内的对象,当队列不为空并且标记变量表示数据写入完成时,队列弹出顶部的数据进行写入,保证每次都只有一组数据进行写入

6.如果数据一次无法全部写入WorkSheet,在分割数据时判断用户excel版本,如果是2003则按60000进行分隔,如果为2007则按1000000进行分隔

7.当写入数据量达到Excel最大承受值时,关闭该Excel,新建一个Exce进行写入,这里需要注意检查当前的数据是否已经写入,如果没有写入,需要在新的Excel进行写入

在注销时问题的解决方案:

1.在关闭程序时先检查数据队列是否为空并且检查标识变量是否表示数据写入完成,如果不是,则警告用户数据丢失或者采用静默写入方式,待数据写入完成后在退出程序,两种方法有待考证那种更优!

代码马上更新...

通过上面的分析,我们首先大致整个流程的流程图画出来,打开Excel流程如下图:



相应代码如下:

/// <summary>
/// 打开Excel文档,如果不存在,新建
/// </summary>
/// <param name="filePath"></param>
public void OpenExcel(string saveFolder)
{
if (!System.IO.Directory.Exists(saveFolder))
System.IO.Directory.CreateDirectory(saveFolder);
string fileName = saveFolder + "//data_" + DateTime.Now.ToString("yyyy_MM_dd") + "_" + excelNumber.ToString();
string fileName2003 = fileName + ".xls";
string fileName2007 = fileName + ".xlsx";
int iFile = 0;
if (System.IO.File.Exists(fileName2003))
iFile = 1;
else if (System.IO.File.Exists(fileName2007))
iFile = 2;
this._excelOperate = new ExcelOperate("");
this.dtQueue = new Queue<System.Data.DataTable>();
switch (iFile)
{
case 0:
{
_excelOperate.FilePath = fileName.ToString();
_excelOperate.CreatExcel();
if (System.IO.File.Exists(fileName2003))
AppconfigOperate.SaveBeginWritePlace(2003, 1, 1);
else if (System.IO.File.Exists(fileName2007))
AppconfigOperate.SaveBeginWritePlace(2007, 1, 1);
else
throw new Exception("无法创建Excel文件,请检查文件夹是否有写入权限");
break;
}
case 1:
{
_excelOperate.FilePath = fileName2003;
_excelOperate.OpenWorkBook();
break;
}
case 2:
{
_excelOperate.FilePath = fileName2007;
_excelOperate.OpenWorkBook();
break;
}
}
}


因为我选择的是在软件启动时加载Excel文件,在打开时需要保存该类状态,所以使用实例方法,并在外部声明私有_excelOperate成员变量,ExcelOperate类主要负责直接操作Excel,包括Excel的启动,Excel文件新建,打开,WorkBook的添加、定位以及数据写入,Excel资源注销等操作。

好了,快12点了,洗洗弄了睡!明天在下一篇博客中说明向Excel中写入数据的具体实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: