#c# #epplus
#c# #epplus
Вопрос:
У меня есть 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);
Но это создает исключение:
Указанные свойства в свойствах параметра должны быть того же типа, что и T
Есть предложения о том, как я могу создать электронную таблицу с использованием анонимных типов в c #?
Комментарии:
1. Покажите код, в котором вы инициализируете
dataSource
2. На самом деле вам вообще не следует использовать анонимные типы здесь. Они предназначены для статического использования, когда их структура известна во время компиляции. Вам лучше создать что-то другое, чем анонимные типы, которые предназначены для использования, когда схема не известна статически.
3. @TimSchmelter Код, используемый для инициализации, будет отличаться, но простым примером может быть что-то вроде:
ReportData = new ProductRepository(this.DbContext).AllItems.Where(p => p.IsStocked).Select(p => new {p.Id, p.Title }).ToList();
Извините, форматирование немного запутанное.
Ответ №1:
Я протестировал
using (var excel = new OfficeOpenXml.ExcelPackage())
{
var sheet = excel.Workbook.Worksheets.Add("Test");
sheet.Cells["A1"].LoadFromCollection(dataSource, true);
excel.SaveAs(new FileInfo(@"C:TempTest.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:TempTest.xlsx"));
}
Комментарии:
1. Это началось с таблиц данных, но код стал немного больше по размеру для более сложной таблицы данных. Итак, были опробованы альтернативные анонимные типы, и то, что, как я думал, должно было сработать, не сработало, вывод свойств объекта в виде одной ячейки. Я собираюсь попробовать тест с использованием ExpandoObject, если это не сработает, он вернется к DataTable. Большое спасибо за вашу помощь.
2. Использование анонимного типа работает очень хорошо, если IEnumerable<a> представляет собой коллекцию того же типа, a.
Ответ №2:
Вы могли бы сгруппировать анонимные типы, чтобы упростить экспорт с таблицами данных. Ошибка «Предоставленные свойства в свойствах параметров должны быть того же типа, что и T» все еще существует, и обходной путь заключается в использовании таблиц данных.
// 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();
}
Комментарии:
1. теперь это кажется очевидным, я вижу это! Я использовал слегка измененную версию этого, все анонимные типы в перечислимой коллекции будут иметь одинаковые свойства. Я также добавил PropertyType при создании столбца, чтобы позже я мог форматировать столбцы электронной таблицы.
Ответ №3:
Я был, этот поток старше, но я ищу ту же проблему. Со следующим кодом (VB) у меня есть успех. 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