从通用数据类型填充 SelectListItem

Populate SelectListItem from generic data type

我有以下代码,returns 来自数据库 table,包含一个 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总是获取" .名称" 属性.

我怎样才能使它通用?我正在尝试实现这样的目标,尽管它在语法上不正确并且可能是不正确的方法:

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;
}));

您可以为通用列表对象创建自定义扩展,然后使用反射检索要映射到 SelectListItem.Text 和名称字段的值。请注意,我使用 "nameof" 是为了防止我试图映射到的属性的任何混淆或魔术字符串表示。

我确实为 namePropertyName 参数定义了默认值 "Name"。根据您的描述,按照惯例,您的大多数 DTO 中都有 属性 "Name"。如果不是这种情况,只需删除定义的默认值。

可以对此扩展进行额外的检查以防止 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));

这里是TestDtoclass供参考:

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

一些准备工作

如果可以更改 table 列名称,则使用约定。例如,始终将 "Value" 列命名为 "X",将 "Text" 列命名为 "Y"(给它们起更好的名字)。然后让那些 table 的所有 类 实现一个类似这样的接口:

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();