2014-09-01 43 views
8
调用LoadFromCollection时忽略特性

我尝试使用下面的代码来生成一个Excel文件:在EPPlus

public static Stream GenerateFileFromClass<T>(IEnumerable<T> collection, int startrow, int startcolumn, byte[]templateResource) 
     { 
using (Stream template = new MemoryStream(templateResource))//this is an excel file I am using for a base/template 
    { 
     using (var tmpl = new ExcelPackage(template)) 
     { 
      ExcelWorkbook wb = tmpl.Workbook; 
      if (wb != null) 
      { 
       if (wb.Worksheets.Count > 0) 
       { 
        ExcelWorksheet ws = wb.Worksheets.First(); 
        ws.Cells[startrow, startcolumn].LoadFromCollection<T>(collection, false); 
       } 
       return new MemoryStream(tmpl.GetAsByteArray()); 
      } 
      else 
      { 
       throw new ArgumentException("Unable to load template WorkBook"); 
      } 
     } 
    } 
} 

这就像一种享受,但是..我想忽略了几个属性的我类集合,所以它匹配我的模板。我知道LoadFromCollection将基于该类的公共属性在Excel文件中生成列,但由于我使用实体框架加载类,如果将该字段标记为私有,则EF会抱怨 - 主要是因为其中一个字段我不想展示的是钥匙。

我试图标记我不想使用[XmlIgnore]的属性,无济于事。有没有办法做到这一点,短缺将整个集合加载到一个数据集或一些这样的,并修剪列?或者转换成没有我不需要的属性的基类?

+1

难道你不使用类似AutoMapper或自定义映射到另一个POCO只所需的属性,然后通过新的对象集合以epplus,而不是EF集合 – 2014-11-12 13:52:30

回答

14

是的,EPPlus提供.LoadFromCollection<T>()方法的过载,MemberInfo[]参数用于您希望包含的属性。

这给我们所有我们需要忽略具有某个属性的任何属性。

例如,如果我们想忽略这个自定义属性的属性:

public class EpplusIgnore : Attribute { } 

那么我们可以写一个小扩展方法,先找到一个属性的所有MemberInfo对象没有[EpplusIgnore]属性然后返回EPPlus dll中.LoadFromCollection方法的正确超载结果。

事情是这样的:

public class Person 
{ 
    [EpplusIgnore] 
    public int Key { get; set; } 
    public string Name { get; set; } 
    public int Age { get; set; } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var demoData = new List<Person> { new Person { Key = 1, Age = 40, Name = "Fred" }, new Person { Key = 2, Name = "Eve", Age = 21 } }; 

     FileInfo fInfo = new FileInfo(@"C:\Temp\Book1.xlsx"); 
     using (var excel = new ExcelPackage()) 
     { 
      var ws = excel.Workbook.Worksheets.Add("People"); 
      ws.Cells[1, 1].LoadFromCollectionFiltered(demoData); 

      excel.SaveAs(fInfo); 
     } 
    } 
} 

给输出我们会:

public static class Extensions 
{ 
    public static ExcelRangeBase LoadFromCollectionFiltered<T>(this ExcelRangeBase @this, IEnumerable<T> collection) where T:class 
    { 
     MemberInfo[] membersToInclude = typeof(T) 
      .GetProperties(BindingFlags.Instance | BindingFlags.Public) 
      .Where(p=>!Attribute.IsDefined(p,typeof(EpplusIgnore))) 
      .ToArray(); 

     return @this.LoadFromCollection<T>(collection, false, 
      OfficeOpenXml.Table.TableStyles.None, 
      BindingFlags.Instance | BindingFlags.Public, 
      membersToInclude); 
    } 

} 

因此,举例来说,使用它像这样将导出Person收集到Excel时忽略.Key财产预期:

enter image description here

+0

它适用于我。但我创建了一个方法LoadFromCollectionFiltered (这ExcelRangeBase @ this,IEnumerable 集合,布尔printHeader)为了决定我想打印头或不晚。 – 2016-02-25 08:48:18

+0

这是一个奇妙的建议,但不幸的是它不适用于我。对于IEnumerable 使用标准LoadFromCollection,它可以很好地工作,只需使用额外的列。当我使用这里提供的过滤方法时,我收到关于声明类型的错误: '参数属性中提供的属性的类型必须与T'相同 – 2017-06-02 05:00:07

+0

解决了我自己的问题;我的类继承自另一个类,并且Epplus在检查声明类型时不检查子类型或继承。因此,我只需添加另一个where子句以确保它不包含'MemberInfo'数组中的父类。 Like:'.Where(p => p.DeclaringType!= typeof(ParentObject))' – 2017-06-02 15:54:45

2

感谢Stewart_R,根据你的工作我做了接收属性名称一个新问题:

public static ExcelRangeBase LoadFromCollection<T>(this ExcelRangeBase @this, 
    IEnumerable<T> collection, string[] propertyNames, bool printHeaders) where T:class 
{ 
    MemberInfo[] membersToInclude = typeof(T) 
      .GetProperties(BindingFlags.Instance | BindingFlags.Public) 
      .Where(p=>propertyNames.Contains(p.Name)) 
      .ToArray(); 

    return @this.LoadFromCollection<T>(collection, printHeaders, 
      OfficeOpenXml.Table.TableStyles.None, 
      BindingFlags.Instance | BindingFlags.Public, 
      membersToInclude); 
}