iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
佛心分享-IT 人自學之術

學過一點 React,卻被撈進 C#:新手用 AI 硬啃 MVC 企業專案的 3 個月實錄系列 第 19

Day 19|Controller 怎麼把資料交給 View?ViewData、ViewBag 和 Model

  • 分享至 

  • xImage
  •  

上一篇整理到:

網址
↓
Route
↓
Controller
↓
Action
↓
return View()
↓
找到對應的 .cshtml

但實際畫面不可能永遠只是固定文字。

企業專案裡常常要顯示:

使用者姓名
部門
角色
查詢結果
狀態
時間
列表資料

這些資料通常要先在 Controller 準備好,再交給 View 顯示。

所以這篇就來整理最常見的三種方式:

ViewData
ViewBag
Model

先看最基本的問題

假設 Controller 裡有:

public IActionResult Index()
{
    string userName = "Abbie";

    return View();
}

這時候:

userName

只存在 Index() 這個方法裡。

Index.cshtml 不會自動知道它。

也就是:

Controller 裡有資料
≠
View 自動拿得到

還需要明確把資料交出去。

方法一:ViewData

最直接的一種方式是:

public IActionResult Index()
{
    ViewData["UserName"] = "Abbie";

    return View();
}

View 裡就可以寫:

<h1>你好,@ViewData["UserName"]</h1>

最後顯示:

你好,Abbie

可以先把 ViewData 想成一個:

用文字當 Key 的資料盒子

Controller 放資料:

ViewData["UserName"] = "Abbie";

View 用同一個 Key 取:

@ViewData["UserName"]

所以:

Controller
ViewData["UserName"]

↓

View
@ViewData["UserName"]

ViewData 最容易出什麼問題?

就是 Key 寫錯。

例如 Controller:

ViewData["UserName"] = "Abbie";

View 卻寫:

@ViewData["Name"]

UserNameName 不一樣。

這樣就拿不到原本那筆資料。

所以使用 ViewData 時,我會注意:

Key 名稱一定要對

其實我們前面已經看過 ViewData

前面看 _Layout.cshtml 時,可能已經出現過:

<title>@ViewData["Title"]</title>

View 裡則可能寫:

@{
    ViewData["Title"] = "首頁";
}

這時流程就是:

Index.cshtml
設定 ViewData["Title"]

↓

_Layout.cshtml
讀取 ViewData["Title"]

所以 ViewData 很常拿來放:

頁面標題
簡單訊息
少量額外資料

方法二:ViewBag

第二種常見方式是:

public IActionResult Index()
{
    ViewBag.UserName = "Abbie";

    return View();
}

View 裡:

<h1>你好,@ViewBag.UserName</h1>

最後一樣會顯示:

你好,Abbie

ViewData 比起來,寫法看起來比較像一般物件:

ViewBag.UserName

而不是:

ViewData["UserName"]

所以我一開始會覺得 ViewBag 比較直覺。

ViewData 和 ViewBag 差在哪裡?

兩者用途很接近。

可以先這樣比較:

ViewData

ViewData["UserName"] = "Abbie";

↓

@ViewData["UserName"]
ViewBag

ViewBag.UserName = "Abbie";

↓

@ViewBag.UserName

最大的感受差異就是寫法。

ViewData 用:

["Key"]

ViewBag 用:

.屬性名稱

而且 ViewBag 底層其實是建立在 ViewData 上的。

所以它們不是兩套完全無關的資料。

ViewBag 有沒有缺點?

有。

例如 Controller:

ViewBag.UserName = "Abbie";

View 不小心寫:

@ViewBag.UserNmae

拼錯了。

因為 ViewBag 是 dynamic,編譯器不一定能像強型別 Model 那樣提早幫你抓到問題。

所以資料一多,我就不會很想全部塞進:

ViewBag

方法三:Model

如果資料不是一個簡單字串,而是一整組有結構的資料,就很常用 Model。

例如先建立:

public class UserViewModel
{
    public string Name { get; set; }

    public string Email { get; set; }
}

Controller:

