如何延长epplus数据提取与像WithAllProperties方法

问题描述 投票:1回答:1

我与Excel文件的工作。我有读取存储在Excel工作表的表格值,并将其反序列化对象。对于这一点,我使用OfficeOpenXml与EPPlus.DataExtractor的帮助。我的Excel表格中有若干列,因此我班有几个特性 - 具有不同数据类型的字符串,整型,DateTime是否,双打和可空整型,DateTime是否,双打。我不能假设,更多类型不会进来的时间。例如,对于反序列化的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; }
}

列是我自己的属性,它告诉哪一列包含给定属性值的提取:

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方法中使用,以获得列属性(一个简单的字符串)的值。

为了完整起见,我将提供属性提取,它看起来像这样:

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做了很多次。并再次,每一个类,我有不同的相应的提取。

我想到了创建提取的通用版本,像

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 /这是相应属性的列属性值 - 为了这个,我不知道如何获得它。

任何帮助/提示/线索需要。

c# excel generics reflection epplus
1个回答
0
投票

好了,因为没有人有时间提供任何暗示,我制作我自己的解决方案。这不是理想的 - 也许它可以改进,但我对结果感到满意,因为我的代码已经减少了很多。

第一个变化是从静态GetColumnAnnotation方法清除所有的Excel模型一样为MyModel。什么左边是用列属性纯粹的性能。接下来的变化是摆脱仿制AttributeExtractor - 它不需要任何更多。

我创建了一个通用ExcelExtractor类,看起来非常渺茫:

public class ExcelExtractor<T> where T: class, new()
{
    public ExcelExtractor(IExcelPathProvider pathProvider) => _pathProvider = pathProvider;
    public List<T> Create(int sheetNumber)
    {
        using (var excelPackage = new ExcelPackage(new FileInfo(_pathProvider.GetPath())))
        {
            var worksheet = excelPackage.Workbook.Worksheets[sheetNumber];
            return worksheet
                .Extract<T>()
                .WithAllProperties()
                .GetData(2, row => worksheet.Cells[row, 1].Value != null)
                .ToList();
        }
    }

    private readonly IExcelPathProvider _pathProvider;
}

接下来,我创建了一些扩展类,看起来像这样:

public static class ReflectionExtensions
{
    public static ICollectionPropertyConfiguration<T> WithAllProperties<T>(
        this IDataExtractor<T> extractor) where T : class, new() =>
        typeof(T)
        .GetProperties()
        .Aggregate(extractor, ExtractProperty) as ICollectionPropertyConfiguration<T>;

    private static string ToColumn(this PropertyInfo property) =>
        ((ColumnAttribute)property.GetCustomAttributes(typeof(ColumnAttribute), true)
            .First()).Column;
    private static IDataExtractor<T> ExtractProperty<T>(IDataExtractor<T> extractor,
        PropertyInfo property) where T : class, new()
    {
        if (property.PropertyType == typeof(string))
            return extractor.WithProperty(ExpressionGenerator<T>.GetStringProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(int))
            return extractor.WithProperty(ExpressionGenerator<T>.GetIntProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(int?))
            return extractor.WithProperty(ExpressionGenerator<T>.GetNullableIntProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(DateTime))
            return extractor.WithProperty(ExpressionGenerator<T>.GetDateTimeProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(DateTime?))
            return extractor.WithProperty(ExpressionGenerator<T>.GetNullableDateTimeProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(bool))
            return extractor.WithProperty(ExpressionGenerator<T>.GetBooleanProperty(property), property.ToColumn());

        if (property.PropertyType == typeof(bool?))
            return extractor.WithProperty(ExpressionGenerator<T>.GetNullableBooleanProperty(property), property.ToColumn());

        throw new ArgumentException($"Unknown type {property.PropertyType}");
    }
    private static class ExpressionGenerator<T>
    {
        public static Expression<Func<T, string>> GetStringProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, string>>(GetMember(property), Parameter);
        public static Expression<Func<T, int>> GetIntProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, int>>(GetMember(property), Parameter);
        public static Expression<Func<T, int?>> GetNullableIntProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, int?>>(GetMember(property), Parameter);
        public static Expression<Func<T, DateTime>> GetDateTimeProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, DateTime>>(GetMember(property), Parameter);
        public static Expression<Func<T, DateTime?>> GetNullableDateTimeProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, DateTime?>>(GetMember(property), Parameter);
        public static Expression<Func<T, bool>> GetBooleanProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, bool>>(GetMember(property), Parameter);
        public static Expression<Func<T, bool?>> GetNullableBooleanProperty(PropertyInfo property) =>
            Expression.Lambda<Func<T, bool?>>(GetMember(property), Parameter);

        private static readonly ParameterExpression Parameter =
            Expression.Parameter(typeof(T), "p");
        private static MemberExpression GetMember(PropertyInfo property) =>
            Expression.Property(Parameter, property.Name);

    }
}

也许解决方案甚至可以进一步提高,如果有人提供任何暗示,我会很感激,但我很满意的效果 - 我有一个通用提取器,其工作原理是对我都不能创建任何新的Excel车型的魅力。如果需要一些额外的数据类型,我将添加两种方法来帮助我的班。

© www.soinside.com 2019 - 2024. All rights reserved.