最佳Excel导入实践(四)
相关链接
最佳Excel导入实践(一)
最佳Excel导入实践(二)
最佳Excel导入实践(三)
最佳Excel导入实践(四)
最佳Excel导入实践(五)
上一篇我们讲了Excel导入模板的生成,这一节我们将完成数据导入的功能。整个方案的主要类图如下(图中仅显示出了主要的方法):
为了做到通用,能满足所有业务单据的导入,我们大量用到了.net的反射技术。如构造订单表头实体的代码大致如下(构建表体的与此类似):
private Object GetHeadEntity(int rowIndex)
{
HSSFRow row=sheet.GetRow(rowIndex);
HSSFCell cell;
Type cls=Type.GetType(regulation.HeadClass);
Object obj=Activator.CreateInstance(cls);
PropertyInfo pi;
Object val;
int colIndex;
foreach(Column col in regulation.Columns)
{
if(!col.IsEntry)
{
//根据列名找出Excel中对应的列序号
colIndex=GetExcelColumnIndex(col.ColumnName);
cell=row.GetCell(colIndex);
//根据Column的数据类型取出Cell中的内容。如果为空将取Column对象中定义的默认值
val=GetCellValue(cell,col.DataType);
//获取实体类中的属性
pi=cls.GetProperty(col.Property);
//给实体类中的属性赋值
pi.SetValue(obj,val);
}
}
return obj;
}
{
HSSFRow row=sheet.GetRow(rowIndex);
HSSFCell cell;
Type cls=Type.GetType(regulation.HeadClass);
Object obj=Activator.CreateInstance(cls);
PropertyInfo pi;
Object val;
int colIndex;
foreach(Column col in regulation.Columns)
{
if(!col.IsEntry)
{
//根据列名找出Excel中对应的列序号
colIndex=GetExcelColumnIndex(col.ColumnName);
cell=row.GetCell(colIndex);
//根据Column的数据类型取出Cell中的内容。如果为空将取Column对象中定义的默认值
val=GetCellValue(cell,col.DataType);
//获取实体类中的属性
pi=cls.GetProperty(col.Property);
//给实体类中的属性赋值
pi.SetValue(obj,val);
}
}
return obj;
}
执行保存的Execute()方法代码大致如下:
private void Execute()
{
int rowsCount = sheet.PhysicalNumberOfRows;
Object headEntity,entryEntity;
List <object> entries;
for (int rowIndex = 0; rowIndex < rowsCount; rowIndex++)
{
if (headEntity == null)
{
//构建表头实体
headEntity = GetHeadEntity(rowIndex);
//构建表体实体
entries = new List<object>();
entryEntity = GetHeadEntity(rowIndex);
entries.Add(entryEntity);
}
//检查表头关键字段值是否与下一行相等,如果相等则当同一张单据处理,只用构建表体;如果不等说明是一张新的单据,要先把当前单据保存。
if (IsSameToNextRow(rowIndex))
{
entryEntity = GetHeadEntity(rowIndex);
entries.Add(entryEntity);
}
else
{
//设置分录属性的值
Type head = Type.GetType(regulation.HeadClass);
PropertyInfo entryProperty = head.GetProperty(regulation.EntryProperty);
entryProperty.SetValue(headEntity, entries);
Type clsMethod=Type.GetType(regulation.MethodClass);
Object service=Activator.CreateInstance(clsMethod);
MethodInfo saveMethod = clsMethod.GetMethod(regulation.SaveMethod);
MethodInfo exValidateMethod = clsMethod.GetMethod(regulation.ExValidateMethod);
if (exValidateMethod != null)
//执行附加的检查方法
exValidateMethod.Invoke(service, new object[]{headEntity});
if (saveMethod != null)
//执行保存方法
exValidateMethod.Invoke(service,new object[]{headEntity});
//重置为一张新的单据
headEntity = null;
entries = null;
}
}
}
{
int rowsCount = sheet.PhysicalNumberOfRows;
Object headEntity,entryEntity;
List <object> entries;
for (int rowIndex = 0; rowIndex < rowsCount; rowIndex++)
{
if (headEntity == null)
{
//构建表头实体
headEntity = GetHeadEntity(rowIndex);
//构建表体实体
entries = new List<object>();
entryEntity = GetHeadEntity(rowIndex);
entries.Add(entryEntity);
}
//检查表头关键字段值是否与下一行相等,如果相等则当同一张单据处理,只用构建表体;如果不等说明是一张新的单据,要先把当前单据保存。
if (IsSameToNextRow(rowIndex))
{
entryEntity = GetHeadEntity(rowIndex);
entries.Add(entryEntity);
}
else
{
//设置分录属性的值
Type head = Type.GetType(regulation.HeadClass);
PropertyInfo entryProperty = head.GetProperty(regulation.EntryProperty);
entryProperty.SetValue(headEntity, entries);
Type clsMethod=Type.GetType(regulation.MethodClass);
Object service=Activator.CreateInstance(clsMethod);
MethodInfo saveMethod = clsMethod.GetMethod(regulation.SaveMethod);
MethodInfo exValidateMethod = clsMethod.GetMethod(regulation.ExValidateMethod);
if (exValidateMethod != null)
//执行附加的检查方法
exValidateMethod.Invoke(service, new object[]{headEntity});
if (saveMethod != null)
//执行保存方法
exValidateMethod.Invoke(service,new object[]{headEntity});
//重置为一张新的单据
headEntity = null;
entries = null;
}
}
}
最后,细心的读者可能会发现,上面的代码我并没有进行数据有效性验证和出错时的处理。在刚刚不久做的一个系统中,我是将最终的出错信息用动态生成的一张Excel显示的,其显示了和模板结构一样的原始数据和批注的出错信息(正确数据已被导入,不会显示),这样可以直接在这个Excel中对原来的数据进行修改后再直接执行导入,这样做也是为了让用户出错后只用关心出错后的数据,提高可用性。由于有效性验证和出错信息显示相对简单,也不是本方案的重点,所以不作详细说明。
至此,基本的导入数据功能已经实现。我们发现,基于这个方案对于普通的Excel导入主要的工作就是写那个配置引入规则的XML文件,所以可以轻松实现整个业务系统中所有的导入工作。当然,本方案是以表头表体的案例来进行设计的,对于单表结构稍作修改就可以实现,然而如果要对应一对多的关系,比如一条Excel中的记录要写到多个表中,本设计本不适用,但基本的基于反射的思想是不会变的,如果有需要读者可以自已试试。