【发布时间】:2019-02-08 23:31:10
【问题描述】:
我正在处理 excel 文件。我必须读取存储在 excel 表中的表格值并将它们反序列化为对象。为此,我在 EPPlus.DataExtractor 的帮助下使用了 OfficeOpenXml。我的 Excel 表有几列,因此我的类有几个属性 - 具有不同的数据类型字符串、整数、日期时间、双精度和可空整数、日期时间、双精度。我不能假设更多类型不会及时出现。例如,用于反序列化 excel 行的类可以如下所示:
public class MyModel
{
[Column("A")]
public string Id { get; set; }
[Column("B")]
public string Code { get; set; }
[Column("C")]
public int Number { get; set; }
[Column("D")]
public DateTime? ValidTo { get; set; }
}
Column 是我自己的属性,它告诉提取器哪一列包含给定属性的值:
public class ColumnAttribute : Attribute
{
public string Column { get; set; }
public ColumnAttribute(string column) => Column = column;
}
这就是为什么,我可以像这样使用EPPlus
public class MyModelExtractor
{
private readonly string _path;
public MyModelExtractor(string path) => _path = path;
public List<MyModel> Create()
{
using (var excelPackage = new ExcelPackage(new FileInfo(_path)))
{
var worksheet = excelPackage.Workbook.Worksheets[1];
return worksheet
.Extract<MyModel>()
.WithProperty(p => p.Id, MyModel.GetColumnAnnotation(p => p.Id))
.WithProperty(p => p.Code , MyModel.GetColumnAnnotation(p => p.Code ))
.WithProperty(p => p.Number, MyModel.GetColumnAnnotation(p => p.Number))
.WithProperty(p => p.ValidTo , MyModel.GetColumnAnnotation(p => p.ValidTo ))
.GetData(2, row => worksheet.Cells[row, 1].Value != null)
.ToList();
}
}
现在,MyModel 类中还有更多内容,即:
public static string GetColumnAnnotation<T>(Expression<Func<MyModel, T>> propertySelector) =>
AttributeExtractor.GetPropertyAttributeValue<MyModel, T, ColumnAttribute, string>(propertySelector, attribute => attribute.Column);
可以看出,它在WithProperty方法中使用,用于获取Column属性的值(只是一个字符串)。
为了完整起见,我将提供 AttributeExtractor,如下所示:
public static class AttributeExtractor
{
public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T, TOut>> propertyExpression,
Func<TAttribute, TValue> valueSelector) where TAttribute : Attribute
{
var propertyInfo = (PropertyInfo)((MemberExpression)propertyExpression.Body).Member;
return propertyInfo.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault() is TAttribute attr
? valueSelector(attr)
: throw new MissingMemberException(typeof(T).Name + "." + propertyInfo.Name, typeof(TAttribute).Name);
}
}
现在,在每个模型类(我有几十个)中,我必须提供这个静态方法 GetPropertyAttributeValue。更有问题的是,这些类包含很多属性,所以对 WithProperty 的调用要进行多次。而且,对于每个班级,我都有单独的相应提取器。
我想过创建一个通用版本的 Extractor,比如
public class Extractor<T> { ... }
其中 T 将是 MyModel 之类的类型,然后我可以编写一些方法,例如 WithAllProperties(),它将替换对 WithProperty 的所有调用。
该类将如下所示
public class Extractor<T>
{
...ctor and _path, and then:
public List<T> Create()
{
using (var excelPackage = new ExcelPackage(new FileInfo(_path)))
{
var worksheet = excelPackage.Workbook.Worksheets[1];
return worksheet
.Extract<T>()
.WithAllProperties()
.GetData(2, row => worksheet.Cells[row, 1].Value != null)
.ToList();
}
}
}
现在,我正在努力使用 WithAllProperties 方法。它应该是这样的:
public static ICollectionPropertyConfiguration<T> WithAllProperties<T>(
this IDataExtractor<T> extractor) where T : class, new()
{
foreach(var property in typeof(T).GetProperties())
extractor = extractor.WithProperty(/1/, /2/);
return extractor as ICollectionPropertyConfiguration<T>;
}
缺少的是 /1/ 类型应该是
Expression<Func<T,TProperty>>
我不能动态生成这个值(没有一些对我来说似乎不聪明的技巧,比如切换属性变量的类型,并创建所需的表达式。它可以工作,但是当新类型出现时,这个开关必须扩展并且我确信它可以使用反射动态地完成)。另一件事是 /2/ ,它是对应属性的 Column 属性的值 - 为此我不知道如何获取它。
需要任何帮助/提示/线索。
【问题讨论】:
标签: c# excel generics reflection epplus