对于ViewBag,我听说这是禁止使用的。我会假设ViewBag中的内容应该合并到视图模型中吗?
题:
ViewBag是一个动态字典。因此,当使用ViewBag在操作方法和视图之间传输数据时,如果在尝试访问视图中的ViewBag项时在代码中输入错误,编译器将无法捕获。您的视图将在运行时崩溃:(
通常,最好使用视图模型在操作方法和视图之间传输数据。 view model是一个简单的POCO类,它具有特定于视图的属性。因此,如果要传递一些额外的数据进行查看,请向视图模型添加新属性并使用该属性。强大的类型视图使代码更清晰,更易于维护。使用这种方法,您不需要将viewbag字典项显式地转换为某些类型来回与视图包有关。
public class ProductsForCategoryVm
{
public string CategoryName { set;get; }
public List<ProductVm> Products { set;get;}
}
public class ProductVm
{
public int Id {set;get;}
public string Name { set;get;}
}
在您的操作方法中,创建此视图模型的对象,加载属性并将其发送到视图。
public ActionResult Category(int id)
{
var vm= new ProductsForCategoryVm();
vm.CategoryName = "Books";
vm.Products= new List<ProductVm> {
new ProductVm { Id=1, Name="The Pragmatic Programmer" },
new ProductVm { Id=2, Name="Clean Code" }
}
return View(vm);
}
并且您的视图是视图模型的强类型,
@model ProductsForCategoryVm
<h2>@Model.CategoryName</h2>
@foreach(var item in Model.Products)
{
<p>@item.Name</p>
}
很多教程/书籍都有代码示例,它们使用ViewBag作为下拉数据。我个人仍然认为ViewBag不应该用于此。它应该是视图模型中List<SelectListItem
>类型的属性,以传递下拉数据。这是一个post,带有如何做到这一点的示例代码。
是否存在绝对需要ViewBag的情况?
有一些有效的用例,您可以(不必要)使用ViewBag发送数据。例如,要在“布局”页面上显示某些内容,可以使用ViewBag。另一个例子是默认MVC模板中的ViewBag.Title
(用于页面标题)。
public ActionResult Create()
{
ViewBag.AnnouncementForEditors="Be careful";
return View();
}
在布局中,您可以阅读ViewBag.AnnouncementForEditors
<body>
<h1>@ViewBag.AnnouncementForEditors</h1>
<div class="container body-content">
@RenderBody()
</div>
</body>
我以为我会对此发表意见,因为我已广泛使用ViewBag。
使用它可以获得的唯一真正的好处是用于一个小项目,或者在你有一个新项目的情况下,只是想让球滚动,而不必担心创建大量的模型类。
当您的项目更加成熟时,您可能会发现在应用程序中使用弱键入导致的意外行为问题。当出现问题时,逻辑可能会令人困惑,难以测试并且难以排除故障。
我实际上是从我的应用程序中完全删除ViewBag的路线,并阻止其他开发人员在尝试时抛出编译错误,在同一代码库中使用它,即使在Razor视图中也是如此。
这是GitHub上的一个示例ASP.NET 5项目,我删除了它:https://github.com/davidomid/Mvc5NoViewBag
这是一篇博客文章,其中我解释了删除它的动机以及解决方案如何工作的解释:https://www.davidomid.com/hate-the-aspnet-mvc-viewbag-as-much-as-i-do-heres-how-to-remove-it
1)我的假设是否是最佳实践。 (不使用ViewBag,第二个在视图模型中使用它)
您应该使用viewmodels而不是通过ViewBag尽可能多地传递数据。
2)是否存在绝对需要ViewBag的情况?
没有ViewBag绝对必要的情况。但是,有些数据我个人更喜欢使用ViewBag而不是View Model。例如,当我需要填充预定义值(即Cities)的下拉框时,我使用ViewBag来携带SelectListItem数组进行查看。我不想用这些数据污染我的ViewModel。
1)我的假设是否是最佳实践。 (不使用ViewBag,第二个在视图模型中使用它)
是。
2)是否存在绝对需要ViewBag的情况?
不存在。您存储在ViewBag中的所有内容都可以进入传递给视图的视图模型。
ViewBags的问题和推荐的最佳实践归结为编译时间检查。 ViewBags只是字典,你可以获得“魔术”字符串,所以如果最终改变其中一个视图包项目的对象类型或者直到运行时才会知道的键名,即使你使用<MvcBuildViews>true</MvcBuildViews>
预编译视图也是如此。
坚持查看模型是最好的,即使你必须改变它们以适应特定的视图。
我发现ViewBag有一些用途,其中所有页面都有共同的功能,而且功能不依赖于显示的页面。例如,假设您正在构建StackOverflow。作业板出现在每个页面上,但显示的作业与页面无关(我的用法在概念上类似)。向每个ViewModel添加属性将非常困难且耗时,并且会给您的测试带来很多麻烦。在这种情况下,我认为这不值得。
我已经使用了一个带有交叉数据的基本ViewModel类,但如果您不止一个(例如,作业和堆栈交换站点列表),您必须开始填充额外的数据,或者其他一些滥用ViewModel,加上您需要一个ViewModel构建器来填充基础数据。
至于魔术弦问题,有很多解决方案。常量,扩展方法等
尽管如此,如果您的页面上显示的内容取决于页面的上下文,ViewModel就是您的朋友。
埃里克
如果无法重新设计EXISTING ViewModel,请使用ViewBag。
2.是否存在绝对需要ViewBag的情况?
在某些情况下,您需要跨布局,视图和部分视图从Controller共享您的数据。在这种情况下,ViewBag非常有用,我怀疑还有更好的方法。
如果没有用例,则不会首先实现。是的,你可以使用ViewModel做任何事情,但如果你真的不需要它呢?一种这样的场景是编辑实体。您可以直接将DTO作为模型传递。
@model CategoryDto
<div class="md-form form-sm">
<input asp-for="Name" class="form-control">
<label asp-for="Name">("Category Name")</label>
</div>
但是如果你想选择Category parent呢?实体DTO理想地仅包含它自己的值,因此要填充选择列表,请使用ViewBag
<select asp-for="ParentId" asp-items="ViewBag.ParentList">
<option value="">None</option>
</select>
为什么这样?好吧,如果你有50种类型的实体,每种实体都有不同的值选择,你只需要创建50个额外的ViewModel。