Заполнить SelectListItem из универсального типа данных

У меня есть следующий код, который возвращает результаты из таблицы базы данных, состоящей из поля Id и поля Name, и передает их в список SelectListItems (в моем представлении это заполняет раскрывающийся список).

var locationTypes = await APIHelper.GetAsync<List<LocationType>>(url);

var items = new List<SelectListItem>();

items.AddRange(locationTypes.Select(locationType =>
{
    var item = new SelectListItem();
    item.Value = locationType.LocationTypeId.ToString();
    item.Text = locationType.Name;
    return item;
}));

Я часто повторяю это в своем приложении, заменяя LocationType на разные другие вещи. Item.Value всегда получает свойство Id возвращаемых данных (поле Id всегда имеет формат {TableName} + «Id»), а item.Text всегда получает свойство «.Name».

Как я могу сделать это универсальным? Я пытаюсь добиться чего-то вроде этого, хотя это синтаксически неверно и может быть неправильным подходом:

var myGenericObjects = await APIHelper.GetAsync<List<T>>(url)

var items = new List<SelectListItem>();

items.AddRange(myGenericObjects .Select(myGenericObjects =>
{
    var item = new SelectListItem();
    item.Value = myGenericObject.Field[0].ToString();
    item.Text = myGenericObject.Name;
    return item;
}));

Попробуйте var data = locationTypes.ToDictionary(lt => lt.LocationTypeId, lt => lt.Name), а затем используйте new SelectList(data, "Key", "Value").

Mark G 01.05.2018 22:54

Спасибо, @MarkG - но как я могу сделать lt => lt [0] (т.е. первое поле) вместо того, чтобы указывать LocationTypeId

egmfrs 02.05.2018 00:22
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
588
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Вы можете создать собственное расширение для объекта общего списка, а затем с помощью отражения получить значения, которые вы хотите сопоставить с полями SelectListItem.Text и Name. Примечание. Я использую nameof, чтобы избежать путаницы или магических строковых представлений свойств, с которыми я пытаюсь сопоставить.

Я определил значение по умолчанию «Name» для параметра namePropertyName. В соответствии с вашим описанием это звучало так, как будто по соглашению большинство ваших DTO имеют свойство «Имя». Если это не так, просто удалите заданное значение по умолчанию.

Есть дополнительные проверки, которые могут быть выполнены в этом расширении, чтобы предотвратить NullReference и ArgumentExceptions, но для простоты примера они были опущены. Пример. Обеспечение наличия значения в параметрах idPropertyName и namePropertyName и обеспечение наличия этих имен свойств в предоставленном универсальном объекте до преобразования.

public static class ListExtensions
{
    public static List<SelectListItem> ToSelectList<T>(this List<T> list, string idPropertyName, string namePropertyName = "Name")
        where T : class, new()
    {
        List<SelectListItem> selectListItems = new List<SelectListItem>();

        list.ForEach(item =>
        {
            selectListItems.Add(new SelectListItem
            {
                Text = item.GetType().GetProperty(namePropertyName).GetValue(item).ToString(),
                Value = item.GetType().GetProperty(idPropertyName).GetValue(item).ToString()
            });
        });

        return selectListItems;
    }
}

Пример использования:

var testList = new List<TestDto>
{
    new TestDto { Name = "Test0", TestId = 0 },
    new TestDto { Name = "Test1", TestId = 1 },
    new TestDto { Name = "Test2", TestId = 2 },
    new TestDto { Name = "Test3", TestId = 3 },
    new TestDto { Name = "Test4", TestId = 4 },
};

var selectList = testList.ToSelectList(nameof(TestDto.TestId), nameof(TestDto.Name));

Вот класс TestDto для справки:

public class TestDto
{
    public int TestId { get; set; }
    public string Name { get; set; }
}

Это было невероятно полезно, спасибо, любезный сэр! Обратите внимание, что в .Add( ... я изменил текст, чтобы получить имя, и значение, чтобы получить идентификатор, а не наоборот :)

egmfrs 02.05.2018 01:18

Я ... поймал это после того, как опубликовал ... не набралось достаточно очков репутации, чтобы исправить это :-D Я рад, что смог помочь!

bman7716 02.05.2018 01:25

Нет проблем, впечатляющее начало StackOverflow! Я заметил, что код работает без where T : class, new() - в чем его предназначение?

