我已经读过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>
}
答案 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>
}
我希望这会有所帮助。