【问题标题】:How does ViewBag in ASP.NET MVC work behind the scenes?ASP.NET MVC 中的 ViewBag 如何在幕后工作?
【发布时间】:2013-06-05 20:40:25
【问题描述】:

我正在阅读一本关于 ASP.NET MVC 的书,我想知道以下示例是如何工作的:

示例 #1

控制器

public class MyController : Controller
{
    public ActionResult Index()
    {
        ViewBag.MyProperty = 5;

        return View();
    }
}

查看

<h1>@ViewBag.MyProperty</h1>

现在我知道ViewBag 是一个动态对象,所以你可以这样设置属性(虽然我对动态对象了解不多,从未使用过它们。)但是视图如何获取特定实例来自控制器的ViewBag,即使我们没有直接传递任何东西?

我认为ViewBag 可能是一个public static 对象,但是对它的任何更改都将是全局的,并且不会特定于视图实例。

您能否详细说明这是如何在幕后工作的?

示例 #2

控制器

public class MyController : Controller
{
    public ActionResult Index()
    {
        ViewBag.MyProperty = 5;

        return View();
    }

    public ActionResult Index2()
    {
        ViewBag.MyProperty = 6;

        return View();
    }
}

现在假设首先调用Index 方法,然后调用Index2。最后,ViewBag.MyProperty 的值最终会变为 6(来自Index2 的值)。我觉得这不是一件好事,但同时我觉得我在考虑桌面开发方面的问题。与 ASP.NET MVC 一起使用时可能无关紧要,因为 Web 是无状态的。 是这样吗?

【问题讨论】:

  • 我想ViewBag 对象只是用控制器对象作为实例属性(不是static)来实例化。
  • 可以看到其实是控制器的成员:msdn.microsoft.com/en-us/library/…
  • 那么如果有两个方法返回不同的视图在ViewBag实例上设置相同的属性,它会相互影响吗?这不会造成问题吗?我觉得我仍然在考虑桌面开发。
  • 控制器在Index()被调用时被实例化,在return View();被执行后被释放。因此,一旦整个请求完成,ViewBag 就会超出范围。这就是为什么 ViewBag 属性不会在请求之间持续存在的原因。
  • @RobertHarvey,好吧。现在说得通了。我不知道什么时候实例化了什么。正如我所说,仍然以桌面开发方式思考。你能把这些写成答案吗,我会选择它作为接受的答案。

标签: c# asp.net-mvc dynamic viewbag


【解决方案1】:

ViewBagControllerBase 的一个属性,所有控制器都必须从该属性继承。它是一个dynamic 对象,这就是为什么您可以向它添加新属性而不会出现编译时错误。

不是static,它是对象的成员。在请求的生命周期中,控制器实例被创建和释放,所以你不会有“并发”问题,比如覆盖值。

View(及其变体)方法也不是static,这就是视图接收ViewBag值的方式:在渲染视图的过程中,控制器实例的 ViewBag 实例为嗯。

【讨论】:

  • ViewBag 也是WebViewPage 的属性。它如何获取分配给ControllerBase.ViewBag 属性的数据。能详细点吗?
  • @Abi 视图在创建ActionResult 时从控制器接收它。如果我们以ASP.NET Core MVC为例,可以看到ViewContext在ctor上接收到了:github.com/aspnet/Mvc/blob/dev/src/…
【解决方案2】:

如果您分析ControllerBase 类,您会发现 ViewBag 属性是 ViewData 属性的“代理”,只是为了让您的源代码看起来更漂亮。 (我什至记得 Scott Hanselman 接受了 Phil Haack 的采访,Phil 介绍了 ViewBag 属性作为 ViewData 的快捷方式,并消除了重复方括号和引号的需要)。即使ViewBag 属性公开为dynamic 对象,它也实现了直接与ViewData 一起使用的DynamicViewDataDictionary 类。

查看Controller类的源码可以找到这个方法:

protected internal virtual ViewResult View(string viewName, string masterName, object model)

所以基本上,当您从控制器调用 return View(); 时,它会创建一个新的 ActionResult 类实例,将 ViewData 从控制器传递到它的构造函数。然后将ActionResult 的实例传递给特定的视图引擎(ASPX、Razor),以便它可以用于呈现有问题的视图。

将 ViewBag/ViewData 设为公共静态可能是有害的。对 MVC 应用程序的每个 Web 请求都会创建一个新的控制器实例。如果您将 ViewData/ViewBag 作为公共静态,那么两个并发用户将共享来自 ViewBag/ViewData 的相同数据。

Here 是一个视频。 04:05开始讨论ViewBag(原ViewModel)

【讨论】:

    【解决方案3】:

    ViewBagControllerBase 的属性。定义如下:

    public Object ViewBag { get; }
    

    请注意,此签名实际上是不正确的。以下是源代码的实际样子:

    public dynamic ViewBag {
            get {
                if (_dynamicViewDataDictionary == null) {
                    _dynamicViewDataDictionary = new DynamicViewDataDictionary(() => ViewData);
                }
                return _dynamicViewDataDictionary;
            }
        }
    

    _dynamicViewDataDictionary 是一个 ExpandoObject;您可以在运行时向其添加属性。它的生命周期与控制器的生命周期相同,也就是HTTP请求的生命周期。

    【讨论】:

    • ControllerBase 的 ViewBag 属性似乎一直被定义为动态的,也许是最近:public dynamic ViewBag { get; }
    • ViewBag 也是 WebViewPage 的一个属性。它如何获取分配给 ControllerBase.ViewBag 属性的数据。能详细点吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多