Как преобразовать объект, содержащий IList<class>, который дополнительно содержит 6 свойств — 4 строки, 1 IList<anotherclass>, 1 IList<string> в DataTable?

Я пытаюсь преобразовать свой объект, который имеет только 1 элемент (IList класса), этот класс содержит 6 свойств - 4 строки, 1 IList другого класса и 1 IList строки. Я использую ниже общий метод для преобразования: -

public static DataTable ToDataTable<T>(IList<T> items)
    {
        DataTable dataTable = new DataTable(typeof(T).Name);

        //Get all the properties
        PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (PropertyInfo prop in Props)
        {
            //Defining type of data column gives proper data table 
            var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
            //Setting column names as Property names
            dataTable.Columns.Add(prop.Name, type);
        }

        foreach (T item in items)
        {
            var values = new object[Props.Length];
            for (int i = 0; i < Props.Length; i++)
            {
                //inserting property values to datatable rows
                values[i] = Props[i].GetValue(item, null);
            }
            dataTable.Rows.Add(values);
        }
        //put a breakpoint here and check datatable
        return dataTable;
    }

На данный момент - dataTable.Rows.Add(values); У меня есть значения списка для свойств 4 и 5, но когда я добавляю их в строку, в строке отображается текст (тип свойства). Хотя мне нужно, чтобы каждый элемент из 4 и 5 свойств попадал в следующую строку. Например :-

values[0] - "FirstString",
values[1] - "SecondString",
values[2] - "ThirdString",
values[3] - "FourthString",
values[4] - Count 2 - 100, 200 (IList<AnotherClass>()),
values [5] - Count 2 - "10W", "20W" (IList<string>)

Я хочу ниже типа вывода: -

1String     | 2String      | 3String     | 4String      | Per | W  |
:-----------|:------------:|:-----------:|:------------:|:---:|---:|
FirstString | SecondString | ThirdString | FourthString | 100 | 10W|
FirstString | SecondString | ThirdString | FourthString | 200 | 20W|

В то время как результат, который я сейчас получаю: -

1String     | 2String      | 3String     | 4String      | Per            | W                 |
:-----------|:------------:|:-----------:|:------------:|:--------------:|--------------:|
FirstString | SecondString | ThirdString | FourthString | typeofproperty | typeofproperty|

Я просто хочу, чтобы список моих объектов преобразовывался в DataTable.

Ниже приведен результат отладки в соответствии с приведенным ниже решением: -

@Dennis, все еще получаю тип свойства как значение для свойства типа списка.

Сначала вам нужно сгладить ваши объекты, чтобы ваш код заработал. Это будет непросто сделать в общем виде, потому что T может быть чем угодно, так что логика сглаживается. Было бы намного проще, если бы items был готов к конвертации DTO только со скалярными свойствами (я имею в виду без коллекций или других вложенных объектов).

Dennis 11.12.2020 06:54

Вызов этого метода — toDataTable<anotherClass>(IList<anotherClass>). Как я могу сгладить объект?

Ekta C 11.12.2020 07:07

Звонящий должен это сделать. Иначе это будет не банально. Другой вариант — ввести ограничения для структуры T (например, только 2 списка, длина списков одинаковая), но в этом случае конвертер общего назначения не имеет смысла.

Dennis 11.12.2020 07:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
381
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш код отлично работает для T, который содержит только скалярные свойства (то есть примитивные типы, строки, даты и т. д.).
Когда он имеет дело с коллекцией или вложенным объектом, он пытается сохранить этот объект или коллекцию в строке таблицы данных как есть, поэтому результат не такой, как вы ожидали.

Чтобы получить желаемый результат, вам нужно сначала сгладить элементы списка. Поскольку T может быть чем угодно, логика сглаживания не может быть реализована в общем виде. Но вызывающий может сделать это за вас, так как он знает все о структуре объекта.

Наличие таких типов:

class Foo
{
    public string A { get; set; }

    public string B { get; set; }

    public string C { get; set; }

    public string D { get; set; }

    public IList<Bar> Bars { get; set; }

    public IList<string> Strings { get; set; }
}

class Bar
{
    public int A { get; set; }
}

вы можете написать этот код:

var items = new List<Foo>
{
    new Foo
    {
        A = "FirstString",
        B = "SecondString",
        C = "ThirdString",
        D = "FourthString",
        Bars = new List<Bar>
        {
            new Bar { A = 100 },
            new Bar { A = 200 }
        },
        Strings = new List<string>
        {
            "10W",
            "20W"
        }
    }
};

var dataTable = items
    .SelectMany(item => item.Bars.Zip(item.Strings, (bar, stringValue) => new
    {
        BarA = bar.A,
        StringValue = stringValue
    }),
    (item, _) => new
    {
        item.A,
        item.B,
        item.C,
        item.D,
        _.BarA,
        _.StringValue
    })
    .ToDataTable();

Результат:

Как видите, логика сглаживания внутри SelectMany зависит от того, что такое T. Если будет, скажем, 3 вложенных списка и Bar будет содержать свойство public Boo { get; set; }, эта логика изменится, чтобы отразить новую структуру объекта.

P.S. Я немного изменил определение ToDataTable, чтобы сделать его методом расширения для IEnumerable<T>:

static class ConversionExtensions
{
    public static DataTable ToDataTable<T>(this IEnumerable<T> items)
    {
        // your code here
    }
}

Привет, что такое item.Strings и stringValue в примере? item.Bars.Zip(item.Strings, (bar, stringValue) => new { BarA = bar.A, StringValue = stringValue })

Ekta C 11.12.2020 08:36
item.Strings является IList<string> свойством Foo класса. stringValue является элементом item.Strings . Я думаю, вы спрашиваете в основном о методе Zip - он просто делает из двух последовательностей разных типов третью. Буквально составляет последовательность пар (100, "10W"), (200, "20W").
Dennis 11.12.2020 08:44

Да, это работает, но после определения элементов я не могу вызвать метод ToDataTable(). Просят меня определить тип. Вы имеете какое-нибудь представление об этом?

Ekta C 11.12.2020 08:51

@EktaC, ну, я не знаю, как выглядит твой модифицированный код. :) Если вы можете опубликовать или поделиться им, пожалуйста, сделайте это.

Dennis 11.12.2020 08:54

Пожалуйста, посмотрите на приведенный ниже скриншот, предоставленный мной.

Ekta C 11.12.2020 08:59

Определите ToDataTable как метод расширения. Смотрите это: learn.microsoft.com/en-us/dotnet/csharp/programming-guide/…

Dennis 11.12.2020 09:22

Обновлен ответ, чтобы показать, как определить метод расширения, см. класс ConversionExtensions.

Dennis 11.12.2020 09:28

Не могли бы вы поделиться своим методом ToDataTable(), который вы использовали для показанного вывода, пожалуйста? @Деннис

Ekta C 11.12.2020 09:36

@EktaC, на самом деле, это твой код из исходного поста. :) Я только что скопировал/вставил без изменений. Источник: dotnetfiddle.net/1J5O1H

Dennis 11.12.2020 09:39

Убедитесь, что значение снимка экрана не исходит из свойств типа списка. Но количество строк увеличилось в соответствии с количеством элементов.

Ekta C 11.12.2020 09:48

@EktaC, вы передаете item.ToString() в качестве второй последовательности в метод Zip. Следовательно, вы «запаковываете» каждый элемент Per_100 с каждым символом в имени типа SmallType. Я сомневаюсь, что это то, что вы хотите.

Dennis 11.12.2020 09:55

Другие вопросы по теме