egmfrs 02.05.2018 01:34

Это ограничения типа. «Class» ограничивает «T» типами классов C#, а «new ()» говорит, что для предоставленного класса должен быть определен пустой конструктор. Поскольку большинство Dtos являются простыми классами POCO, можно было безопасно добавить эти ограничения. Вы же не хотите, чтобы кто-то пытался преобразовать List <string> или List <Enum>, если вы знаете, что типы, которые вы пытаетесь преобразовать, являются классами и имеют пустые конструкторы.

bman7716 02.05.2018 01:43

Другими словами ... удаление ограничений типа позволит вам передать любой объект List <T>, и именно поэтому их удаление работает для вас, но открывает дверь для кого-то еще, чтобы случайно неправильно использовать метод расширения для несоответствующих объектов.

bman7716 02.05.2018 01:48

Это помогает с производительностью или просто для того, чтобы попытаться предоставить какую-то встроенную документацию к методу? Конечно, если кто-то попробует такое безумие, они скоро увидят все ошибки, говоря ... нет, это не работает так :) - извините, я пытаюсь понять реальный мир зверя C#

egmfrs 02.05.2018 01:50

@egmfrs Если кто-то попробует такое безумие, он увидит ошибки только во время выполнения. Это дает поддержку времени компиляции, и компилятор остановит их.

CodingYoshi 02.05.2018 02:27

@CodingYoshi прав, и поэтому вы пытаетесь ограничить использование с помощью ограничений типа и / или добавления дополнительных проверок в метод. Отражение может быть немного волшебством, которое люди не часто хотят иметь в своем коде, однако, если его понять и правильно использовать, это очень мощная функция .Net Framework.

bman7716 02.05.2018 03:01

@egmfrs Типовые ограничения существуют только для того, чтобы ограничить доступные типы теми, которые вы хотите разрешить для метода. Прирост производительности за счет удаления ограничений в этом случае ничтожен, а преимущество ограничения метода от неправильного использования намного превышает миллисекунду в производительности, которую вы можете потерять ИМХО.

bman7716 02.05.2018 03:12

Некоторая подготовительная работа

Если вы можете изменить имена столбцов таблицы, используйте соглашение. Например, всегда называйте столбец «Значение» «X», а столбец «Текст» - «Y» (дайте им более удачные имена). Затем сделайте так, чтобы все классы для этих таблиц реализовали интерфейс, подобный этому:

public interface ICustomLookup
{
    string X { get; set; }
    string Y { get; set; }
}

public class SomeClass : ICustomLookup
{
    public string X { get; set; }
    public string Y { get; set; }
}

Затем такой метод расширения:

public static class EnumerableExtension
{
    public static SelectList ToSelectList(this IEnumerable<ICustomLookup> items)
    {
        return new SelectList(items.Select(thisItem => new SelectListItem
        {
            Text = thisItem.X,
            Value = thisItem.Y
        }));
    }
}

Применение

var items = new List<SomeClass>
{
    new SomeClass { X = "XOne", Y = "YOne" },
    new SomeClass { X = "XTwo", Y = "YTwo" }
};
SelectList selectList = items.ToSelectList();

Если бы я мог изменить имена своих столбцов, я бы просто переименовал все идентификаторы в «Id», и тогда мой предложенный код был бы в порядке, если бы я просто использовал myGenericObject.Id вместо myGenericObject.Field[0], с которым я боролся. В любом случае спасибо за ваше время и хотя бы урок.

egmfrs 02.05.2018 01:24

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

CodingYoshi 02.05.2018 01:34

Спасибо - Можно ли добавить интерфейс с int id, а затем унаследовать его в моих классах и установить id => MytableId? Или это безумный разговор. И в чем причина избегать размышлений?

egmfrs 02.05.2018 01:43

@egmfrs Имя столбца таблицы должно совпадать с именем интерфейса. Тогда ваш класс для таблицы автоматически будет удовлетворять интерфейсу. Если они разные, то в реализации интерфейса можно вернуть значение столбца сопоставления. Отражение происходит медленно (медленнее, чем в этом подходе), и ваш проект не будет иметь согласованного дизайна. При таком подходе вы, другие разработчики, пользователи вашего API (если таковые имеются) с легкостью привыкнете к вашему дизайну.

CodingYoshi 02.05.2018 01:52

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