首先,新的一年祝大家新年快乐。
元旦也是浪了几天没怎么写东西,但必须趁着新的一年赶快开动起来
在学习了NPOI操作Excel并完成了打印报表的经历后,
个人有一些感觉,
1.用DataTable导Excel还是太麻烦
2.使用Dapper等ORM的工具,再把结果变成Datatable(个人觉得是个带壳的二维数组)和orm的概念冲突,很别扭
所有觉得有必要写个工具直接把直接把List转化为Excel,不用经过DataTable这种东西,也能和ORM一起使用,
虽然实际的开发可能因为ExceL格式的要求而使用空间不大,但总归是要有。
这个工具我已经放在码云上,地址:https://gitee.com/godenSpirit/mto-excel
代码可以在上面看,下面是我的一些总结
在List集合中必须要指定一个泛型T,他是动态的,我要做的就是用反射分析这个T的属性,然后再做进一步的操作
好在C#的泛型比JAVA更强大,在运行时也保证其类型的确定,而不是JAVA的类型擦除(在运行时无法保证类型的确定性),
反射拿出一个类型的属性很简单,以所有属性都是基础类型的poco为例,
Type Target = typeof(T); PropertyInfo[] pros = Target.getProperties();
再遍历属性拿出值即可
foreach(PropertyInfo pro in pros)
{
Object obj = pro.getValue(集合的Element实例);
}
但以如此方法,拿出引用类型属性则会直接给那个对象不能进一步的打印,我使用一个自定义的Attribute来标记是否需要打印引用对象属性的具体值
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public class ReferenceType : Attribute //这个标记将属性标记为引用类型
{
//是否将引用类型再按属性拆分为多个列展示
private bool isMultiColumn = true;
public ReferenceType(bool isMultiColumn)
{
this.isMultiColumn = isMultiColumn;
}
public bool getIsMultiPart()
{
return this.isMultiColumn;
}
}
如此一来这则得到引用属性的属性值(比较套娃)则比较困难
但并不是不能获取
list.Foreach(item=>{
PropertyInfo[] pros = typeof(item).getProperties();
foreach(PropertyInfo property in pros)
{
if(property is 引用类型)
{
PropertyInfo[] innerPros = property.PropertyType.getProperties();
foreach(PropertyInfo secProperty in innerPros)
{
//获取引用属性的属性值,在三层的循环中,不太好判断
Object obj = secProperty.getValue(property.getValue(item));
}
}
//正常写入Excel
}
});
二.不同的输出到Excel的方式
可以将引用类型输出成很多列,也要允许只在一个列里,
也要能让人忽略掉一些不想输出的属性,
这个则比上面更加简单一点
////// 忽略制定标记的属性值 /// [AttributeUsage(AttributeTargets.Property, AllowMultiple = false,Inherited =true)] public class IgnoreType:Attribute { public IgnoreType() { } }
忽略某个属性其实就是在打印Excel的循环中跳过中间的某次循环
if (WrapperConverter.IgnoreTypePool.ContainsKey(pro.PropertyType))
{
//一定要在循环的开头进行判断要不要忽略本次循环
//如果在忽略类型中就直接Continue,开始下一轮循环
continue;
}
三.使用设计模式来分离关注度
虽然这个工具的代码不多,但三层循环去操作泛型,反射,标记还是有一定难度的。
使用设计模式来分散代码的功能
其中,我使用包装模式,将判断属性是否被标记的操作交给包装类,不在真正操作类上去获取标记并进行判断
////// 这是一个基本转化器的包装类,Design By Description Mode /// /// public class WrapperConverter { //类型池,用来保存打了Attribute的标签的自定义类型 public static DictionaryTypePool = new Dictionary (); public static Dictionary IgnoreTypePool = new Dictionary (); /// /// taget对象,用来具体的打表 /// public BasicConverter basic = null; public WrapperConverter() { basic = new BasicConverter(); } public IWorkbook ConvertToExcel(List list) { //在调target执行真正的方法之前,在此可以设置增强方法 advice method CheckAttribute(typeof(T)); return basic.ConvertToExcel(list); } /// /// (增强方法)检查泛型类型的属性中是否带有自定义的Reference的标签--check /// /// public void CheckAttribute(Type type) { PropertyInfo[] pros = type.GetProperties(); foreach (PropertyInfo pro in pros) { //遍历获取属性中的Attribute对象 ReferenceType refer = (ReferenceType)pro.GetCustomAttribute(typeof(ReferenceType)); IgnoreType ignore = (IgnoreType)pro.GetCustomAttribute(typeof(IgnoreType)); if (refer != null) { //将打了标记的类型和标记本身放到类型池中 TypePool.Add(pro.PropertyType, refer); } if(ignore != null) { IgnoreTypePool.Add(pro.PropertyType, ignore); } } } }
这样我就不用在检查标记了
测试: public class Animal
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public string LivingArea { get; set; }
public int[] testArray { get; set; }
}
public class Person
{
public string id { get; set; }
public string name { get; set; }
[IgnoreType]
public float tall { get; set; }
[ReferenceType(true)]
public Animal pet { get; set; }
}
static void Main(string[] args)
{
List list = new List()
{
new Person { id = "202101",name = "张三",tall = 1.7f,pet = new Animal { Id = 1,Name = "佩奇",Category="猪",LivingArea="新日暮里" } },
new Person { id = "202102",name = "李四",tall = 1.8f,pet = new Animal { Id = 2,Name = "旺财",Category="狗",LivingArea="SomeWhere" } },
};
WrapperConverter wrapper = new WrapperConverter();
//wrapper.basic = new BasicConverter();
IWorkbook workbook = wrapper.ConvertToExcel(list);
FileStream fileStream = new FileStream("C:/Users/ASUS/Desktop/Demo3.xls", FileMode.Create);
workbook.Write(fileStream);
fileStream.Close();
}
最后,欢迎提出意见



