.NET MVC TempData、ViewData、ViewBag

来源:http://www.cnblogs.com/rancrazy/archive/2016/08/18/5785941.html
-Advertisement-
Play Games

說明: 原文作者賢新 原文地址:http://www.cnblogs.com/chenxinblogs/p/4852813.html ViewData和ViewBag主要用於將數據從控制器中傳遞到視圖中去,ViewData本身就是一個字典。以KeyValue的形式存取值。ViewData的Value ...


說明: 原文作者賢新

   原文地址:http://www.cnblogs.com/chenxinblogs/p/4852813.html

 

ViewData和ViewBag主要用於將數據從控制器中傳遞到視圖中去,ViewData本身就是一個字典。以KeyValue的形式存取值。ViewData的Value類型是Object,也就是可以將任意類型的值存儲到ViewData中去,平時我們都在控制器中直接使用ViewData.本質上ViewData只是Controller父類ControllerBase中的一個屬性,其類型是ViewDataDictionary,因為我們在自己的Controller中並未定義一個叫做ViewData的屬性,也就是說當我們訪問在某個類的屬性或者方法中所訪問的某個方法或者屬性中沒有找到時,我們就要想到這個屬性或者方法是否在父類中已經定義了,這個對於一個新手來說往往是容易忽略的,TempData是用於解決在不同的的Action方法之間跳轉的時候的數據傳遞。這裡不同的Action可以是同一個Controller下的不同的Action之間,也可以是不同Controller的Action之間。有些人說,利用Session不是也可以實現嗎?是的,沒錯,不過仔細的去看下微軟的Mvc源碼,你會發現,其實TempData中的數據的維護也是用到了Session的。

ViewData

    我們很經常看到這樣,

複製代碼
     public ActionResult Index()
        {
            //從資料庫中讀取產品列表
            List<Product> productList = db.Products.ToList();   
        //這個時候productList這個集合對象將被傳遞到視圖頁中去.       
        //如果此時,在視圖頁面中使用@model List<Product> 聲名,你會發現在視圖頁面中,直接訪問View       
        //註意,每個視圖頁面在網站第一次被請求時,都會被編譯成一個對應的       
        //一個類,這些視圖類都會被編譯到一個臨時的程式集中去,這個臨時的程式集的位置,可以通過在視圖頁面中編寫代碼@this.GetType().Assembly.Locaiton       
        //的方式來查看其位置,實際上就是在C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\vs的某個目錄下。       
       //這個類直接或者間接繼承自:System.Web.Mvc.WebViewPage類.
            return View(productList);
        }
複製代碼

      細心的查看View()方法的源碼:

複製代碼
protected internal virtual ViewResult View(string viewName, string masterName, object model)
{
    if (model != null)
    {
     //看到了沒有,其實就是直接將我們想要傳遞到視圖頁的數據,保存到了父類ControllerBase的ViewData屬性對象的一個名為Model的屬性中去了。
     //然後在將Controller中的ViewData的引用傳遞給視圖頁面類的實例對象上的ViewData屬性,這樣就能將在控制器的Action中往ViewData中設置的值傳遞到視圖中去了,也就是我們上面的代碼還可以改為
     //ViewData.Model=productList;return View();就行了。
        base.ViewData.Model = model;
    }
    return new ViewResult { ViewName = viewName, MasterName = masterName, ViewData = base.ViewData, TempData = base.TempData, ViewEngineCollection = this.ViewEngineCollection };
}
複製代碼

    需要註意的是:ViewData中的數據只能傳遞到當前這個Action所要去載入的視圖頁面中去,而不能跨Action傳輸。

ViewBag

   ViewBag,其實內部真正存儲數據的還是ViewData,也就是說,ViewData和ViewBag的數據是共用的,通過ViewData設置的數據,可以通過ViewBag訪問,通過ViewBag設置的數據可以通過

ViewData訪問。

   看看ControllerBase中的ViewBag屬性的源碼:

複製代碼
[Dynamic]
public object ViewBag
{
    [return: Dynamic]
    get
    {
        Func<ViewDataDictionary> viewDataThunk = null;
        if (this._dynamicViewDataDictionary == null)
        {
            if (viewDataThunk == null)
            {
                viewDataThunk = () => this.ViewData;
            }
            //viewDataThunk是個lambda表達式,返回ViewData.也就是DynamicViewDataDictionary內部還是用的是ViewData.
            this._dynamicViewDataDictionary = new DynamicViewDataDictionary(viewDataThunk);
        }
        return this._dynamicViewDataDictionary;
    }
}
複製代碼

    以下是DynamicViewDataDictionary中的兩個方法。

複製代碼
   
    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;
    }
複製代碼

TempData

    我們需要知道的是,.NET Mvc中最終處理來自於瀏覽器端的請求的是一個MvcHandler的類,這個類實現了IHttpHandler,而IHttpHandler中定義了一個ProccessRequest方法,和WebForm

