Search code examples
c#asp.net-coreasp.net-core-mvc.net-8.0pagedlist

Display Multi-select dropdown list in .NET 8


I'm new to .NET 8 and I've build a form using MVC, EF and X.PagedList. My form has a multi-select drop-down list. I'm able to save the form to database. The Multi-select drop-down list is saved to database like this ["abc", "xyz"].

Here is my model:

public partial class Bat
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid Id { get; set; }
    
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public IEnumerable<string>? MyMultiSelectList { get; set; } = null!; 
}

But when I go to view the Index page, I get an error at the IQueryable line below:

ArgumentException: Expression of type 'System.Collections.Generic.IEnumerable1[System.String]' cannot be used for parameter of type 'System.Collections.Generic.IList1[System.String]' of method 'System.Collections.Generic.IList1[System.String] PopulateList[String](System.Collections.Generic.IList1[System.String], System.Collections.Generic.IList`1[System.String])' (Parameter 'arg0').

Here is my Index method code:

public IActionResult Index(int? page)
{
    IQueryable<Bat> Bats = _context.Bats.AsQueryable();

    int pageSize = 10;
    int pageNumber = (page ?? 1);

    return View(Bats.ToPagedList(pageNumber, pageSize));
}

Here is the code of my Index page:

@using X.PagedList
@using X.PagedList.Mvc.Core
@model X.PagedList.IPagedList<QII.Models.Initiative>

<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.FirstName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.MyMultiSelectList)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.FirstName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.MyMultiSelectList)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
    }
    </tbody>
</table>

If I change the

public IEnumerable<string>? MyMultiSelectList { get; set; } = null!;

to

public string? MyMultiSelectList { get; set; } = null!;

error goes away but only first selected option 'abc' is stored in database.

Can someone tell me how to display this multi-select drop-down list as string like abc,xyz on my Index page and get rid of this error?


Solution

  • Since the IEnumerable type cannot be directly bound to the view. If you want to display the value in the view, you can convert the IEnumerable to a string, create a viewmodel to contain the converted string value, and then in the controller's Index method, convert the Bat instance to a BatViewModel instance, and use the string.Join() method to convert the IEnumerable<string> to a string, after that bind the BatViewModel in the view.The following is an example for your reference.

    BatViewModel:

    public class BatViewModel
    {
         public Guid Id { get; set; }
    
           
         public string? FirstName { get; set; }
            
         public string? LastName { get; set; }
    
           
         public string? MyMultiSelectList { get; set; } 
     }
    

    Controller:

    public IActionResult Index(int? page)
    {
         IQueryable<Bat> Bats = _context.Bats.AsQueryable();
         int pageSize = 10;
         int pageNumber = (page ?? 1);
    
         var batViewModels = Bats
      .Select(b => new BatViewModel
      {
          Id = b.Id,
          FirstName = b.FirstName,
          LastName = b.LastName,
          MyMultiSelectList = b.MyMultiSelectList != null ? string.Join(",", b.MyMultiSelectList) : string.Empty
      });
    
         var pagedList = batViewModels.ToPagedList(pageNumber, pageSize);
    
         return View(pagedList);
    }
    

    View:

    @model X.PagedList.IPagedList<BatViewModel>
    @using X.PagedList.Mvc.Core
    
    <table class="table">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.FirstOrDefault().FirstName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstOrDefault().LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstOrDefault().MyMultiSelectList)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
        @foreach (var item in Model) {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.MyMultiSelectList)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
                </td>
            </tr>
        }
        </tbody>
    </table>
    

    MyDbContext:

    public class MyDbContext : DbContext
    {
        public MyDbContext(DbContextOptions<MyDbContext> options)
            : base(options)
        {
        }
    
        public DbSet<Bat> Bats { get; set; }
    
            
    }
    

    Result:

    enter image description here

    And it should be noted that using DisplayNameFor here will cause an exception, which can be avoided by using FirstOrDefault().