我們在前一篇文章中,實作了最簡單的 Controllers 與 Views,也看到了這兩個物件之間的關聯性,但是 MVC 中還少一個東西,就是 Models,但還是老樣子,雖然很多書都用 LINQ to SQL 或 Entity Framework 來當 Models,但是我仍然堅持要由最簡單最原始的方式來實作,所以我們一樣由類別來實作 Models。
我們在前一篇文章中,實作了最簡單的 Controllers 與 Views,也看到了這兩個物件之間的關聯性,但是 MVC 中還少一個東西,就是 Models,但還是老樣子,雖然很多書都用 LINQ to SQL 或 Entity Framework 來當 Models,但是我仍然堅持要由最簡單最原始的方式來實作,所以我們一樣由類別來實作 Models。
首先,在 Models 資料夾中加入自訂的類別 (以 Northwind 資料庫為範例):
namespace MvcApplication1.Models
{
public class Customer
{
public string CustomerID { get; set; }
public string CompanyName { get; set; }
public string ContactName { get; set; }
public string ContactTitle { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Region { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string Phone { get; set; }
public string Fax { get; set; }
}
}
然後加入一個讀取資料的物件:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
namespace MvcApplication1.Models
{
public class CustomerDataContext
{
public static List<Customer> LoadCustomers()
{
List<Customer> customers = new List<Customer>();
SqlConnection conn = new
SqlConnection("initial catalog=northwind; integrated security=SSPI");
SqlCommand cmd = new SqlCommand("SELECT * FROM Customers", conn);
conn.Open();
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
Customer customer = new Customer();
for (int i = 0; i < reader.FieldCount; i++)
{
PropertyInfo property =
customer.GetType().GetProperty(reader.GetName(i));
property.SetValue(customer,
(reader.IsDBNull(i)) ? "[NULL]" : reader.GetValue(i), null);
}
customers.Add(customer);
}
reader.Close();
return customers;
}
}
}
接著,修改 MyController,加入一個 Customers 方法,並且由 CustomerDataContext 將 Customer 資料讀出來,再交給 View:
public ActionResult Customers()
{
List<Models.Customer> customers = Models.CustomerDataContext.LoadCustomers();
return View(customers);
}
再來,和前一篇文章一樣,我們手動加入一個新的 View 在 /Views/My 資料夾下,名稱為 Customers:
@model IList<MvcApplication1.Models.Customer>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>List Northwind Customers</title>
</head>
<body>
<div>
<p>
Customer List:</p>
<table cellpadding="9" cellspacing="0" border="1">
<tr>
<td>
ID
</td>
<td>
Company Name
</td>
<td>
Contactor Name
</td>
<td>
Address
</td>
<td>
Phone
</td>
<td>
Fax
</td>
</tr>
@foreach (var modelItem in this.Model)
{
<tr>
<td>@modelItem.CustomerID
</td>
<td>@modelItem.CompanyName
</td>
<td>@modelItem.ContactName
</td>
<td>@modelItem.Country + @modelItem.City + @modelItem.Address
</td>
<td>@modelItem.Phone
</td>
<td>@modelItem.Fax
</td>
</tr>
}
</table>
</div>
</body>
</html>
看起來和前一篇很像,但我們加了點工,為了要讓 View 認識我們傳入的 Model 的型別,我們先在第一行宣告了 Model 的型別,即使用 @model 指令,設定 Model 的型別,如此我們才可以在 Visual Studio 的編輯器中享有強型別的好處。然後加入一個表格,將資料繫結進去,語法一樣是使用 foreach,但這次資料來源不是 ViewData,而是 this.Model,this.Model 則是在 Controller 的程式中設定給 View 參數的物件。
執行起來如下圖:
只有列表還不夠,我們會想要實作一個 Master/Detail 的應用程式,所以除了 List 以外,我們還要有個 Detail,所以我們先修改 MyController,加入檢視 Detail 資料的動作:
public ActionResult Details(string CustomerID)
{
var customer = from customerItem in Models.CustomerDataContext.LoadCustomers()
where customerItem.CustomerID == CustomerID
select customerItem;
if (customer.Count() == 0)
return new HttpNotFoundResult();
else
return View(customer.First());
}
然後再於 Views 中加入一個 Details.cshtml (Razor 的預設副檔名都是 cshtml,若是 ASPX 則是 .aspx):
@model MvcApplication1.Models.Customer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<title>Customer Detail</title>
</head>
<body>
<div>
<ul>
<li>ID: @this.Model.CustomerID</li>
<li>Company Name: @this.Model.CompanyName</li>
<li>Contactor Name: @this.Model.ContactName</li>
<li>Contactor Title: @this.Model.ContactTitle</li>
<li>Address: @this.Model.Country @this.Model.City @this.Model.Address</li>
<li>Phone: @this.Model.Phone</li>
<li>Fax: @this.Model.Fax</li>
<li>Region: @this.Model.Region</li>
</ul>
@Html.ActionLink("Return To List", "Customers")
</div>
</body>
</html>
在明細頁的部份,我們使用了一個 HtmlHelper 的方法 ActionLink,它會產生連結到指定 Action 的頁面的連結,因為我們要回到列表頁,所以直接傳入 Customers 即可。接著,我們要修改列表頁,讓它可以進入明細頁,所以我們修改了 CustomerID 這一欄,同樣是設定 Html.ActionLink,但這次我們會傳入參數:
@foreach (var modelItem in this.Model)
{
<tr>
<td>@Html.ActionLink(modelItem.CustomerID, "Details", new { CustomerID = modelItem.CustomerID })
</td>
<td>@modelItem.CompanyName
</td>
<td>@modelItem.ContactName
</td>
<td>@modelItem.Country + @modelItem.City + @modelItem.Address
</td>
<td>@modelItem.Phone
</td>
<td>@modelItem.Fax
</td>
</tr>
}
完成後,瀏覽 /My/Customers,會看到原本 CustomerID 的部份會變成連結:
點進去後會變成明細頁:
PS: 有趣的是,如果參數是是字串,產生的 URL 會是 "/Action?name=value",但如果是整數,則會直接附加在 Action 之後,如 "/Action/{value}"。
Reference:
http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-2