3

I have about 10 very simple tables in database:

create table EmploymentStatus
(
    Id int identity(1,1) Primary Key,
    Name nvarchar(100) NOT NULL
)

These tables are used for DropDownLists on registration view, like this:

@Html.DropDownListFor(m => m.EmploymentStatus, Model.EmploymentStatusList, Resource.EmploymentStatus, new { @class = "form form-control" })

DropDownListFor requires a IEnumerable<SelectListItem> selectList as a collection. So, I created a method:

public static List<SelectListItem> ToSelectItemList(IQueryable<EmploymentStatu> statuses)
{
    return statuses.Select(m => new SelectListItem
    {
        Text = m.Name,
        Value = m.Id.ToString()
    }).ToList();
}

The problem is that I have to create a method for each of ten entities. Is there a good way of writing some generic method, like this:

public static List<SelectListItem> ToSelectItemList(IQueryable<T> collection)
{
    return collection.Select(m => new SelectListItem
    {
        Text = m.Name,
        Value = m.Id.ToString()
    }).ToList();
}
jsanalytics
  • 13,058
  • 4
  • 22
  • 43
kir.gera
  • 192
  • 1
  • 12

3 Answers3

8

You can define an interface for all entities that you are going to use for DropDownLists like

public interface IDropDownItem
{
   int Id {get; set;}
   string Name {get; set;}
}

then

public static List<SelectListItem> ToSelectItemList<T>(IEnumerable<T> collection) 
   where T : IDropDownItem
{
    return collection.Select(m => new SelectListItem
    {
        Text = m.Name,
        Value = m.Id.ToString()
    }).ToList();
}

It would be better to make ToSelectItemList as extension method:

public static class EnumerableExtensions
{
    public static List<SelectListItem> ToSelectItemList<T>(this IEnumerable<T> collection)
        where T : Program
    {
        return collection.Select(m => new SelectListItem
        {
            Text = m.Name,
            Value = m.Id.ToString()
        }).ToList();
    }
}
Roman Marusyk
  • 23,328
  • 24
  • 73
  • 116
  • ok, should i inherit this interface in entity class? Because every time I will update my model from db this will be missed – kir.gera Nov 28 '17 at 13:29
  • 1
    the approach using delegate is more better in my opinion as suggested by @Titian – Ehsan Sajjad Nov 28 '17 at 13:30
  • @kir.gera yes you should implement the interface. His solution has a flaw though, you should change `IQueryable` to `IEnumerable` otherwise you will get an error. You can't do LINQ to EF on interfaces, you can do it on an `IEnumerable` as that will get handled in memory and no SQL has to be genearetd – Titian Cernicova-Dragomir Nov 28 '17 at 13:36
  • Also I would have made the method an extension method (and I did in my answer) – Titian Cernicova-Dragomir Nov 28 '17 at 13:38
  • @kir.gera Yes, you should. It is additional work but you will get a nice code where you're going to call it `m => m.EmploymentStatus.ToSelectItemList()`. And it makes that method more testable. As I write before (and Titian) it would be better to make it as an extension method. See updates – Roman Marusyk Nov 28 '17 at 14:49
6

There are two options:

If you can implement an interface on the entity

public interface ISelectable
{
    string Name { get; }
    int Id { get; }
}
public static List<SelectListItem> ToSelectItemList<T>(this IEnumerable<T> collection)
    where T: ISelectable
{
    return collection.Select(m => new SelectListItem
    {
        Text = m.Name,
        Value = m.Id.ToString()
    }).ToList();
}

Using delegates

public static List<SelectListItem> ToSelectItemList<T>(this IEnumerable<T> collection, Func<T, string> nameGetter, Func<T, int> idGetter)
{
    return collection.Select(m => new SelectListItem
    {
        Text = nameGetter(m),
        Value = idGetter(m).ToString()
    }).ToList();
}

Usage :

m.EmploymentStatus.ToSelectItemList(e => e.Name, e => e.Id);

The second option is more verbose to use, but you get a ton more flexibility, since you don't have to clutter your data model with useless interface implementation, and you are free to use any property names for Name or Id

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
3

DropDownListFor requires a IEnumerable<SelectListItem> selectList as a collection

There's already a class for that, namely SelectList:

var selectListItems = new SelectList(items, "Id", "Name");

No need for new extension methods whatsoever. I thought SelectList() also has an overload with expressions for the key/value members instead of strings, but I may have found that somewhere else.

See also ASP.NET MVC Dropdown List From SelectList.

CodeCaster
  • 147,647
  • 23
  • 218
  • 272
  • 1
    I didn't know about the class, generally it is better to use built-in classes, however, I don't think properties as magic uncheckable strings is a great idea, would still prefer a more type safe option. – Titian Cernicova-Dragomir Nov 28 '17 at 13:41