ViewBag, ViewData and TempData

222
有人能解释一下,何时使用以下选项:
  1. TempData
  2. ViewBag
  3. ViewData
我有一个需求,需要在控制器一中设置一个值,该控制器将重定向到控制器二并呈现视图。
我尝试过使用ViewBag,但是当我到达控制器二时,值就会丢失。
我能知道什么时候使用这些选项以及其优缺点吗?
谢谢。

5
这篇文章很好地解释了 ASP.NET MVC 3 应用程序中 ViewBag、ViewData 和 TempData 的区别。 - Beku
1
stackoverflow.com/a/17199709/2015869 - Imad Alazani
6个回答

310

1) TempData

TempData允许您存储能够在重定向后仍然存在的数据。它内部使用Session作为后备存储,当重定向完成后,数据将自动被清除。使用方式如下:

public ActionResult Foo()
{
    // store something into the tempdata that will be available during a single redirect
    TempData["foo"] = "bar";

    // you should always redirect if you store something into TempData to
    // a controller action that will consume this data
    return RedirectToAction("bar");
}

public ActionResult Bar()
{
    var foo = TempData["foo"];
    ...
}

2) ViewBag、ViewData

允许你在控制器的操作中存储数据,这些数据将在对应的视图中使用。此前提是该操作返回一个视图而非重定向。仅在当前请求期间存在。

使用方法如下:

public ActionResult Foo()
{
    ViewBag.Foo = "bar";
    return View();
}

在视图中:

@ViewBag.Foo

或者使用 ViewData:

public ActionResult Foo()
{
    ViewData["Foo"] = "bar";
    return View();
}

并且在视图中:

@ViewData["Foo"]

ViewBag只是ASP.NET MVC 3中ViewData的动态包装器。

话虽如此,这两个结构都不应该被使用。你应该使用视图模型和强类型视图。因此,正确的模式如下:

视图模型:

public class MyViewModel
{
    public string Foo { get; set; }
}

操作:

public Action Foo()
{
    var model = new MyViewModel { Foo = "bar" };
    return View(model);
}

强类型视图:

@model MyViewModel
@Model.Foo
在这个简短的介绍之后,让我们回答你的问题:
我的要求是在控制器一中设置一个值,该控制器将重定向到控制器二,控制器2将呈现视图。
public class OneController: Controller
{
    public ActionResult Index()
    {
        TempData["foo"] = "bar";
        return RedirectToAction("index", "two");
    }
}

public class TwoController: Controller
{
    public ActionResult Index()
    {
        var model = new MyViewModel
        {
            Foo = TempData["foo"] as string
        };
        return View(model);
    }
}

还有相应的视图 (~/Views/Two/Index.cshtml):

@model MyViewModel
@Html.DisplayFor(x => x.Foo)

使用TempData也有缺点:如果用户在目标页面上按下F5,数据将会丢失。

个人而言,我也不使用TempData。这是因为它内部使用Session,而且我在我的应用程序中禁用了Session。我更喜欢通过更符合RESTful的方式来实现这一点。具体方法是:在第一个控制器操作执行重定向时,将对象存储在数据存储库中,并在重定向时使用生成的唯一ID。然后在目标操作上使用此ID来获取最初存储的对象:

public class OneController: Controller
{
    public ActionResult Index()
    {
        var id = Repository.SaveData("foo");
        return RedirectToAction("index", "two", new { id = id });
    }
}

public class TwoController: Controller
{
    public ActionResult Index(string id)
    {
        var model = new MyViewModel
        {
            Foo = Repository.GetData(id)
        };
        return View(model);
    }
}

视图保持不变。


61
回答很好,但我不同意独断的说法“这两种构造永远不应该使用”。我发现了一些合法的 ViewBag 用法。例如,我在所有视图上设置 ViewBag.Title 属性,它会在我的 _Layout.cshtml 基本视图文件中使用。我使用它的另一个情况是向用户提供信息消息(例如,“产品已成功保存!”)。我在 Layout.cshtml 中放置了一些通用标记来呈现提供的消息,这使得我可以在任何操作中设置 ViewBag.Message。对于这两种情况,使用 ViewModel 属性有太多缺点。 - Jesse Webb
23
我同意Jesse的观点,虽然这篇描述很好,但直言没有好的理由使用ViewBag是一种观点,而不是事实。滥用ViewBag肯定是不良实践,有些开发人员会陷入这个误区,但如果使用得当,它是一个强大的资源。 - Ron DeFreitas
1
@ron.defreitas,请告诉我一个使用ViewBag的好理由。请描述一种具体的实际情况,在此情况下,ViewBag有所用处。你说它是“强大的资源”,我猜你肯定有一些特定情况,这个“强大的资源”在这些情况下非常“强大”。由于我在我的职业生涯中从未使用过它,我很想了解人们如何使用这个“强大”的武器。 - Darin Dimitrov
28
我们这里有一个精英主义者。Darin, Jesse特别提到了这样的例子。仅仅因为总是有其他做事方式,并不会自动使它们失去用处。 - Djentleman
2
@DarinDimitrov:我现在有一个场景,需要从属性方法中向视图传递一些信息。使用filterContext.Controller.ViewData比尝试将其传递给强类型视图要容易得多。话虽如此,还是非常感谢您的解释,它非常有用。 - Andy
显示剩余2条评论

