使用ViewBag填充DropDownList的替代方法是什么?

时间:2016-05-10 14:55:56

标签: c# asp.net-mvc

我已经读过ViewBags很糟糕,如果可能的话应该避免,但是我看到的每个训练片(包括MVC 5 tutorial和许多SO答案)都显示了下面显示的样式来填充DropDownList风景。

如果它是一个或两个参数,它并不明显,但客户要求非常具体的东西:一个怪物大小的表,有20个独立的独立参数用于过滤数据库查询。我向你展示的例子是高度减少的。想象一下,有更多的参数,可以想象指数行动将如何变得非常肥胖而不是简单。

我可以使用#region块来折叠Action的各个部分,这可以帮助组织和可读性,但我想知道是否有更好的方法来执行此操作。

这就是一个大问题:使用ViewBags从Controller中填充DropDownLists有什么替代方法吗?

摘自控制器中的索引操作:

public ActionResult Index(string campus = "CRA", string fy = "FY16", int? ageYr)
{                       
    // Used to populate the DropDownLists in the HTML.BeginForm() block.
    ViewBag.CampusList = new SelectList(new List<string> { "CRA", "DRA", "MRA", "PRA" }, campus);
    ViewBag.FyList = new SelectList(new List<string> { "FY12", "FY13", "FY14", "FY15", "FY16" }, fy);                        

    // Optional Filter
    List<int?> ages = query.Select(m => (int?)m.AgeYrAtEndOfSy).Distinct().OrderBy(m => m).ToList();
    ages.Insert(0, null);
    ViewBag.AgeYrList = new SelectList(ages, ageYr);

    IEnumerable<StudentSummaryLayer0> query = db.StudentSummaryLayer0
        .Where(m => m.Campus == campus).Where(m => m.FY == fy);

    if (ageYr != null) query = query.Where(m => m.AgeYrAtEndOfSy == ageYr);            

    query = query.OrderBy(m => m.StudentName);

    return View(query.ToList());
}

摘自索引视图:

@model IEnumerable<RoseDashboardFY16.Models.StudentSummaryLayer0>

@using (Html.BeginForm("Index", "StudentSummaryLayer0", FormMethod.Post))
{
    <div class="panel panel-default">
        <div class="panel-heading">Filter results</div>
        <div class="panel-body">
            <p>
                Required Filters: <br />
                <strong>Campus</strong>: @Html.DropDownList("campus", (SelectList)ViewBag.CampusList)
                <strong>FY</strong>: @Html.DropDownList("fy", (SelectList)ViewBag.FyList)
            </p>

            @*Imagine many more parameters.*@
            <p style="line-height:2.0">
                Optional Attribute Filters: <br />                
                <span class="KeepTogether">
                    <strong>Age (Yr)</strong>: @Html.DropDownList("ageYr", (SelectList)ViewBag.AgeYrList)
                </span>
            </p>

            <div><input type="submit" value="Search" /><input type="reset" value="Reset"></div>
        </div>
    </div>
}

2 个答案:

答案 0 :(得分:1)

正如其他人所说,首选方法是使用视图模型。 ViewBag的一个大问题以及为什么不使用它是因为它是动态的。您失去了类型安全性并被迫进行手动铸造,这可能导致错误。此外,由于在视图中完成了转换,因此您会得到 runtime 错误,这些错误就像代码中的小时间炸弹一样。理想情况下,您希望在编译时出错,以便在部署之前修复它们。视图模型允许您保持强类型,并且如果它将完全中断,它将在编译时中断。

为了保持您的操作干净,您的选择列表的构建应该在您的视图模型中或在控制器上的私有/受保护方法中处理(如果它需要数据库访问)。这样,您可以轻松地在其他地方重复使用相同的构造。例如,使用动作的GET和POST版本:

public ActionResult Foo()
{
    ...

    PopulateSelectLists(model);
    return View(model);
}

[HttpPost]
public ActionResult Foo(FooViewModel model)
{
    ...

    PopulateSelectLists(model);
    return View(model);
}

private void PopulateSelectLists(FooViewModel model)
{
    // set select list properties
}

此外,您希望避免新增SelectList个实例。所有Razor需求都是IEnumerable<SelectListItem>。它将自动创建SelectList实例,并根据模型的状态设置相应项目的Selected属性。

答案 1 :(得分:0)

有很多方法可以填充下拉列表。我从不使用view bags。下面是关于我通常如何填充下拉列表的代码。

在我的示例中,我将填充包含状态列表的下拉列表。当我的view包含项目列表而没有其他内容时,我可以将列表传递给视图,而不是view model。如果有更多数据需要传递给视图,那么我将使用视图模型。在此示例中,我使用了视图模型,因为它需要包含状态列表和选定的状态ID:

public class TestViewModel
{
     public int StatusId { get; set; }

     public List<Status> Statuses { get; set; }
}

这是包含每个状态数据的domain model

public class Status
{
     public int Id { get; set; }

     public string Name { get; set; }
}

在我的控制器的动作方法中,我将填充状态列表。通常来自数据库调用,但在这个例子中,我很难编写我的列表:

public ActionResult Index()
{
     TestViewModel model = new TestViewModel();

     model.Statuses = new List<Status>();
     model.Statuses.Add(new Status { Id = 1, Name = "Status 1" });
     model.Statuses.Add(new Status { Id = 2, Name = "Status 2" });
     model.Statuses.Add(new Status { Id = 3, Name = "Status 3" });

     return View(model);
}

填充列表后,我将视图模型传递给视图。视图接受此视图模型并创建下拉列表:

@model WebApplication_Test.Models.TestViewModel

@using (Html.BeginForm())
{
     <div>
          @Html.DropDownListFor(
               m => m.StatusId,
               new SelectList(Model.Statuses, "Id", "Name", Model.StatusId),
               "-- Select --"
          )
          @Html.ValidationMessageFor(m => m.StatusId)
     </div>
     <div>
          <button type="submit">Submit</button>
     </div>
}

我希望这会有所帮助。