[ASP.NET MVC] LINQ 查詢結果送至 View 與 Model 綁定

基礎課程裡 LINQ 查詢結果要顯示在 View 上,通常是一筆 record 或多筆成一個 List,但實務上有更多需求例如 join 和選取特定 field ,下面將網路上找來的教學做一個筆記。


查詢一筆資料,綁定傳給 View

例如有一個資料表 Orders,內有三筆資料,已建立 Entity 叫 dbtestEntities:

查主鍵 OrderId 為 102 的資料,可用 .Find() 方法,回傳為一個 Orders 物件。

Controller:
using prjMVC.Models;
namespace prjMVC.Controllers
{
  public class HomeController : Controller
  {
    dbtestEntities db = new dbtestEntities();
    public ActionResult QueryOneRecord()
    {
      Orders result = db.Orders.Find(102);
      return View(result);
    }
  }
}

查到的 result 是單一 Orders 物件,直接帶去 View。

加入 QueryOneRecord 的檢視時,模型類別要選 Entity Framework 產生的 Orders 類別,或是手動在 View 最上面寫:

@model prjMVC.Models.Orders
<div>
  <p>
    OrderId : @Model.OrderId
  </p>
  <p>
    ProductId : @Model.ProductId
  </p>
  <p>
    Quantity : @Model.Quantity
  </p>
  <p>
    Date : @Model.Date
  </p>
</div>

結果:


查詢多筆資料,傳給 View

例如有一個資料表 Products內有七筆資料:

要查詢「Price 大於 30 的資料」,Controller:

using prjMVC.Models;
namespace prjMVC.Controllers
{
  public class HomeController : Controller
  {
    dbtestEntities db = new dbtestEntities();
    public ActionResult QueryList()
    {
      IQueryable<Products> result = db.Products.Where(m => m.Price > 30);
    //或 IQueryable<Products> result = from prod in db.Products 
    //                                 where prod.Price > 30 
    //                                 select prod;
      return View(result);
    }
  }
}

新增 QueryList 的檢視,綁定的模型類別用 IQueryable<prjMVC.Models.Products>,或是如 List 範本產生的 IEnumerable<prjMVC.Models.Products>:

@model IQueryable<prjMVC.Models.Products>
<table border="1">
    <tr>
      <th>ProductId</th>
      <th>Name</th>
      <th>Price</th>
    </tr>
    @foreach(var item in Model)
    {
        <tr>
            <td>@item.ProductId</td>
            <td>@item.Name</td>
            <td>@item.Price</td>
        </tr>
    }
</table>

結果:

Controller 傳給 View 的是集合 IQueryable<Products>,自然在 View 裡就要宣告 @model 為 IQueryable<Products> 綁定 Model 為傳入資料,始可對他使用 foreach 遍尋,也可以在 LINQ 查詢完後接 .ToList() 轉型,若有轉為 List 就要在 View 以 List<prjMVC.Models.Products> 來綁定


Join 並只選取部份欄位

一起看以上 Orders 和 Products 兩個資料表:

兩資料表以 ProductId 來 join ,若 SQL 中執行 select * from Orders as o join Products as p on o.ProductId = p.ProductId; 指令,得到的結果為:

用了 Join 會面臨一個問題,就是沒有哪個類別同時包含兩個資料表類別的屬性,所以這裡要用新的類別去接,方法又分匿名類別和 ViewModel


Join 並只選取部份欄位 by anonymous class

如果只在 Controller 之中使用查詢結果,其實用匿名類別即可,例如:

using prjMVC.Models;
namespace prjMVC.Controllers
{
  public class HomeController : Controller
  {
    dbtestEntities db = new dbtestEntities();
    public ActionResult JoinTwoTable()
    {
      var result = from ord in db.Orders
                join prod in db.Products
                on ord.ProductId equals prod.ProductId
                select new            // 匿名類別
                {
                  OrderId = ord.OrderId,
                  Name = prod.Name,
                  Price = prod.Price,
                  Quantity = ord.Quantity
                };
      string output = string.Empty;
      foreach(var item in result)
      {
        output += $"{item.OrderId} {item.Name} : {item.Price},{item.Quantity}<br />";
      }
      return Content(output);
    }
  }
}

滑鼠移到 var result 上可看到是匿名類別

結果:

這個適合在沒有要傳到 View 的操作,例如 Ajax 來 action 取資料,用匿名類別即可。


Join 並只選取部份欄位 by ViewModel

但是要將 LINQ 查詢結果傳至 View ,顯然匿名類別就無法在 View 中做綁定(View 中不知道要 @model 什麼類別),這個解決辦法是寫一個為這個 action 打造的 ViewModel,例如新增一個 ~/ViewModels/ 資料夾,新增一個類別叫 VMJoinTwoTable:

namespace prjMVC.ViewModels
{
  public class VMJoinTwoTable
  {
    public int OrderId { get; set; }   // 這些屬性的類型是根據 Entity 中對應
    public string Name { get; set; }   // 的屬性的類型,完全一樣才行
    public Nullable<int> Price { get; set; }
    public Nullable<int> Quantity { get; set; }
  }
}

接下來 LINQ 查詢結果用這個 ViewModel 來實體化,Controller:

using prjMVC.Models;
using prjMVC.ViewModels;
namespace prjMVC.Controllers
{
  public class HomeController : Controller
  {
    dbtestEntities db = new dbtestEntities();
    public ActionResult JoinTwoTable()
    {
      var result = from ord in db.Orders
                join prod in db.Products
                on ord.ProductId equals prod.ProductId
                select new VMJoinTwoTable       // 使用 ViewModel 接
                {
                  OrderId = ord.OrderId,
                  Name = prod.Name,
                  Price = prod.Price,
                  Quantity = ord.Quantity
                };
      return View(result);
    }
  }
}

將滑鼠移到 var result 上會得知 result 有確定的類型 IQueryable<VMJoinTwoTable> 了,所以也可以直接宣告這個類別。

然後新增 JoinTwoTable 的檢視,可用 List 範本選 ~/ViewModels/VMJoinTwoTable 模型類別:

或自己手動在 View 中 @model 綁定 IQueryable<prjMVC.ViewModels.VMJoinTwoTable> 或如 List 範本產生的IEnumerable<prjMVC.ViewModels.VMJoinTwoTable>:

@model IEnumerable<prjMVC.ViewModels.VMJoinTwoTable>
<table border="1">
  <tr>
    <th>OrderId</th>
    <th>Name</th>
    <th>Price</th>
    <th>Quantity</th>
  </tr>
  @foreach(var item in Model)
  {
    <tr>
      <td>@item.OrderId</td>
      <td>@item.Name</td>
      <td>@item.Price</td>
      <td>@item.Quantity</td>
    </tr>
  }
</table>

結果:


後記:LINQ Join 的 method syntax

上述的 LINQ Join 語法是 query syntax,少數比 method syntax 還好寫的,下面是 LINQ 的 Join 的 method syntax:

var result = db.Orders.Join(db.Products,
            ord => ord.ProductId,
            prod => prod.ProductId,
            (ord, prod) => new VMJoinTwoTable
                {
                   OrderId = ord.OrderId,
                   Name = prod.Name,
                   Price = prod.Price,
                   Quantity = ord.Quantity
                });

留言