深入理解ASP.NET MVC(10)

來源:互聯網
上載者:User

系列目錄

前言

Model是MVC強大的機制之一,它是MVC架構中用戶端和服務端資料互動的核心機制。深入的理解Model有助於我們自己在MVC的基礎上擴充,也有助於我們創造出更具複用意義的軟體模組。主要包含以下議題:

  • Templated view helpers:根據Model產生Html控制項元素
  • Model Binding:自動對應和解析使用者提交的資料
  • Integrating validation:整合用戶端認證

我們知道ASP.NET web應用程式的資料互動其實就是用戶端表單資料和.NET對象(Model)之間的轉化。說明了這個問題:

在MVC中,眾多HTML Helper負責將Model轉化成Html標記,Model binding將使用者提交的資料轉化成Model。

 

Templated View Helpers

MVC2新增的Templated View Helpers指的是類似Html.TextBoxFor()之類的擴充方法。用這樣的方法來構造連結資料表單之類的的Html元素的話,是十分方便和智能的。這些方法會根據Model或Model屬性的類型自動決定轉換成什麼樣的Html元素,並自動使得Model Binding得以支援。

比如如果你有個屬性叫Approved,是個bool類型,那麼Html.EditorFor(x => x.Approved)將會轉化成一個check box。比如當調用Html.EditorFor()時,MVC需要選擇一個合適的模板呈現,因此模板可以理解成對某種資料結構的預定義的Html的呈現方式。先來看看MVC內建有哪些模板,下面的代碼是從TemplateHelpers中摘錄的:

static readonly Dictionary<string, Func<HtmlHelper, string>> defaultDisplayActions =            new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {                { "EmailAddress",       DefaultDisplayTemplates.EmailAddressTemplate },                { "HiddenInput",        DefaultDisplayTemplates.HiddenInputTemplate },                { "Html",               DefaultDisplayTemplates.HtmlTemplate },                { "Text",               DefaultDisplayTemplates.StringTemplate },                { "Url",                DefaultDisplayTemplates.UrlTemplate },                { "Collection",         DefaultDisplayTemplates.CollectionTemplate },                { typeof(bool).Name,    DefaultDisplayTemplates.BooleanTemplate },                { typeof(decimal).Name, DefaultDisplayTemplates.DecimalTemplate },                { typeof(string).Name,  DefaultDisplayTemplates.StringTemplate },                { typeof(object).Name,  DefaultDisplayTemplates.ObjectTemplate },            };        static readonly Dictionary<string, Func<HtmlHelper, string>> defaultEditorActions =            new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) {                { "HiddenInput",        DefaultEditorTemplates.HiddenInputTemplate },                { "MultilineText",      DefaultEditorTemplates.MultilineTextTemplate },                { "Password",           DefaultEditorTemplates.PasswordTemplate },                { "Text",               DefaultEditorTemplates.StringTemplate },                { "Collection",         DefaultEditorTemplates.CollectionTemplate },                { typeof(bool).Name,    DefaultEditorTemplates.BooleanTemplate },                { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },                { typeof(string).Name,  DefaultEditorTemplates.StringTemplate },                { typeof(object).Name,  DefaultEditorTemplates.ObjectTemplate },            };

從中可以看到內建的模板有哪些。總的來說,Html元素可以分為兩類,顯示類和編輯類。分別地,主要有兩大類的Helper,DisplayXXX、LabelXXX和EditorXXX。MVC架構套件含有DefaultDisplayTemplates和DefaultEditorTemplates分別負責完成render的工作。而TemplateHelpers類負責調配選擇使用那種模板。MVC內建的模板主要是簡單類型,對於複雜類型MVC支援使用者自己定義模板,自訂一個模板實際上就是建立ascx,然後放在合適的位置,由View引擎自行尋找。架構會審查/Views/Shared/DisplayTemplates//Views/Shared/EditorTemplates/目錄下的ascx檔案,並將其視為自訂模板載入,所以我們可以設計複雜的ascx模板,將他視為使用者控制項,然後簡單的使用TemplateHelper的各種方法來載入,這也不失為一種代替PartialView或ChildAction的方式。這裡的ascx檔案名稱要盡量對應類型名,因此,我們可以建立一個DateTime.ascx,並像下面這樣編輯代碼來“重載”MVC內建對DateTime資料類型的模板。

