MVC ViewBag最佳实践

33

关于 ViewBag,我听说使用它是不好的。我认为应该将 ViewBag 中的内容合并到视图模型中,是最佳实践?

问题:

  1. 我的假设是最佳实践吗?(不使用 ViewBag,其次将其内容放入视图模型中)

  2. 是否有绝对需要使用 ViewBag 的情况?


9
从微软的角度来看,我们不同意从不使用ViewBag或者说ViewBag是禁忌。使用ViewBag传递模型数据是另一回事,应该避免这样做。使用ViewBag传递元数据(例如Select List)是可以的。Sanderson的《Pro MVC》和其他书籍使用了ViewBag,并没有作出明确的声明。对于是否必须使用ViewBag,我持怀疑态度。 - RickAndMSFT
11个回答

32

1) 我的假设是否符合最佳实践。(不使用ViewBag,而是将其放在视图模型中)

尽可能使用视图模型代替通过ViewBag传递数据。

2) 是否存在必须使用ViewBag的情况?

没有必须使用ViewBag的情况。但是,某些数据我个人更喜欢使用ViewBag而不是视图模型。例如,当我需要为预定义值(如城市)填充下拉框时,我使用ViewBag来传递SelectListItem数组到视图中。我不想用这些数据来污染我的视图模型。


4
非常好的回应,符合我们(微软)发布的指导方针。我只会将其从传递数据更改为传递模型数据(元数据除外)。请参见我的教程底部http://www.asp.net/mvc/tutorials/javascript/working-with-the-dropdownlist-box-and-jquery/examining-how-aspnet-mvc-scaffolds-the-dropdownlist-helper - RickAndMSFT
@RickAndMSFT,我必须承认,当我第一次学习ViewBags时,我利用了你的帖子。顺便说一句,这是一篇很棒的帖子。 - SadullahCeran
1
好的回答,我也认为使用ViewBag而不是将选项列表污染到要传递给视图的ViewModel中是一个好习惯。如果在ViewModel中传递了一个列表,那么很可能是因为该列表可能需要进行编辑或者因为它对于模型来说“有意义”。但是在只选择其中一个选项的选项列表的情况下,使用ViewBag会更加简洁。 - diegosasw
我认为使用动态类型所带来的所有缺点比污染视图模型要糟糕得多。甚至在不读完整个控制器的情况下,你无法查看视图并告诉ViewBag中设置了什么。 - Joel McBeth

32

ViewBag是一个动态字典。因此,当使用ViewBag在操作方法和视图之间传递数据时,你的编译器无法捕获如果有代码中拼写错误尝试访问ViewBag项的情况。你的视图将在运行时崩溃 :(

通常最好使用视图模型在操作方法和视图之间传递数据。视图模型是一个简单的POCO类,具有特定于视图的属性。因此,如果要向视图传递一些附加数据,请将新属性添加到视图模型中并使用它。强类型视图使代码更干净、更易于维护。通过这种方法,你不需要对你的ViewBag字典项进行显式转换来回多次,这是你必须使用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 来传递下拉菜单数据。我个人认为不应该这样做,而是应在视图模型中将其定义为类型为 List<SelectListItem> 的属性来传递下拉菜单数据。在这里有一个帖子,其中提供了如何实现的示例代码。

是否存在绝对必须使用 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 中,因此它始终是必需的。使用魔术字符串可能不是可维护性的巅峰,但除此之外,使用模型只是强制声明类型和页面变量的约定。 - Travis J

5

1) 我上面的假设是最佳实践吗?(不使用 ViewBag,其次是将其放在视图模型中)

是的。

2) 有没有绝对需要使用 ViewBag 的情况?

没有。您在 ViewBag 中存储的所有内容都可以放入传递给视图的视图模型中。


元数据怎么办?我们应该从模板中删除@ViewBag.Title吗? - RickAndMSFT
8
好的,请扔掉它。我不想在ASP.NET MVC 4中看到它,它让我想吐。如果必须在框架内部使用,请将其标记为“internal”,这样开发人员就永远不必面对它。 - Darin Dimitrov

