ASP.Net 从多个表格中筛选下拉列表项

ASP.Net Filter Dropdown list items from multiple tables

总结:

我正在尝试使用两个 DropDownList 控件来筛选当前正在排序并显示在视图中的数据。

我们要学习什么 为可以使用 DropDownList

过滤数据的一对多和多对多关系创建 ViewController

可能的原因 如果我的 DropdownList 代码没有严重错误,那么我用来显示数据的 ViewModel 对 DropDownList 项目没有适当的支持。

换句话说,RazorView 和我的 ViewModel 与我想要实现的目标不兼容。如果我尝试更改我的 ViewModel 或 RazorView,我会收到现有代码的无限错误循环。

或者 Linq 查询需要专家的关注

这里是FilterViewModel.cs

    public IEnumerable <App>      Apps { get; set; }
    public IEnumerable <Language> Languages { get; set; }
    public IEnumerable <Platform> Platforms { get; set; }
    public IEnumerable <AgeGroup> AgeGroups { get; set; }
    public IEnumerable <ProductCode> ProductCodes { get; set; }

这里是AppsController.cs

 public ActionResult FilterApps(App app)
    {
        var apps = _context.Apps.ToList();
        var languages = _context.Languages.ToList();
        var productCodes = _context.ProductCodes.ToList();
        var platforms = _context.Platforms.ToList();
        var ageGroups = _context.AgeGroups.ToList();
        var viewModel = new FilterViewModel
        {
            AgeGroups = ageGroups,
            Languages = languages,
            Platforms = platforms,
            ProductCodes = productCodes,
            Apps = apps
            .Where(a => a.LanguageId == app.LanguageId && a.PlatformId == app.PlatformId)
     // I also tried all possible combinations :(a.Lanage.Id etc)
        };
        return View("FilterApps", viewModel);
    }

这里是FilterApps.cshtml

@model Marketing.ViewModels.FilterViewModel
<h2>FilterApps</h2>
@using (Html.BeginForm("FilterApps", "Apps", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<div class="form-group">

    @Html.DropDownListFor( m => m.Languages,
      new SelectList(Model.Languages, "Id", "Name"),"Select Language",
      new { @class = "form-control", @id = "dropDown" })

    @Html.DropDownListFor(m => m.Platforms, 
      new SelectList(Model.Platforms, "Id", "Name"), "Select Platform",
      new { @onchange = "this.form.submit();",
      @class = "form-control", @id = "dropDown" })

</div>
}

//The existing code below is working fine so far.
@foreach (var group in Model.AgeGroups)
  {
   <h4>@group.Name</h4>

     @foreach (var app in Model.Apps.OrderBy(a => a.AppOrder))
            {
                if (app.AgeGroupId == group.Id)
                {
                 @app.ProductCode.Name
                 @app.Name
                 @app.Platform.Name
                }
             }
   }

可能没有必要,但我希望额外的信息能有所帮助。

附加信息

App.cs 正在引用所有其他表,例如

public Language Language { get; set; }
public int LanguageId { get; set; }

public Platform Platform { get; set; }
public int PlatformId { get; set; } 
and so on... 

我已经尝试过的 几个断点和日志来跟踪数据,我也尝试使用以下但它破坏了我现有的排序和分组。

public App App { get; set; } //Instead of the IEnumerable<App> 

您的代码存在多个问题。

首先,您不能将 <select> 元素绑定到复杂对象的集合。 <select> posts 返回其 selected 选项的值(这将是一个 int 假设 Id 属性 为 Languageint).

接下来模型中的视图是 FilterViewModel(并且您生成的表单控件具有基于这些属性的 name 属性),但是您 post 返回到不同的模型( App) 不包含这些属性,因此无论如何都不会绑定。

您添加了一个 null 标签选项 ("Select Language"),如果它被 select 编辑,它将 post 一个 null 值,这会导致您的查询失败。

还有一些不好的做法,我在下面指出。

你的视图模型应该是

public class AppsFilterVM
{
    public int? Language { get; set; }
    public int? Platform { get; set; }
    public IEnumerable<SelectListItem> LanguageOptions { get; set; }
    public IEnumerable<SelectListItem> PlatformOptions { get; set; }
    ...
    public IEnumerable <App> Apps { get; set; }
}

不清楚 AgeGroupsProductCodes 的用途,所以我在上面的代码中省略了它们,根据您的评论,我假设用户可以通过 LanguagePlatform 或两者

控制器代码为

public ActionResult FilterApps(AppsFilterVM model)
{
    var apps = _context.Apps;
    if (model.Language.HasValue)
    {
        apps = apps.Where(x => x.LanguageId == model.Language.Value);
    }
    if (model.Platform.HasValue)
    {
        apps = apps.Where(x => x.PlatformId == model.Platform.Value);
    }
    model.Apps = apps;
    ConfigureViewModel(model);
    return View(model);
}

private void ConfigureViewModel(AppsFilterVM model)
{
    // populate the selectlists
    var languages = _context.Languages;
    var platforms = _context.Platforms
    model.LanguageOptions = new SelectList(languages, "Id", "Name");
    model.PlatformOptions = new SelectList(platforms , "Id", "Name");
}

然后在视图中(注意它是 GET,而不是 POST)

@model.AppsFilterVM
....
@using (Html.BeginForm("FilterApps", "Apps", FormMethod.Get))
{
    @Html.LabelFor(m => m.Language)
    @Html.DropdownListFor(m => m.Language, Model.LanguageOptions, "No filter")
    @Html.ValidationMessageFor(m => m.Language) 
    @Html.LabelFor(m => m.Platform)
    @Html.DropdownListFor(m => m.Platform, Model.PlatformOptions, "No filter")
    @Html.ValidationMessageFor(m => m.Platform) 
    <input type="submit" value="Filter" />
}
@foreach (var group in Model.AgeGroups)
{
    ....

还有一些您不应该做的事情。您给两个 <select> 元素相同的 id 属性,该属性无效 html(DropDownListFor() 方法已经基于 属性 生成了一个唯一的 id姓名)。

您不应基于 <select>change 事件提交表单 这不仅是意外行为,如果用户使用键盘浏览选项(例如使用箭头键, 或键入一个字符以转到以该字母开头的第一个选项,然后将立即提交表单。此外,用户可能会首先 select 从第二个下拉列表中选择一个选项,这将立即 post 在他们有机会 select 第一个选项之前。允许用户制作他们的 select 离子,检查它们,然后在他们选择时提交表格。

您的视图不应包含 linq 查询,并且您的分组和排序应在将模型传递给视图之前在控制器中完成。你的 Apps 属性 实际上应该是一个视图模型,其中包含一个 属性 作为组名,一个集合 属性 作为应用程序,(类似于你的视图模型) 这样视图就是

@foreach(var group in Model.AgeGroups)
{
    @group.Name
    foreach (var app in group.Apps)
    {
        @app.ProductCode
        @app.Name
        @app.Platform
    }
}

您还应该考虑使用 ajax 提交您的表单,这将调用单独的服务器方法 returns 只是 Apps 的部分视图,并更新 DOM 在成功回调中,这将提高性能。例如,参考 .