epplus using LoadFromCollection with anonymous types

左心房为你撑大大i 提交于 2019-12-12 10:41:16

问题


I have a IEnumerable<object> dataSource which contains a collection anonymous types. The actual structure of the anonymous type won't be known at design time, so I'm trying to find a generic solution that can handle any anonymous type.

How can I load them into epplus to create a spreadsheet? I have a worksheet called ws and I tried:

ws.Cells["A1"].LoadFromCollection(dataSource, true);

However when that runs it outputs all of the anonymous type's properties into a single cell:

{ Id = 10000, Title = This is a test }

I've tried passing in MemberInfo using:

var members = dataSource.First().GetType().GetMembers();
ws.Cells["A1"].LoadFromCollection(this._dataSource, true,
    TableStyles.Medium1, BindingFlags.Public, members);

But that throws an exception:

Supplied properties in parameter Properties must be of the same type as T

Any suggestions on how I can create a spreadsheet using anonymous types in c#?


回答1:


You could group the anonymous types to make it easier for exporting with dataTables. The bug "Supplied properties in parameter Properties must be of the same type as T" is still there and a workaround is using DataTables.

// Imagine list is your main datasource
IEnumerable<object> list = Enumerable.Empty<object>(); // Data Source of <object>

// Added anon types at runtime added to the object list
var anonTypesOne = new object[] 
{ 
    new { GuidID = Guid.NewGuid(), StringProperty = "the string property" },
    new { IntegerID = 1, IntegerProperty = 99 }
};

var anonTypesTwo = new object[]
{
    new { StringID = "1", BooleanProperty = true, NumberProperty = 3, StringProperty = "Four" },
    new { GuidID = Guid.NewGuid(), NumberThree = 3 },
    new { GuidID = Guid.NewGuid(), NumberThree = 3 },
    new { GuidID = Guid.NewGuid(), NumberThree = 3 }
};

list = list.Concat(anonTypesOne).Concat(anonTypesTwo);

// Grouping works on anon types so we can group the export into their own tables
var groupings = list.GroupBy(i => i.GetType());

using(var package = new ExcelPackage(new FileInfo("C:\\Temp\\Anon.xlsx")))
{
    var ws = package.Workbook.Worksheets.Add("Anonymous Types");

    // add each "anon type matched grouping"
    foreach(var grouping in groupings)
    {
        var isNew = ws.Dimension == null; // the sheet is empty if Dimension is null.
        var row = 0;

        if(isNew)
        {
            row = 1; // start from the first row
        }
        else 
        {       
            // otherwise there are tables already, start from the bottom
            row = ws.Dimension.End.Row; 
        }       

        // because of EPP inheritance bug of T, we can just use dataTable
        DataTable dt = new DataTable(grouping.Key.Name);
        var properties = grouping.Key.GetProperties(); // Get anon type Properties

        foreach(var property in properties)
        {
            dt.Columns.Add(property.Name);
        }

        foreach(var item in grouping.ToList())
        {
            var dataRow = dt.NewRow();

            foreach(var p in properties) // populate a single row
            {
                dataRow[p.Name] = p.GetValue(item); // item is anon object instance
            }

            dt.Rows.Add(dataRow);
        }

        if(isNew) // load into the top most left cell of the worksheet
            ws.Cells[1, 1].LoadFromDataTable(dt, PrintHeaders: true);
        else // load from the dimension of current items + 1 row for spacing
            ws.Cells[ws.Dimension.End.Row + 1, 1].LoadFromDataTable(dt, PrintHeaders: true);

        ws.InsertRow(ws.Dimension.End.Row + 2, 5); // Insert some padding between each group

    }

    package.Save();
}



回答2:


I have tested

using (var excel = new OfficeOpenXml.ExcelPackage())
{
    var sheet = excel.Workbook.Worksheets.Add("Test");
    sheet.Cells["A1"].LoadFromCollection(dataSource, true);
    excel.SaveAs(new FileInfo(@"C:\Temp\Test.xlsx"));
}

with this sample data:

var dataSource = Enumerable.Range(1, 100).Select(i => new{ ID=i, Title="Title " + i });

It works fine. It creates two columns with the correct headers and 100 rows.

But you should use anonymous types only if you know the structure at compile time.

You could use a DataTable and LoadFromDataTable instead. Since i don't know how you create the anonymous type i show you just a small sample:

DataTable dataSource = new DataTable();
dataSource.Columns.Add("Id");    // default type is string 
dataSource.Columns.Add("Title");  
// add other columns
dataSource.Rows.Add("1", "Title1");
// add other rows

using (var excel = new OfficeOpenXml.ExcelPackage())
{
    var sheet = excel.Workbook.Worksheets.Add("Test");
    sheet.Cells["A1"].LoadFromDataTable(dataSource, true);
    excel.SaveAs(new FileInfo(@"C:\Temp\Test.xlsx"));
}



回答3:


I was, this thread is older, but I'm looking for the same problem. With the following code (VB) I have success. Carsten

    Dim targetFile = New IO.FileInfo(sFN)
    Dim dataSource = Enumerable.Range(0, 1).Select(Function(i) New With {.ID = 1000, .Titel = "This is a test "}).ToList
    Using epp = New OfficeOpenXml.ExcelPackage(targetFile)
        Dim ws = epp.Workbook.Worksheets.Add("lst_Anonymous")
        ws.Cells(1, 1).LoadFromCollection(dataSource, True,
                                               OfficeOpenXml.Table.TableStyles.Medium1,
                                               Reflection.BindingFlags.Public,
                                               dataSource.GetType.GetGenericArguments()(0).GetProperties)
                    epp.Save()
    End Using


来源:https://stackoverflow.com/questions/24534207/epplus-using-loadfromcollection-with-anonymous-types

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!