4
使用ViewBag的问题与推荐的最佳实践归结为编译时检查。ViewBag只是字典,因此你会得到“魔法”字符串,如果你最终更改了一个viewbag项的对象类型或键名,直到运行时才会知道,即使使用<MvcBuildViews>true</MvcBuildViews>预编译视图也无济于事。
坚持使用视图模型是最好的选择,即使你现在和再次需要修改它们以适应特定的视图。

2
我发现在所有页面中都有一些通用功能,而且这些功能不依赖于所显示的页面时,ViewBag非常有用。例如,假设您正在构建StackOverflow,招聘板块出现在每个页面上,但是所显示的工作与页面无关(我的使用方式类似于此概念)。为每个ViewModel添加属性将会很困难和耗时,并且会增加测试的冗余内容。在这种情况下,我认为这不值得。
我已经使用了一个带有交叉数据的基本ViewModel类,但是如果您有多个(例如,工作和堆栈交换网站列表),则必须开始填充额外的数据或滥用ViewModel,此外还需要ViewModel构建器来填充基本数据。
至于魔术字符串问题,有很多解决方案。常量、扩展方法等。
总之,如果您有需要根据页面上下文显示的内容,ViewModel是您的好朋友。
Erick

我知道这已经过时了,但是在视图中使用部分视图和@{Html.RenderAction("SomeAction", "SomeController");}是否更好呢? - wingyip

1

我想发表我的观点,因为我经常使用ViewBag。

你只有在小项目中使用它,或者在你有一个新项目而且只是想开始运行而不必担心创建大量模型类的情况下才会从中获得真正的好处。

当你的项目更加成熟时,您可能会发现由于在整个应用程序中使用弱类型而导致的意外行为问题。逻辑可能令人困惑,难以测试,并且当事情出错时难以进行故障排除。

实际上,我曾经从我的应用程序完全删除了ViewBag,并通过在Razor视图中尝试使用ViewBag时引发编译错误来防止其他开发人员在同一代码库中使用它。

这里是一个ASP.NET 5项目的示例,我已经从中删除了ViewBag: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
如果无法重新设计现有的ViewModel,请使用ViewBag。

1
  1. 不要使用ViewModels
  2. 如果您设计了一个完美的ViewModel,就不需要使用ViewBag。

1
如果没有用例,它就不会被首先实现。是的,您可以使用ViewModels完成所有操作,但如果您真的不需要呢?其中一个场景是编辑实体。您可以直接将DTO作为模型传递。
@model CategoryDto
<div class="md-form form-sm">
    <input asp-for="Name" class="form-control">
    <label asp-for="Name">("Category Name")</label>
</div>

但是,如果您想选择类别的父级呢?实体DTO理想情况下只保存自己的值,因此要填充选择列表,您需要使用ViewBag。

<select asp-for="ParentId" asp-items="ViewBag.ParentList">
    <option value="">None</option>
</select>

为什么要这样做?如果您有50种实体类型,每种都有从不同值中进行选择的功能,那么您就避免了创建50个额外的ViewModel。

0

冒着重新引发争议的风险,让我提供一点见解。无论什么是“最佳实践”,让我提供现实世界的经验,广泛使用ViewBag可能成为一个令人头痛的问题源,难以找到的错误和问题会让人非常困扰,而且很难追踪和解决。即使可以建立关于使用ViewBag的可接受概念或规则,它们也很容易成为初级开发人员依赖分心的支撑,并阻碍正确、强类型的ViewModels的开发。

不幸的是,太多的“教程”YouTube视频展示了使用ViewBags进行演示,并几乎没有提供任何关于何时不适用于生产代码的见解。是的,有时使用ViewBag可能是解决特定问题的合适方案,但可能会导致长时间的挫败感和较差的可维护性。冒着过度谨慎的风险,我鼓励年轻开发人员在他们更多地了解MVC、开发出自然的对ViewModels何时以及如何有用的感觉之后,不要依赖ViewBag,然后在此之后,再发展出更有经验的使用ViewBag的感觉。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接