ASP.NET MVC企业级实战
上QQ阅读APP看书,第一时间看更新

4.1 View详解

View(视图)的职责是向用户提供界面,负责根据提供的模型数据生成准备提供给用户的格式界面,并提供用户和系统交互的入口。

MVC支持多种视图引擎(Razor和ASPX视图引擎是官方默认给出的,其实还支持N种视图引擎,甚至都可以是自己写的视图引擎)。

4.1.1 View和Action之间数据传递的方式

View和Action之间前后台数据传递的方式有如下几种。

● 弱类型ViewData[""]

● 动态型ViewBag //dynamic

● 强类型Model

● 临时存储TempData[""]

● 后台:return View(data); //存入ViewData.Model

● 前台:Model //其实就是WebViewPage.Model

下面我们通过一个实例来进行讲解。

打开VS2012,选择“文件→新建项目→Web→ASP.NET MVC4 Web应用程序”,并将项目命名为MvcViewApp,选择“基本”项目模板,然后单击“确定”按钮。

在Controllers目录中添加Home控制器、Models目录中添加User模型类:

public class User
{
      public string Name { get; set; }
}
public ActionResult Index()
{
      ViewData["One"] = "天机老人孙老头";
      ViewBag.Two = "子母龙凤环上官金虹";
      var _user = new User{Name="小李飞刀李寻欢" };
      TempData["Four"] = "嵩阳铁剑郭嵩阳";
      return View(_user); //等于ViewData.Model = _user;
}

添加Index视图,这里使用强类型视图,用法为“@model类型”,写在View最上面:@ViewData.Model(一般直接简写为@Model)、@Html.xxFor(x=>x.**)。

使用强类型视图的好处:提供智能提示,实现了编译时错误检查,防止写错字段。

@model MvcViewApp.Models.User
@{
    ViewBag.Title = "Index";
}
<h2>百晓生兵器谱排名</h2>
<p>第一名:@ViewData["One"]</p>
<p>第二名:@ViewBag.Two</p>
<p>第三名:@Model.Name</p>
<p>第四名:@TempData["Four"] </p>

运行结果如图4-1所示。

图4-1

4.1.2 TempData、ViewData和ViewBag的区别

ViewData是字典型的(Dictionary), ViewBag不再是字典的键值对结构,而是dynamic(动态)型,会在程序运行的时候动态解析。ViewData为object型,而ViewBag为dynamic型。dynamic型与object型的区别是在使用时它会自动根据数据类型转换,而object型则需要我们自己去强制转换。

通过反编译工具查看System.Web.Mvc.dll的源码(当然也可以直接下载System.Web.Mvc4的源码,本书云盘中提供了MVC4的源码Chapter04 \MVC4SourceCode.zip),代码如下所示。

public abstract class WebViewPage : WebPageBase, IViewDataContainer,
IViewStartPageChild
{
    [Dynamic]
    public object ViewBag { [return: Dynamic] get; }
    public ViewContext ViewContext { get; set; }
    public ViewDataDictionary ViewData { get; set; }
    ……
}

这里ViewBag只有get方法,没有set方法,但是我们在上面却给ViewBag赋值了。通过反编译发现ViewBag的代码如下所示。

[Dynamic]
public object ViewBag
{
          [return: Dynamic]
          get
          {
              Func<ViewDataDictionary> viewDataThunk = null;
              if (this._dynamicViewDataDictionary == null)
              {
                if (viewDataThunk == null)
                {
                    viewDataThunk = () => this.ViewData;
                    //创建一个委托对象,内部方法返回ViewData
                }
                this._dynamicViewDataDictionary = new
                DynamicViewDataDictionary(viewDataThunk);
              }
              return this._dynamicViewDataDictionary;
          }
}

不难看出ViewBag返回的是_dynamicViewDataDictionary,继续跟踪发现_dynamicViewData Dictionary属于DynamicViewDataDictionary类,代码如下:

internal sealed class DynamicViewDataDictionary : DynamicObject
{
    // Fields
    private readonly Func<ViewDataDictionary> _viewDataThunk;
    // Methods
    public DynamicViewDataDictionary(Func<ViewDataDictionary> viewDataThunk);
    public override IEnumerable<string> GetDynamicMemberNames();
    public override bool TryGetMember(GetMemberBinder binder, out object result);
    public override bool TrySetMember(SetMemberBinder binder, object value);
    // Properties
    private ViewDataDictionary ViewData { get; }
}

TryGetMember和TrySetMember方法如下:

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    result = this.ViewData[binder.Name];
    return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
    this.ViewData[binder.Name] = value;
    return true;
}

ViewBag其实就是ViewData,只是多了一层Dynamic控制,可以说它是另一种访问ViewData的方式。理论上ViewBag要比ViewData慢一点点,但是几乎可以忽略,所以使用何种方式完全取决于个人的爱好。

TempData的使用同ViewData和ViewBag一样,TempData也可以用来向视图传递数据,只是ViewData和ViewBag的生命周期和View相同,它们只对当前View有用。TempData则可以在不同的Action中进行传值,类似Webform里的Session。有一点需要注意,TempData的值在取了一次后会自动删除。TempData用来在一次请求中同时执行的多个Action方法之间共享数据。

从源码中可以看到TempData是一个TempDataDictionary类型。TempDataDictionary类型中有一个this索引器:

public TempDataDictionary TempData { get; set; }
public object this[string key]
{
    get
    {
      object obj2;
      if (this.TryGetValue(key, out obj2))
      {
          this._initialKeys.Remove(key);
          return obj2;
      }
      return null;
  }
  set
  {
      this._data[key] = value;
      this._initialKeys.Add(key);
  }
}

每次获取数据之后,就马上将该数据从键值对列表_initialKeys中移除了。

public class TempDataDictionary : IDictionary<string, object>,
ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string,
object>>, IEnumerable
{
    // Fields
    private Dictionary<string, object> _data;
    private HashSet<string> _initialKeys; //注意,就是往这里面存数据
    private HashSet<string> _retainedKeys;
    internal const string TempDataSerializationKey = "__tempData";
    ……
}