不一樣的是,Controller對象的創建是在MvcHandler中來完成的。從MvcHanlder的ProccessRequest方法中開始追蹤,我們會發現以下代碼:

複製代碼
protected internal virtual void ProcessRequest(HttpContextBase httpContext)
{
    IController controller;
    IControllerFactory factory;
    this.ProcessRequestInit(httpContext, out controller, out factory);
    try
    {
        controller.Execute(this.RequestContext);
    }
    finally
    {
        factory.ReleaseController(controller);
    }
}
複製代碼

    再看看controller.Execute的源碼(因為controller變數是IController介面類型,所以要查看實現了IController的類的Execute方法)發現是ControllerBase實現了該介面中的Execute方法,再看看,ControllerBase中的Execute方法,發現調用了自己的ExecuteCore方法,發現ExecuteCore是一個abstract方法,沒有方法體,然後我們從其子類Controller中看到了重寫了其父類ControllerBase中的ExecuteCore方法,Controller->ExecuteCore方法如下:

複製代碼
protected override void ExecuteCore()
{
    //這句代碼表示在調用目標Action之前,去Session中載入對應的來自於上個Action      //中保存的傳遞過來的數據。
    this.PossiblyLoadTempData();
    try
    {
        string requiredString = this.RouteData.GetRequiredString("action");
        if (!this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString))
        {
            this.HandleUnknownAction(requiredString);
        }
    }
    finally
    {
        this.PossiblySaveTempData();
    }
}
複製代碼

    我們在看看PosiblyLoadTempData這個方法中的代碼,很明顯,這個方法就是Controller中的.

複製代碼
internal void PossiblyLoadTempData()
{
    //這裡說明瞭,只有當前被請求的不是子Action的時候才會去載入對應的TempData數據,從Session中。
    if (!base.ControllerContext.IsChildAction)
    {
        //TempData的類型是TempDataDictionary,我們看看這個類中的Load方法,查看TempDataDictionary.Load方法,我們發現其實真正去載入的是一個實現了ITempDataProvider介面的某個類的實例對象去載入的。       //其實就是SessionStateTempDataProvider,找到這個類,查看其LoadTempData方法。
        base.TempData.Load(base.ControllerContext, this.TempDataProvider);
    }
} 
複製代碼

    下麵是SessionStateTempDataProvider->LoadTempData方法源碼:

複製代碼
    // Methods
    public virtual IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
    {
        HttpSessionStateBase session = controllerContext.HttpContext.Session;
        if (session != null)
        {
        //真相大白了,其實我們從TempData中取數據時,還是從一個key為__ControllerTempData的Session中取出來的,也就是說TempData只是一個臨時的數據保存的地方,        //最終在調用Action完畢後,框架自動把在Action中往TempData中設置的值保存到Session中去,然後跳轉到下個Action併在這個Action執行之前,又從Session中取出來,         //通過as Dictionary讓我們也知道了,其實TempData中保存數據的就是一個普通的字典而已,這就是為什麼TempData能在不同的請求之間保存數據,同時也說明瞭為什麼能在多個不同的Action之間無限的進行        //數據傳遞。
            Dictionary<string, object> dictionary = session["__ControllerTempData"] as Dictionary<string, object>;
            if (dictionary != null)
            {
                session.Remove("__ControllerTempData");
                return dictionary;
            }
        }
        return new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
    }
複製代碼

    下麵是SessionStateTempDataProvider的SaveTempData方法

複製代碼
   public virtual void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
    {
        if (controllerContext == null)
        {
            throw new ArgumentNullException("controllerContext");
        }
        HttpSessionStateBase session = controllerContext.HttpContext.Session;
        bool flag = (values != null) && (values.Count > 0);
        if (session == null)
        {
            if (flag)
            {
                throw new InvalidOperationException(MvcResources.SessionStateTempDataProvider_SessionStateDisabled);
            }
        }
        else if (flag)
        {
            session["__ControllerTempData"] = values;
        }
        else if (session["__ControllerTempData"] != null)
        {
            session.Remove("__ControllerTempData");
        }
    }
複製代碼

   再回頭看看Controller類中的ExecuteCore方法,其實就是在瀏覽器發出請求之後,調用控制器的某個Action之前,先會去Session中嘗試找Key為__ControllerTempData的Session["__ControllerTempData"]是否為null,如果不為null,那麼就將其取出,並且轉換為Dictionary<string,object>類型,並且存儲到ControllerBase父類中的TempData屬性對象里的內部屬性_data中去,

