MVC ViewTemplate和ModelMetadata詳解-MVC原理系列10

上節:ASP.NET MVC Controller異步機制-MVC原理系列9,講述了ASP.NET MVC Controller 異步機制的相關知識。

本節你講解:ViewTemplate和ModelMetadata這兩方面的內容,讓你更深入的理解ASP.NET MVC的原理,從而更有助於自已實現MVC擴展,創造出屬於自己的模塊。

今天的內容主要爲:

  • Templated view helpers:根據Model生成Html控件元素
  • Model Binding:自動映射和解析用戶提交的數據
  • Integrating validation:集成客戶端認證

ASP.NET web應用程序的數據交互其實就是客戶端表單數據和.NET對象(Model)之間的轉化。下圖說明了這個問題:

clip_image002

在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。

  1. 既然MVC以模板的方式呈現UI,那麼是什麼影響MVC的選擇呢?以下因素按優先級影響到MVC框架對模板的選擇:
  2. 在EditorFor方法中顯示指定的模板名稱,Html.EditorFor(x => x.SomeProperty , “My Template”)。
  3. 對應Model的元數據描述,比如在屬性上添加特性[UIHint(“My Template”)]
  4. Model的元數據描述的數據類型,比如[DataType(DataType.EmailAddress)]
  5. 對應屬性的真實.NET 類型
  6. 對於可以被轉化成string的簡單類型,使用String模板
  7. Model的父類屬性也會被轉化
  8. 如果屬性實現了IEnumable,將選擇Collection模板
  9. 最後使用Object模板

 

ModelMetadata

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

clip_image002[5]

可以看到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就能保留原有的支持了。

image

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

image

 

當我們需要用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     } }


http://www.th7.cn/Program/net/201307/143648.shtml

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章