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.IEnumerable
1[System.String]' cannot be used for parameter of type 'System.Collections.Generic.IList
1[System.String]' of method 'System.Collections.Generic.IList1[System.String] PopulateList[String](System.Collections.Generic.IList
1[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?
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:
And it should be noted that using DisplayNameFor
here will cause an exception, which can be avoided by using FirstOrDefault()
.