epplus использует LoadFromCollection с анонимными типами

У меня есть источник данных IEnumerable<object>, который содержит коллекцию анонимных типов. Фактическая структура анонимного типа не будет известна во время разработки, поэтому я пытаюсь найти универсальное решение, которое может обрабатывать любой анонимный тип.

Как я могу загрузить их в 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);

Но это вызывает исключение:

Предоставленные свойства в параметре Properties должны быть того же типа, что и T.

Любые предложения о том, как я могу создать электронную таблицу, используя анонимные типы в С#?


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)


Вы можете сгруппировать анонимные типы, чтобы упростить экспорт с помощью таблиц данных. Ошибка "Предоставляемые свойства в параметрах Properties должны быть тот же тип, что и 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