12

TempData

TempData基本上像一个DataReader,一旦读取,数据就会丢失。

观看该视频

示例

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";
        TempData["T"] = "T";
        return RedirectToAction("About");
    }

    public ActionResult About()
    {
        return RedirectToAction("Test1");
    }

    public ActionResult Test1()
    {
        String str = TempData["T"]; //Output - T
        return View();
    }
}

如果你仔细看上面的代码,就会发现RedirectToAction对TempData没有影响,直到读取TempData为止。因此,一旦读取了TempData,其中的值就会丢失。

如何在读取后保留TempData?

请查看Action方法Test 1和Test 2的输出。

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";
        TempData["T"] = "T";
        return RedirectToAction("About");
    }

    public ActionResult About()
    {
        return RedirectToAction("Test1");
    }

    public ActionResult Test1()
    {
        string Str = Convert.ToString(TempData["T"]);
        TempData.Keep(); // Keep TempData
        return RedirectToAction("Test2");
    }

    public ActionResult Test2()
    {
        string Str = Convert.ToString(TempData["T"]); //OutPut - T
        return View();
    }
}

如果您注意以上代码,可以发现在执行RedirectToAction和Reading the Data后数据并没有丢失,原因是我们使用了TempData.Keep()方法。

通过这种方式,您可以使数据在其他控制器中持久化。

ViewBag/ViewData

数据将保留到相应的视图中。


3

MVC中的ViewBag、ViewData、TempData和View State

http://royalarun.blogspot.in/2013/08/viewbag-viewdata-tempdata-and-view.html

ASP.NET MVC为我们提供了三种选项ViewData、VieBag和TempData来从控制器传递数据到视图,并在下一个请求中使用。ViewData和ViewBag几乎相似,而TempData则承担额外的职责。

ViewBag和ViewData的相似之处:

帮助在从控制器到视图的移动时保留数据。用于将数据从控制器传递到相应的视图。生命周期短,意味着当重定向发生时值变为空。这是因为它们的目标是提供一种在控制器和视图之间进行通信的方式。它是服务器调用内部的通信机制。

ViewBag和ViewData的区别:

ViewData是一个基于ViewDataDictionary类派生的对象字典,可以使用字符串作为键访问。ViewBag是一个动态属性,利用C# 4.0中的新动态特性。ViewData需要对复杂数据类型进行类型转换并检查空值以避免错误。ViewBag不需要对复杂数据类型进行类型转换。

ViewBag和ViewData的例子:

public ActionResult Index()

{  
    ViewBag.Name = "Arun Prakash";
    return View();    
}

public ActionResult Index()  
{
    ViewData["Name"] = "Arun Prakash";
    return View(); 
}

在View中,我们可以这样调用:
@ViewBag.Name   
@ViewData["Name"]

TempData:

在你从一个控制器跳转到另一个控制器或从一个操作跳转到另一个操作时,有助于保留数据。换句话说,当你重定向时,“Tempdata”有助于保留这些重定向之间的数据。它内部使用会话变量。TempData 应该是一个非常短暂的实例,只能在当前请求和后续请求期间使用。

唯一可以可靠地使用 TempData 的场景是当你进行重定向时。这是因为重定向会终止当前请求(并将 HTTP 状态码 302 Object Moved 发送给客户端),然后在服务器上创建一个新请求来服务于重定向视图。

对于复杂数据类型,需要进行类型转换并检查空值以避免错误。

public ActionResult Index()
{   
   var model = new Review()  
   {  
      Body = "Start",  
      Rating=5  
   };  

    TempData["ModelName"] = model;    
    return RedirectToAction("About");   
} 

public ActionResult About()       
{  
    var model= TempData["ModelName"];  
    return View(model);   
}  

1
void Keep()

Calling this method with in the current action ensures that all the items in TempData are not removed at the end of the current request.

    @model MyProject.Models.EmpModel;
    @{
    Layout = "~/Views/Shared/_Layout.cshtml";
    ViewBag.Title = "About";
    var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
    TempData.Keep(); // retains all strings values
    } 

void Keep(string key)

Calling this method with in the current action ensures that specific item in TempData is not removed at the end of the current request.

    @model MyProject.Models.EmpModel;
    @{
    Layout = "~/Views/Shared/_Layout.cshtml";
    ViewBag.Title = "About";
    var tempDataEmployeet = TempData["emp"] as Employee; //need typcasting
    TempData.Keep("emp"); // retains only "emp" string values
    } 

1

TempData将始终可用,直到第一次读取,一旦读取后,它将不再可用,可以用于传递快速消息,也可以查看在第一次读取后将消失的内容。 ViewBag 当快速传递数据到视图时更有用,通常应通过模型将所有数据传递到视图,但是存在模型直接来自映射到数据库(如实体框架)的类的情况,在这种情况下,您不希望更改模型以传递新的数据片段,您可以将其粘贴到viewbag中。 ViewData只是ViewBag的索引版本,在MVC3之前使用。


0

此外,ViewBag 和 TempData 在范围上也有所不同。ViewBag 基于第一个视图(在操作方法之间不共享),但是 TempData 可以在操作方法之间共享。


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