然後我們在Action中或者視圖中取出TempData的值的時候,就是從這個內部字典_data中取值的,在執行完action並且執行完視圖頁面的代碼之後(this.ActionInvoker.InvokeAction(base.ControllerContext, requiredString這句代碼會去找到對應的視圖頁面類,並且去執行,),最後一個步驟,就是去將TempData中內部字典的內容保存到Session中去,因為是最後執行,所以,在視圖頁面中保存到TempData的值也會被保存起來,以供下次使用,也就是說TempData是跨請求的,但是你會發現如果經過了兩次請求,也就是從瀏覽器中輸入兩次,你會發現只能取一次,為什麼呢,看下麵這個TempDataDictionary的Save方法。

複製代碼
public void Save(ControllerContext controllerContext, ITempDataProvider tempDataProvider)
{        //也就是保存到Session之前,會去先看看這些key是否是通過我們手動TempData[""]的方式設置進去的    //(之所以這種判斷,是因為在使用TempData["key"]=value的時候,索引器的set中包含了一句,this._initialKeys.add(key)的方法)    //如果不是,則不會去再次保存,這就是為什麼你在第二個請求的Action中只能取一次的原因了。     //這裡說的,是指第三次刷新第二次請求的那個Action時,已經無法訪問到第一次請求存進去的TempData中的值了,因為第二次請求的時候,因為這個key沒有在_initialKeys和_retainKeys這兩個HashSet中。     //如果要繼續保留,請在使用TempData中的數據之前,註意:是之前哦,調用Keep()或者Keep(string key)方法。
    this._data.RemoveFromDictionary<string, object, TempDataDictionary>(delegate (KeyValuePair<string, object> entry, TempDataDictionary tempData) {
        string item = entry.Key;
        return !tempData._initialKeys.Contains(item) && !tempData._retainedKeys.Contains(item);
    }, this);
    tempDataProvider.SaveTempData(controllerContext, this._data);
}
複製代碼

    為什麼會在使用一次之後,就不會在保存回Session中去了呢,如果,還不夠清楚,還可以看看,TempData的索引器,可以發現get下有一個關鍵代碼:

複製代碼
    public object this[string key]
    {
        get
        {
            object obj2;
            if (this.TryGetValue(key, out obj2))
            {           //原來這邊在我們取數據的時候,將將key從_initialKeys中移除了,當TempData.Save方法被調用時,發現_data字典中的我們取數據的那個key不在這個HashSet中,所以就不會被保存了。          //如果你希望取數據了之後,又希望還能傳遞到下一個action用的話,那麼請使用Peek方法。
                this._initialKeys.Remove(key);
                return obj2;
            }
            return null;
        }
        set
        {
            this._data[key] = value;         //這句說明瞭,為什麼我們使用TempData["key"]=value設置的值,可以留到下次使用的原因。
            this._initialKeys.Add(key);
        }
    }
複製代碼  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘自:http://www.cnblogs.com/hopeworld/archive/2011/04/08/2009252.html 在Windows中可以在某些路徑中查找文件,也可以設定不在某些路徑中查找文件,下麵用Linux中的find的命令結合其-path -prune參數來看看在Linux ...
  • 摘自:http://312788172.iteye.com/blog/730280 我們經常在linux要查找某個文件,但不知道放在哪裡了,可以使用下麵的一些命令來搜索。這些是從網上找到的資料,因為有時很長時間不會用到,當要用的時候經常弄混了,所以放到這裡方便使用。 which 查看可執行文件的位置 ...
  • 1. 下載Linux iso文件,官方下載鏈接中有Mirror,選擇一個合適的鏈接 http://mirrors.163.com/centos/7/isos/x86_64/CentOS-7-x86_64-Minimal-1511.iso 2. 燒錄成光碟,使用USBWriter寫入到U盤,或者使用虛 ...
  • 最近在看u-boot、osekOS的啟動代碼,其中涉及到lds文件,通過參考其他網友的文章,希望對lds文件有個明晰的認識,為了鞏固及加深影響,特將相關博客內容重寫一遍。 原始文章: http://linux.chinaunix.net/techdoc/beginner/2009/08/12/112 ...
  • 編程環境搭建: 因為ubuntu 12.04的內核版本已經是3.x,而目前一些講解內核驅動的書都是2.6.x。 嵌入式開發的版本一般都是基於3.14移植的,因為嵌入式是跑在開發板上的,所以開發驅動沒有問題。但是教材的例子一般都是基於PC機的2.6.x版本,雖然內核內部介面相對穩定,但是我也不太清楚。... ...
  • 源代碼如下: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD TypeOffset[1]; } IMAGE_BASE_RELOCATION; typedef IMAGE_... ...
  • 查看文件內容 1.cat 命令 作用:查看文件內容 語法:cat 文件名 2. more 命令 作用:分頁查看文件內容 語法:more 文件名 例:more /etc/passwd 按下回車刷新一行,按下空格刷新一屏 退出:按q健 3.less 命令 作用:分頁查看文件內容 語法:less 文件名 ...
  • 1、LINQ是什麼? LINQ是Language Integrated Query的縮寫,即“語言集成查詢”的意思。LINQ的提出就是為了提供一種跨越各種數據源的統一的查詢方式,它主要包含4個組件--Linq to Objects、Linq to XML、Linq to DataSet和Linq t ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...