【问题标题】:Ignoring properties when calling LoadFromCollection in EPPlus在 EPPlus 中调用 LoadFromCollection 时忽略属性
【发布时间】:2014-09-01 10:11:47
【问题描述】:

我正在尝试使用以下代码生成 Excel 文件:

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 会抱怨 - 主要是因为其中之一我不想显示的字段是 Key。

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

【问题讨论】:

  • 你不能使用像 AutoMapper 或自定义映射到另一个 POCO 的东西,它只有所需的属性,然后将新的对象集合传递给 epplus,而不是 EF 集合

标签: c# excel entity-framework-6 epplus


【解决方案1】:

是的,EPPlus 提供了 .LoadFromCollection&lt;T&gt;() 方法的重载,并为您希望包含的属性提供了 MemberInfo[] 参数。

这为我们提供了忽略具有特定属性的任何属性所需的一切。

例如,如果我们想忽略具有此自定义属性的属性:

public class EpplusIgnore : Attribute { }

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

类似这样的:

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 属性:

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);
        }
    }
}

给出我们期望的输出:

【讨论】:

  • 它对我有用。但是我创建了一个方法 LoadFromCollectionFiltered(this ExcelRangeBase @this, IEnumerable collection, bool printHeader) 来决定我是否想稍后打印标题。
  • 这是一个很棒的建议,但不幸的是它对我不起作用。使用 IEnumerable 的标准 LoadFromCollection,它可以完美地工作,只是使用额外的列。当我使用您在此处提供的过滤方法时,我收到关于声明类型的错误:Supplied properties in parameter Properties must be of the same type as T
  • 解决了我自己的问题;我的类是从另一个类继承的,而 Epplus 在检查声明类型时不检查子类型或继承。因此,我只需要添加另一个 where 子句以确保它不包含 MemberInfo 数组中的父类。喜欢:.Where(p =&gt; p.DeclaringType != typeof(ParentObject))
【解决方案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);
}

【讨论】:

    【解决方案3】:

    EPPlus 的 5.5 版开始,EpplusIgnore 属性被添加到库中。 https://github.com/EPPlusSoftware/EPPlus/pull/258

    我们可以像下面这样使用它。

    using System;
    using OfficeOpenXml.Attributes;
    
    namespace ExportToExcel.Services.ExportViewModels
    {
       
        [EpplusTable]
        public class StudentExportViewModel
        {
            
            // [EpplusTableColumn]
            [EpplusIgnore]
            public string Id { get; set; }
    
            ...
    
        }
    }
    

    请注意,EpplusTableEpplusTableColumn 属性是必需的。

    这里是相关测试用例的链接https://github.com/EPPlusSoftware/EPPlus/blob/develop/src/EPPlusTest/LoadFunctions/LoadFromCollectionAttributesTests.cs

    最近,我不得不在一个项目中使用 EPPlus,我已经在这篇博文 https://www.kajanm.com/blog/exporting-data-to-excel-in-c-sharp/ 中记录了设置

    【讨论】:

      【解决方案4】:

      以下是 Stewart_R 答案的简短版本。如果有人使用通用方法创建 excel,请继续使用以下方法。

       public static class EPPlusHelper
          {
              public static byte[] GetExportToExcelByteArray<T>(IEnumerable<T> data)
              {
                  var memoryStream = new MemoryStream();
                  ExcelPackage.LicenseContext = LicenseContext.Commercial;
      
                  using (var excelPackage = new ExcelPackage(memoryStream))
                  {
                      //To get all members without having EpplusIgnore attribute added
                      MemberInfo[] membersToInclude = typeof(T)
                      .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                      .Where(p => !Attribute.IsDefined(p, typeof(EpplusIgnore)))
                      .ToArray();
      
                      var worksheet = excelPackage.Workbook.Worksheets.Add("Sheet1");
                      worksheet.Cells["A1"].LoadFromCollection(data, true, TableStyles.None, BindingFlags.Instance | BindingFlags.Public,
                      membersToInclude);
                      worksheet.Cells["A1:AN1"].Style.Font.Bold = true;
                      worksheet.DefaultColWidth = 20;
      
                      return excelPackage.GetAsByteArray();
                  }
              }
          }
      

      然后像这样调用这个方法

      public ActionResult ExportToExcel()
              {
                  var result= // Here pass your data (list)
                  byte[] fileResult = EPPlusHelper.GetExportToExcelByteArray(result);
      
                  return File(fileResult, "application/vnd.ms-excel", "FileName.xlsx");
              }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多