Генерация динамических объектов с использованием ExpandoObject

· 4 мин чтения

Мне нужно было преобразовать файлы Excel с собственными определенными столбцами в новый файл с динамическими столбцами, и я был немного озадачен тем, как это сделать правильно, без каких-либо серьезных проблем с производительностью.

Я подошел к руководству, которое вы можете найти здесь, в котором используется .NET ExpandoObject, который в значительной степени позволяет вам создавать объект и добавлять динамических участников.

РасширяемыйОбъект

Определение Microsoft следующее:

Представляет объект, члены которого можно динамически добавлять и удалять во время выполнения.

И есть несколько замечаний:

Класс ExpandoObject позволяет добавлять и удалять члены его экземпляров во время выполнения, а также устанавливать и получать значения этих членов. Этот класс поддерживает динамическую привязку, что позволяет использовать стандартный синтаксис, например sampleObject.sampleMember, вместо более сложного синтаксиса, такого как sampleObject.GetAttribute(“sampleMember”).

Текущее поведение

У нас есть ExcelExportService, который, передав List<T>, в данном случае ExcelItem, будет использовать Reflection для создания файла xlsx.

Пока что наш код выглядит так:

[[[ТОК_7]]]

ExcelItem — это объект со всеми свойствами, которые используются для создания файла Excel.

[[[ТОК_9]]]

Этот подход работает отлично, файл xlsx генерируется без проблем, причем каждый столбец является каждым свойством, вы можете использовать этот метод GetExcelBytes следующим образом, например, чтобы сохранить его в файл:

[[[ТОК_12]]]

Проблема

Поскольку мы правильно знаем, что используем все свойства объекта ExcelItem , все, что я хочу, это на самом деле не использовать их все, а просто использовать, может быть, 2 или 3 из них.

Также обязательным условием является то, что я не хочу менять код, все должен делать administrator, который будет решать, какие столбцы следует отображать, и он может знать или не знать свойства в коде.

TLDR: все должно быть динамическим, у нас есть объект со свойствами, и мы должны убедиться, что сгенерированный файл имеет свойства N этого объекта, но явно не жестко закодирован.

Динамические столбцы

Изменение будет заключаться в использовании объекта dynamic, поскольку мы хотели бы установить, какие свойства объекта будут использоваться для создания списка столбцов.

Допустим, у нас есть объект с множеством свойств, например, тонной, и мы на самом деле не хотим изменять объект ExcelItem каждый раз, когда мы вносим изменения, мы создаем таблицу ColumnExcelItem, которая будет использоваться для создания этого ExcelItem.

Структура базы данных

Мы сохраняем это определение в нашей базе данных примерно так:

Id    PropertyName
---------------------
1     Name
2     Surname
3     Age

Обратите внимание, что у нас меньше значений, чем у существующих свойств объекта ExcelItem: он имеет свойства 6 и в базе данных указан только 3.

Также обратите внимание, что PropertyName должно соответствовать имени свойства объекта, который я буду использовать для динамического назначения.

Построение объекта

Теперь, когда у нас есть таблицы базы данных, нам нужно создать репозиторий, чтобы получить их и иметь возможность использовать их в коде.Я не буду делать руководство по этой части, я пропущу начало новой функции GetExcelBytes().

public static byte[] GetExcelBytes(List<string> columns) {
    List<ExcelItem> excelItems = new List<ExcelItem>();
    List<dynamic> objectsToExcel = new List<dynamic>();
    ExcelExportService exportService = new ExcelExportService();

    foreach (var item in itemsToExcel)
    {
        dynamic newObject = new ExpandoObject();
        foreach (var col in columns)
        {
            this.AddProperty(newObject, col, product.GetType().GetProperty(col).GetValue(item, null));
        }

        objectsToExcel.Add(newObject);
    }

    return exportService.ExportToExcel(objectsToExcel);
}

Теперь нам предстоит создать объект с некоторыми свойствами ExcelItem, но полностью динамический. Вместо использования всех шести свойств мы используем только три из них — то, которое хотим отправить.

Теперь давайте представим, что этот объект ExcelItem имеет 200 свойств, сумасшедший, но такое может случиться.

Единственное, что мне нужно сделать, это вставить те свойства, которые я хочу отобразить, в файл Excel в эту таблицу ColumnExcelItem и все.

Случаев

В данном случае мы использовали только один случай, но предположим, что вы хотите иметь разные отчеты для некоторых пользователей. Допустим, роль admin должна получить все свойства, тогда вы создадите в базе данных что-то вроде этой структуры.

ReportingTemplates

Id    PropertyName
---------------------
1     Admin
2     Users
ReportingTemplateItems

Id    TemplateId    PropertyName
---------------------
1     1             Id
2     1             Name
3     1             Surname
4     1             Age
5     1             CreatedAt
6     1             UpdatedAt
7     2             Name
8     2             Surname
9     2             Age

Затем, получив шаблон, вы сможете иметь разные столбцы, все динамически и без привязки к коду.

Если у нас есть пользователь с ролью Admins, файл будет содержать столбцы Id, Name, Surname, Age, CreatedAt, UpdatedAt.

В случае, если вы генерируете его с использованием роли Users, он будет иметь Name, Age. Surname.

Заключение

Это отличный способ узнать, как dynamic работает в .NET, и это действительно хорошее решение, когда вам нужно иметь разные роли или шаблоны для разных пользователей и вы не особо заинтересованы в том, чтобы тратить время на жесткое программирование.