public IActionResult Index()
{
    UserViewModel user = new UserViewModel
    {
        Name = "Abbie",
        Email = "abbie@example.com"
    };

    return View(user);
}

這次不是:

return View();

而是:

return View(user);

意思就是:

回傳目前 View

+

把 user 一起交給 View

View 上方則要宣告:

@model UserViewModel

然後使用:

<h1>@Model.Name</h1>

<p>@Model.Email</p>

完整流程就是:

Controller 建立 UserViewModel
↓
return View(user)
↓
View 宣告 @model UserViewModel
↓
使用 Model.Name、Model.Email

@modelModel 又不一樣?

這兩個大小寫很像,但用途不同。

@model UserViewModel

代表:

這個 View 預期收到的資料型別是 UserViewModel

而:

@Model.Name

是在使用實際收到的資料。

可以先記成:

@model
宣告資料型別

Model
使用資料

例如:

@model UserViewModel

<h1>@Model.Name</h1>

前面告訴 Razor:

Model 是 UserViewModel

所以後面才能知道它有:

Name
Email

下一篇會再把 @model、ViewModel、Entity、DTO 一起整理。

為什麼資料多的時候,Model 比較好用?

假設現在頁面需要:

姓名
Email
部門
角色
建立時間
是否可以編輯

全部用 ViewBag 可能變成:

ViewBag.Name = user.Name;
ViewBag.Email = user.Email;
ViewBag.Department = user.Department;
ViewBag.Role = user.Role;
ViewBag.CreateTime = user.CreateTime;
ViewBag.CanEdit = true;

View 裡再:

@ViewBag.Name
@ViewBag.Email
@ViewBag.Department
@ViewBag.Role

資料一多,很容易開始混亂。

如果改成 ViewModel:

public class UserDetailViewModel
{
    public string Name { get; set; }

    public string Email { get; set; }

    public string Department { get; set; }

    public string Role { get; set; }

    public DateTime CreateTime { get; set; }

    public bool CanEdit { get; set; }
}

Controller:

public IActionResult Detail()
{
    UserDetailViewModel model = new UserDetailViewModel
    {
        Name = "Abbie",
        Email = "abbie@example.com",
        Department = "資訊部",
        Role = "使用者",
        CreateTime = DateTime.Now,
        CanEdit = true
    };

    return View(model);
}

View:

@model UserDetailViewModel

<h1>@Model.Name</h1>

<p>@Model.Email</p>
<p>@Model.Department</p>
<p>@Model.Role</p>

@if (Model.CanEdit)
{
    <button type="button">編輯</button>
}

這樣 View 很清楚知道:

自己會收到哪些資料

三種方式要怎麼選?

可以先用最簡單的方式判斷:

只是少量簡單資料
↓
ViewData 或 ViewBag

一整組有結構的頁面資料
↓
Model

例如:

頁面標題
成功訊息
簡單提示

可能會用:

ViewData
ViewBag

但像:

使用者詳細資料
查詢結果清單
表單內容
Dashboard

通常比較適合:

Model / ViewModel

這不是絕對規定,還是要看專案原本的寫法。

Model 也可以是一個清單

Model 不一定只能是一筆資料。

例如 Controller:

public IActionResult Index()
{
    List<UserViewModel> users = new List<UserViewModel>
    {
        new UserViewModel
        {
            Name = "Abbie",
            Email = "abbie@example.com"
        },
        new UserViewModel
        {
            Name = "Amy",
            Email = "amy@example.com"
        }
    };

    return View(users);
}

View 就可以寫:

@model List<UserViewModel>

然後:

@foreach (var user in Model)
{
    <div>
        <h2>@user.Name</h2>
        <p>@user.Email</p>
    </div>
}

這時:

Controller 傳的是 List<UserViewModel>

View 也要接 List<UserViewModel>

所以 Model 還有一個重要觀念:

兩邊型別要對得上

如果 Controller 沒傳資料呢?

假設 View 寫:

@model UserViewModel

<h1>@Model.Name</h1>

但是 Controller:

public IActionResult Index()
{
    return View();
}

沒有傳入 UserViewModel

這時 Model 可能是 null

