ASP.NET MVC 3.0 Razor檢視如何展示多實體
Razor檢視模型支援@model來初始化頁面物件型別,比如:
1 @model FlashTravel.Models.Traveller>
也可以是一個支援迭代的公開列舉器:
1 @model IEnumerable<FlashTravel.Models.Traveller>
當我們只關注對一個實體類進行呈現時,MVC 3.0的實現非常給力,Controller層只需要兩行程式碼:
public ActionResult Index()
{
var traveller = dbFlashTravel.Traveller.ToList();
return View(traveller);
}
ToList()方法返回的是List<T>集合,它實現(Implements)了介面IEnumerable<out T>,所以var型變數traveller被傳遞到View層時,可採用foreach進行遍歷:
@foreach (var item in Model) {
//......
}
再強大的框架,也只是把簡單的事變得更簡單,而問題一旦稍變複雜,多半都要靠聰明的大腦來解決,我挺喜歡去42區逛,其首頁的類似微博的即時資訊功能挺有意思;好歹自己也是做BS開發的,於是就採用MVC 3.0模擬一個玩玩。
其業務模型大概可以這樣描述:已登入會員可以發表一條碎碎念(文字限定142),與之相隨的圖片數量不限定(測試時只提供一張),同時可以被別的會員回覆。從該描述中提取出最基本的實體物件:會員(Traveller)、碎碎念(Chat)、碎碎念圖片(ChatImage)、碎碎念回覆(ChatReply)
來自VS2010的免費圖片:
很明顯,實體Chat的主鍵欄位ChatID關聯起了實體ChatImage(1對多)和實體ChatReply(1對多)。
Linq to SQL該ORM框架會自動把資料庫表一一對映為同名實體類(EF4.0道理亦然),這些實體類就像築造樓房的磚瓦一樣,成為各種業務模型組裝的最為基本的物件。基於上述分析,顯然發表碎碎念這一業務模型有三個最基本的實體類參與:Chat、ChatImage、ChatReply,為了更好地表述他們,我將該業務模型命名為ChatIntegration(碎碎念綜合),並採用一個檢視模型模式(ViewModel Pattern)類來進行封裝:
namespace FlashTravel.ViewModels
{
/// <summary>
/// 碎碎念綜合
/// </summary>
public class ChatIntegration
{
/// <summary>
/// 碎碎念
/// </summary>
public Chat ChatHeader { set; get; }
/// <summary>
/// 碎碎念圖片
/// </summary>
public IList<ChatImage> ChatImages { set; get; }
/// <summary>
/// 碎碎念回覆
/// </summary>
public IList<ChatReply> ChatReplys { set; get; }
}
}
上述程式碼表明,單個碎碎念業務模型包含著一個碎碎念主體叫ChatHeader,和若干圖片(複數),以及若干回覆(複數)。
一般來講,Model層在專案中往往就是起到一個承上啟下的作用(MVC或傳統的三層),如同小學時老師教我們八股式的寫作文一般:先開頭,接著鋪述一段,然後作一個承上啟下的段落,隨之進入下一段的描寫,最後以一個總結段結尾。這裡的業務模型(ChatIntegration)同理:第一段,在Controller層先撈取資料庫中的資料(基於Linq to SQL)將其儲存在業務模型中;第二段,業務模型將被拋給View層的頁面,通過Razor引擎遍歷呈現。
如前所述,接下來會做兩件事:先往下層看,瞧一下處於中間的業務模型(ChatIntegration)是如何得到資料庫資料;然後再擡頭仰視,看看業務模型(ChatIntegration)被拋給View後如何遍歷資料。
(一.)往下層看,按MVC 3.0的"分離關注點"思想,對於訪問資料庫的程式碼應該採用Repository設計模式,使得Controller層中的Action僅需呼叫Repository層中的相關函式就可得到資料集,從而很順暢地將函式返回值傳給View層,雖然"約定大於配置"很重要,但只要我的精簡尚未破壞MVC架構,姑且就"耦合"一下吧,我會把訪問資料庫的程式碼直接寫在名為Index的Action中:
public class ChatController : Controller
{
FlashTravelDataContext dbFlashTravel = new FlashTravelDataContext();
public ActionResult Index()
{
//查詢所有碎碎念
var chatIntegrationList = from chat in dbFlashTravel.Chat
join chatImage in dbFlashTravel.ChatImage
on chat.ChatID equals chatImage.ChatID into chatImages
join chatReply in dbFlashTravel.ChatReply
on chat.ChatID equals chatReply.ChatID into chatReplys
select new ChatIntegration
{
ChatHeader = chat,
ChatImages = chatImages.ToList(),
ChatReplys = chatReplys.ToList()
};
return View(chatIntegrationList);
}
}
通過宣告式的Linq查詢,我們得到了目前資料庫中所有的碎碎念記錄(實際中想必要根據時間進行過濾[比如只查詢最近一週]、並分頁呈現,這裡都忽略),沒有更多的程式碼了。大家會看到在基於內聯接的Linq查詢的後半段,可直接定義(select new ChatIntegation)每一個碎碎念模型中的屬性值。只要搞定一個ChatIntegation的賦值,那麼基於泛型集合的chatIntegrationList都會統統受到影響,最終它作為View(object model)的引數傳給檢視層。
(二.)擡頭仰視,檢視層會接受到我們載著滿滿資料的業務模型集合(ChatIntegation泛型集合):
1 @model IEnumerable<FlashTravel.ViewModels.ChatIntegration>
接下來,業務模型ChatIntegation集合至少需要一次foreach遍歷,用以將每一個ChatIntegation提取出來作進一步處理。就像我們在Controller層對資料作組裝一樣,只要對一個業務模型進行編碼,那麼基於泛型集合的每一個元素都會受到影響。
顯然,第一次的遍歷會在每個一個元素ChatIntegation中取到三個屬性:ChatHeader(碎碎念)、ChatImages(圖片)、ChatReplys(回覆),
發現圖片和回覆分別又是一個新的子泛型集合,於是對他們二位再分別作第二層foreach遍歷: @{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
@foreach (var item in Model)
{
<tr>
<td style="width: 150px; text-align: center;">
@item.ChatHeader.NickName
</td>
<td style="width: 350px; text-align: right;">
@item.ChatHeader.PublishTime
</td>
</tr>
<tr>
<td style="width: 150px; text-align: center;">
@foreach (var image in item.ChatImages)
{
<img alt="@image.ImageRemark" src="@image.ImageUrl" />
}
</td>
<td style="width: 350px; text-align: left;">
@item.ChatHeader.Content
</td>
</tr>
<tr>
<td colspan="2">
<br />
<span style="font-weight: bold;">FeedBack</span>
<hr />
</td>
</tr>
foreach (var reply in item.ChatReplys)
{
<tr>
<td colspan="2">
<table style="width: 100%;">
<tr>
<td>
@reply.PublishTime|@reply.NickName
</td>
</tr>
<tr>
<td>
@reply.Reply
</td>
</tr>
</table>
</td>
</tr>
}
}
</table>
最後呈現的效果便是:
一個Demo下來,真正需要手寫的程式碼仍不足50行,程式設計方式的理解還是最為重要
摘自 不覺流年似水