epplus използва LoadFromCollection с анонимни типове

Имам IEnumerable<object> dataSource, който съдържа колекция от анонимни типове. Действителната структура на анонимния тип няма да бъде известна по време на проектиране, така че се опитвам да намеря общо решение, което може да се справи с всеки анонимен тип.

Как мога да ги заредя в epplus, за да създам електронна таблица? Имам работен лист, наречен ws, и опитах:

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

Въпреки това, когато това се изпълнява, извежда всички свойства на анонимния тип в една клетка:

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

Опитах да предам MemberInfo, използвайки:

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

Но това хвърля изключение:

Предоставените свойства в параметър Свойства трябва да са от същия тип като T

Някакви предложения как мога да създам електронна таблица, използвайки анонимни типове в c#?


person Tony    schedule 02.07.2014    source източник
comment
Покажете кода, където инициализирате dataSource   -  person Tim Schmelter    schedule 02.07.2014
comment
Наистина изобщо не трябва да използвате анонимни типове тук. Те са проектирани да се използват статично, когато тяхната структура е известна по време на компилиране. По-добре е да конструирате нещо различно от анонимни типове, което е предназначено за използване, когато схемата не е известна статично.   -  person Servy    schedule 02.07.2014
comment
@TimSchmelter Кодът, използван за инициализиране, ще варира, но прост пример би бил нещо като: ReportData = new ProductRepository(this.DbContext).AllItems.Where(p => p.IsStocked).Select(p => new {p.Id, p.Title }).ToList(); Съжаляваме, че форматирането е малко объркано.   -  person Tony    schedule 03.07.2014


Отговори (3)


Можете да групирате анонимните типове, за да улесните експортирането с dataTables. Грешката „Доставените свойства в параметъра Свойствата трябва да са от същия тип като T" все още е там и заобиколно решение е използването на 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();
}
person Patrick Magee    schedule 03.07.2014
comment
изглежда очевидно сега го виждам! Използвах леко модифицирана версия на това, всички анонимни типове в изброимата колекция ще имат едни и същи свойства. Също така добавих PropertyType в създаването на колона, за да мога да форматирам колоните на електронната таблица по-късно. - person Tony; 04.07.2014

Тествах

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

с тези примерни данни:

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

Работи добре. Създава две колони с правилните заглавки и 100 реда.

Но трябва да използвате анонимни типове само ако знаете структурата по време на компилиране.

Вместо това можете да използвате DataTable и LoadFromDataTable. Тъй като не знам как създавате анонимния тип, ви показвам само малка извадка:

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"));
}
person Tim Schmelter    schedule 02.07.2014
comment
Това започна с DataTables, но кодът ставаше малко по-голям за по-сложния DataTable. Така че търсенето на алтернативни анонимни типове беше изпробвано и това, което смятах, че трябваше да работи, не успя, извеждайки свойствата на обекта като една клетка. Ще опитам тест с помощта на ExpandoObject, ако това не работи, ще се върна към DataTable. Много благодаря за помощта. - person Tony; 03.07.2014
comment
Използването на анонимен тип работи много добре, ако IEnumerable‹a› е колекция от същия тип, a. - person subsci; 21.12.2017

Бях, тази тема е по-стара, но търся същия проблем. Със следния код (VB) имам успех. Карстен

    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
person C. Sekulla    schedule 19.03.2017