如果直接:

@Model.Name

就可能發生錯誤。

所以看到:

Model 是 null

我會先往 Controller 查:

有沒有取得資料?
↓
return View() 有沒有把資料傳進去?
↓
View 的 @model 型別有沒有一致?

不要只是在 View 裡一直補:

@if (Model != null)

因為真正問題可能是 Controller 根本漏傳資料。

如果 Model 型別不一致呢?

假設 View:

@model UserViewModel

但是 Controller 傳:

ProductViewModel product = new ProductViewModel();

return View(product);

那就會變成:

View 預期
UserViewModel

Controller 實際傳入
ProductViewModel

型別對不上,執行時就可能出錯。

所以我會先記:

Controller 傳入的 Model 型別
要和 View 的 @model 對得上

那 Controller 的資料通常從哪裡來?

這篇先不深入 Service 和資料庫。

現在先假設 Controller 已經有資料。

例如:

UserViewModel model = new UserViewModel
{
    Name = "Abbie"
};

但實際企業專案更常看到:

var model = _userService.GetUser(id);

return View(model);

也就是:

Controller
不一定自己建立資料

可能先去呼叫 Service
↓
Service 取得、處理資料
↓
Controller 再傳給 View

這部分後面會再往下追。

目前先把:

Controller → View

這段弄懂。

React 裡可以怎麼對照?

以前寫 React 時,可能會有:

<UserCard
    name="Abbie"
    email="abbie@example.com"
/>

然後 Component:

function UserCard({ name, email }) {
    return (
        <>
            <h1>{name}</h1>
            <p>{email}</p>
        </>
    );
}

可以先把概念對照成:

React
Component 把資料透過 Props 傳給另一個 Component

MVC
Controller 把 Model 傳給 View

例如:

return View(model);

然後:

@model UserViewModel

@Model.Name

兩邊不是完全相同的技術,但共通點都是:

畫面需要資料
就要有人明確把資料交給它

實際追企業專案時,我會怎麼找資料從哪裡來?

假設 View 裡看到:

@model UserDetailViewModel

<h1>@Model.Name</h1>

但我不知道 Name 從哪裡來。

我現在會往回追:

第一步
看 @model 是哪個型別

↓

第二步
回到對應 Controller Action

↓

第三步
找 return View(...)

↓

第四步
看傳進 View 的是哪個變數

↓

第五步
再找這個變數從哪裡取得

例如:

public IActionResult Detail(int id)
{
    var model = _userService.GetDetail(id);

    return View(model);
}

我就知道:

View 裡的 Model
↓
來自 Controller 的 model
↓
model 又來自 _userService.GetDetail(id)

這時再繼續往 Service 追。

所以實際追資料時,可以從 View 反推:

@Model.Name
↓
@model 是什麼型別?
↓
Controller 傳了什麼?
↓
return View(model)
↓
model 從哪裡來?

這篇先記住什麼?

Controller 要把資料交給 View,最常見可以先認識三種方式:

ViewData
ViewBag
Model

ViewData

ViewData["UserName"] = "Abbie";

View:

@ViewData["UserName"]

ViewBag

ViewBag.UserName = "Abbie";

View:

@ViewBag.UserName

Model:

return View(model);

View:

@model UserViewModel

@Model.Name

可以先這樣選:

少量、簡單的額外資料
↓
ViewData / ViewBag

一整組結構明確的頁面資料
↓
Model

實際追企業專案時,看到:

@Model.Name

不要只盯著 View。

可以反著追:

View 的 @model
↓
Controller
↓
return View(model)
↓
model 從哪裡來

這樣就開始能找到「畫面上的資料到底是哪裡生出來的」。

下一篇就接著拆這些很容易混在一起的名稱:

@model、ViewModel、Entity 和 DTO 到底是什麼?為什麼看起來全部都只是在裝資料?


上一篇
Day 18|`return View()` 沒寫檔名,MVC 怎麼找到對應的 `.cshtml`?
系列文
學過一點 React,卻被撈進 C#:新手用 AI 硬啃 MVC 企業專案的 3 個月實錄19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言