海量、多页数据导出到Excel(C#版本)
最近做了一个项目,其中需要将多个DataGridView中的数据导出到一个Excel文件中,如:整体情况统计表、审核情况明细表及不合格原因统计表。开始觉得难度不大,可是深入进去后发现问题多多。
1、 数据量大:一个明细表中包含的数据基本上超过10w,不能采用常规的一行行导入,必须批量导入;
2、 即使是批量导入,也需要接近1分钟,必须使用多线程;
3、 在多线程进行时,必须在页面通知用户导出正在进行中,需要使用事件通知;
4、 Excel的版本不同,每个sheet页能够显示的数据量不一样,2003是65536,而2007可以达到100多万行。如果客户使用的是Excel2003,单独对明细表数据来说就需要多个sheet页来保存。另外每个DataGridView中的数据也需要单独的sheet页来保存。在这里我将后者称之为逻辑页,前者为物理页;
5、 需要方便扩展,即使是其他类型的数据也能够重用代码,不能仅限于上述的三个DataGridView中显示的数据。
运行效果示意图
经过百度,在网上发现一种将数据以object[,]二维数组的形式直接导出到Excel的range区域是最省时的方式,于是整个方法的设计思路就出来了,代码如下:
1、设计一个特性类,对要显示的数据类中的属性进行说明,在这里我主要使用特性类来保存中文列标题以及指明某个属性是否需要导出到Excel中。
public class ChineseNameAttribute : Attribute
{
public ChineseNameAttribute(string cname, bool show)
{
ChName = cname; this.ShowAsTitle = show;
}
public ChineseNameAttribute(string cname) : this(cname, false) { }
public bool ShowAsTitle { get; set; }
public string ChName { get; set; }
}
2、为要导出到Excel中数据类创建一个基类ExcelDataBase,指定两个方法,一个是获取标题,另一个是获取对象的数组形式,因为上面说了,我需要的数据格式为object[,]二维数组,那么我的每一个对象就应该是object[]一维数组。
using System;
using System.Collections.Generic;
using System. Reflection;
public class ExcelDataBase
{
public virtual string[] GetTitle()
{
List<string> titles = new List<string>();
//获取当前对象的类型描述,这里的对象实际上是子类对象
Type type = this.GetType();
//获取当前对象的所以属性信息
PropertyInfo[] pis = type.GetProperties();
foreach (PropertyInfo pi in pis)
{
//获取我们定义在属性上的特性描述,描述方式在下面
ChineseNameAttribute cnAtt = (ChineseNameAttribute)Attribute
.GetCustomAttribute(pi, typeof(ChineseNameAttribute));
//根据属性上的特性描述,决定该属性是否显示及显示的标题名称
if (cnAtt.ShowAsTitle == true)
titles.Add(cnAtt.ChName);
}
return titles.ToArray();
}
//将当前对象的属性转换成object[]数组
public virtual object[] GetValue()
{
List<object> values = new List<object>();
Type type = this.GetType();
PropertyInfo[] pis = type.GetProperties();
foreach (PropertyInfo pi in pis)
{
ChineseNameAttribute cnAtt = (ChineseNameAttribute)Attribute
.GetCustomAttribute(pi, typeof(ChineseNameAttribute));
if (cnAtt.ShowAsTitle == true)
values.Add(pi.GetValue(this, null).ToString());
}
return values.ToArray();
}
}
3、设计我们显示在DataGridView中的数据类型(处于个人喜好,我绑定到DataGridView都是泛型集合List<SomeClass>)
public class TotalStatistics : ExcelDataBase
{
[ChineseNameAttribute("营业部名称", true)]
public string KHDCode { get; set; }
[ChineseNameAttribute("凭证总数", true)]
public int ImageCounts { get; set; }
[ChineseNameAttribute("已审核数量", true)]
public int AuditedCounts { get; set; }
[ChineseNameAttribute("合格数量", true)]
public int PassedCounts { get; set; }
[ChineseNameAttribute("不合格数量", true)]
public int UnPassedCounts { get; set; }
[ChineseNameAttribute("合格率", true)]
public string PassPercent //只读属性
{
get {
return Math. Round((((decimal)PassedCounts * 100) / AuditedCounts), 2)
.ToString() + "%";
}
}