<%@ Control Language="C#" Inherits="ViewUserControl<DateTime?>" %> <%: Html.TextBox("",                                        /* Name suffix     */                  ViewData.TemplateInfo.FormattedModelValue, /* Initial value   */                  new { @class = "date-picker" }             /* HTML attributes */     ) %>

注意到,這裡用到ViewData.TemplateInfo.FormattedModelValue,而不是Model.ToString(),這樣可以充分利用ModelMetadata的特性,關於ModelMetaData將在以後更多的涉及。這裡也可以這樣寫:

<%@ Control Language="C#" Inherits="ViewTemplateUserControl<DateTime?>" %> <%: Html.TextBox("",                             /* Name suffix     */                  FormattedModelValue,            /* Initial value   */                  new { @class = "date-picker" }  /* HTML attributes */ ) %>

注意到這裡繼承的是ViewTemplateUserControl而不是上面的ViewUserControl。

既然MVC以模板的方式呈現UI,那麼是什麼影響MVC的選擇呢?以下因素按優先順序影響到MVC架構對模板的選擇:

  1. 在EditorFor方法中顯示指定的模板名稱,Html.EditorFor(x => x.SomeProperty , “My Template”)。
  2. 對應Model的中繼資料描述,比如在屬性上添加特性[UIHint(“My Template”)]
  3. Model的中繼資料描述的資料類型,比如[DataType(DataType.EmailAddress)]
  4. 對應屬性的真實.NET 類型
  5. 對於可以被轉化成string的簡單類型,使用String模板
  6. Model的父類屬性也會被轉化
  7. 如果屬性實現了IEnumable,將選擇Collection模板
  8. 最後使用Object模板

 

ModelMetadata

TemplateHelpers的確是完成render工作的最主要類,但事實上TemplateHelpers也僅僅負責render,它需要一個叫ModelMetadata的東西來為它提供資料,而ModelMetadata本身就像它的類名,意思是“模型中繼資料”,相當於一個描述資料的對象,這個對象需要ModelMetadataProvider來提供真正提供資料,可以協助理解:

可以看到MVC內建了DataAnnotationModelMetadataProvider來充當ModelMetadataProvider,它內建支援.NET中的Data Annotation特性,比如DisplayColumDisplayFormatRequired等,不僅如此DataAnnotationModelMetadataProvider還支援MVC特有的特性描述如:UIHint等。

可以像下面這樣指定一個ModelMetadataProvider:ConventionsMetadataProvider。

protected void Application_Start() {     AreaRegistration.RegisterAllAreas();     RegisterRoutes(RouteTable.Routes);     ModelMetadataProviders.Current = new ConventionsMetadataProvider(); }

ModelMetadataProvider本身是個抽象類別,可以從下面任意的類中繼承,推薦從DataAnnotationModelMetadataProvider繼承,這樣我們自訂的ModelMetadataProvider就能保留原有的支援了。

再來大概看看ModelMetadata有哪些屬性和方法,它們中的部分將被TemplateHelpers考察並影響到Html元素的呈現。其中的FromLambdaExpression()方法用於從Lambda運算式中得到ModelMetadata,這也是內建的TemplateHelpers要調用的方法。

 

當我們需要用Attribute描述Model類的時候,也許會碰到這樣的情況,Model本身是諸如ORM等工具產生的,不能直接修改這樣的類。於是MVC提供了[MetadataType]屬性來解決這種情形。通常自動產生的Model類是部分類:

public partial class Person {     public int PersonId { get; set; }     public string FirstName { get; set; }     public string LastName { get; set; }     public DateTime BirthDate { get; set; }     public Address HomeAddress { get; set; }     public bool IsApproved { get; set; } }

定義另一個對應的類,並用MetadataType特性標識,在其中定義一個內部類,標註上需要的特性描述即可。

[MetadataType(typeof(PersonMetadata))] public partial class Person {     // This class is only used as a source of metadata     private class PersonMetadata     {         [HiddenInput(DisplayValue = false)] public int PersonId { get; set; }         [DisplayName("First name")] public string FirstName { get; set; }         [DisplayName("Last name")] public string LastName { get; set; }          // Also add any other properties for which you want to supply metadata     } }

 

總結

以上僅僅從架構的角度闡述了關於Template和ModelMatedata的實現。更多細節還需要在實踐中多多留意。另外這部分內容還涉及到下一篇要談到的“模型繫結”,多做些相應的擴充比較有利於理解這部分內容。

勞動果實,轉載請註明出處:http://www.cnblogs.com/P_Chou/archive/2011/01/23/details-asp-net-mvc-10.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.