ASP.NET MVC —— Model之一模型模板


Mvc model系列文章主要分為三部分:Model Templates,Model Binding,Model Validation。本篇文章主要內容包括下面三個部分:

A.使用模板視圖助手

B.自定義視圖模板系統

C.理解元數據提供體系

一、使用模板視圖助手

1.1助手體驗

  模板視圖助手,我理解為MVC提供的根據model中定義的數據類型,來生成視圖(View)標簽的助手。顯而易見其好處,當我們更改model中的數據類型時,不用擔心要更改view。下面我們就來體驗一下吧。

先建立一個空的mvc解決方案,起名為ModelTemplate,如下圖

 

然后在models中新建一個類,在此只是為了演示,所以沒有把下面三個類分開

View Code
    //定義人員類

    public  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; }

        public Role Role { get; set; }

    }

    //定義人員住址類

    public class Address

    {

        public string Line1 { get; set; }

        public string Line2 { get; set; }

        public string City { get; set; }

        public string PostalCode { get; set; }

        public string Country { get; set; }

    }

    //定義人員角色類

    public enum Role

    {

        Admin,

        User,

        Guest

    }

然后在controllers中添加個HomeControl,在Index中添加下面的代碼:

View Code
            Person myPerson = new Person

            {

                PersonId = 1,

                FirstName = "",

                LastName = "占朋",

                BirthDate = DateTime.Parse("1988-1-15"),

                HomeAddress = new Address

                {

                    Line1 = "倉山區",

                    Line2 = "南后街",

                    City = "福州",

                    Country = "中國",

                    PostalCode = "350000"

                },

                IsApproved = true,

                Role = Role.User

            };

            return View(myPerson);

 

接着生成一下解決方案,在Index上面右擊,添加一個視圖:

 

在視圖中添加下面的代碼,並運行

View Code
<h4>人員</h4>

<div class="field">

<label>姓名:</label>

@Html.EditorFor(x => x.FirstName)

@Html.EditorFor(x => x.LastName)

</div>

<div class="field">

<label>是否通知:</label>

@Html.EditorFor(x => x.IsApproved)

</div>

運行結果為:

 

會發現mvc framework為我們根據我們定義的數據類型生成了input和checkbox。當我們將IsApproved改為字符串,運行結果為:

 

不用更改前端,同樣模板視圖助手會根據類型去生成input。這樣模板視圖助手是不是很好用呢?除了可以生成input還可以生成其他的,如果我們想讓前端顯示的是只讀的話,可以使用:@Html.DisplayFor(x=>x.IsApproved)。運行結果為:

 

除了可以使用強類型的,還可以使用下面的用法,后面直接跟出字段的字符串:@Html.DisplayText("IsApproved"),這樣的結果和上面的一致。

除了上面的@Html.DisplayFor(x=>x.IsApproved),Html.EditorFor(x =>x.FirstName),還有Html.Display("FirstName"),,Html.Label("FirstName"),Html.LabelFor(x=> x.FirstName),Html.DisplayText("FirstName"),  Html.DisplayTextFor(x =>x.FirstName)可以使用。如果想了解個清楚的話,可以自行嘗試。建議大家都使用強類型的表示,可以減少錯誤。注意上面生成的源文件對應的html:

 

 1.2支架模板

我們已經體驗了模板助手的快感,下面就會有個問題,既然他會根據類型來判斷要生成的字段,如果我們想看到所有的字段,是不是要一個個打出來呢?肯定可以有更簡潔的方法,那就是我們要看的一個支架模板,支架模板有三個DisplayForModel,EditorForModel,LabelForModel。如果我們想顯示上面的所有字段,我們就是用一個EditorForModel試試。為了看到生成的checkbox,我把IsApproved還改為布爾型。

 

是不是比我們原來的方法要快捷呢?快的同時,細心的你肯定會發現上面的問題

A.時間字段變長了,
B.地址字段沒有顯示出來
C.還顯示出了用戶ID,通常情況我們不想讓用戶看到這個東西的

如果你是追求完美的人,那么你一定會先想到

D.顯示的lable標簽是英文的,我想看到的是中文的

看來支架模板不怎么理想啊,出現了這么多問題。下面我們就來一一解決上面出現的問題。解決問題之前,我們先來引入一個名詞,元數據(Metadata)在此你就把他看做描述字段的一些標簽吧。然后我們就一一的解決問題

A問題的解決辦法就是使用        

[DataType(DataType.Date)]

public DateTime BirthDate { get; set; }

在DataType上面按F12,會發現其實一個枚舉類型,具體的作用可以看其摘要。還可以設置為其他的類型。在此不一一的說明。

B:問題的解決辦法:先分析一下為什么不能顯示出地址,因為地址的類型是一個類,不是簡單類型,所以我們的Framework為了尊重這個類,沒有推斷出來其字段的類型,所以只有使用前面的@Html.EditorFor(m=>m.HomeAddress),我們列出address的字段時,才可以去推斷。

C:問題有兩種解決方法,一種是單純的為了顯示,不用把Idhidden標簽來呈現在html中,一種要對其操作,比如說修改了,我們通常使用id作為標識符,只是把其隱藏起來。

下面分別給出兩種解決方法:

1[ScaffoldColumn(false)]//前端不會顯示Hidden

 public int PersonId { get; set; }

2、[HiddenInput(DisplayValue=true)]如果是true的話,前端會呈現出值和前端生成的hidden,如果是false的話,前端只生成的hidden

D的解決方法是:使用[Displayname=””)]。接下來看一下顯示的結果:

 

對於元數據,還有個[UIHint("")]沒有使用上,其作用是把原來的屬性的類型按指定的類型顯示,其“”里面可以是Boolean、Collection、Decimal、EmailAddress、HiddenInput、HiddenInput、MultilineText、Object、Password、String、Text、Url。其顯示結果根據意思可以看出來,具體的查msdn或參考老A文章,如果不能轉化的,會報錯,如把一個字符串的轉化成Boolean的,會提示無法轉化。

除此之外,還有個較為重要的是[MetadataType(typeof(ClassName))]元數據,經常用在ORM方面,由於ORM生成的實體如果修改后,編譯之后就會把修改的內容刪除了,所以可以使用

這個元數據把兩個類合並在一起,互相協同工作。但是前提是要聲明類為partial,下面還以人員為例:

假設下面的為orm生成的類:

View Code
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; }

public Role Role { get; set; }

}

通過MetadataType數據源把其與PersonMetadataSource類協同工作,主要是讓姓多行顯示,其代碼如下:

View Code
    [MetadataType(typeof(PersonMetadataSource))]

    public partial class Person

    {

    }

    public partial class PersonMetadataSource

    {

        [UIHint("MultilineText")]

        public string FirstName { get; set; }

    }

其效果圖為:

  

對於其他兩個支架模板用法相似,在此不再贅述。

二、自定義視圖模板系統

2.1自定義模板的舉例

  有時,我們想讓指定值顯示為自己想要的效果,那么我們應該可以使用自定義視圖模板。比如用戶角色想出現一個ddl的效果,當然可以使用Html.DropDownListFor,還有另外一種方法是在~/Views/Shared 文件夾新建一個文件EditorTemplates,里面添加一個視圖,但是這個是分部視圖。如圖所示:

 

注意視圖名稱一定要和前面要展示的類型相一致,這里是和Role保持一致,因為到時間顯示的時間,文件名(類型名)是要作為查找的參數。不一致的話,是查找不到的。把下面的代碼貼到剛才創建的文件中:

 

View Code
@using ModelTemplate.Models

@model Role

<select id="Role" name="Role">

    @foreach (Role value in Enum.GetValues(typeof(Role))) {

    <option value="@value" @(Model == value ? "selected=\"selected\"" : "")>@value

    </option>

    }

</select>

 

運行就可以看到用戶角色是使用ddl來顯示的了,當然還有個好處就是復用了,其他有使用到該字段的視圖也會以ddl來呈現,如果只想在某個controller顯示的話,可以使用/Views/<controller>/EditorTemplates,該路徑是一個指定的路徑,不可以改變的,不能把EditorTemplate改為其他的字母。

下面為用戶角色顯示的效果圖:

 

2.2使用模板的順序

善於思考的你,看到上面的例子,肯定會想為什么不使用原來的模板了呢,而使用自定義模板,那么什么時間使用內置模板呢?這里面是有個順序的。下面稍微列出其順序:

1.Html.EditorFor(m => m.SomeProperty,"MyTemplate")因為我們已經制定了要使用MyTemplate模板,所以排在最前面。
2.被指定了元數據的模版,如UIHint
3.被指定為元數據的數據類型關聯的模版,如DataType特性
4.自定義在EditorTemplates、DisplayTemplates(EditorTemplate類似)里面的模板
5.最后就是內置的模板
注意:上面的23容易弄混。一個是元數據模板,一個是元數據的數據類型。我們可以使用一個例子來說明,包裝IsApproved字段。

通過UIHintDataType以及自定義的模板,來說明,同時使用前兩個的話顯示的是多行,如果同時使用后兩個的話,實現的是密碼。其中代碼如下:

View Code
        [UIHint("MultilineText")]

        [DataType(DataType.Password)]

        public bool IsApproved { get; set; }

@model bool

<select id="Role" name="Role">

    <option  value="@Model" @(Model == true ? "selected=\"selected\"" : "")>通知</option>

    <option value="@(!Model)" @(Model == false ? "selected=\"selected\"" : "")>未通知</option>

</select>

可以自行刪減,來觀察其效果。除了上面的情況,可能還有其他的情況,具體的遇到了再去研究。

三、理解元數據提供體系

  截止到現在,我們已經看了前面的一些例子,是什么在我們上面的例子中為我們默默的服務着呢?前面有提到過元數據,那么是誰創造的元數據呢?答案是DataAnnotationsModelMetadataProvider類。是它來檢查和處理我們加載類前面的特性的,然后根據其特性來呈現在view中。可以使用F12來看其定義,會發現其實它是繼承了AssociatedMetadataProvider一個抽象類,那么我們可以理解為:AssociatedMetadataProvider為元數據的使用提供了一些方法如創建元數據方法,然后DataAnnotationsModelMetadataProvider類重寫了他的方法,最后一些屬性或者說是特性是屬於元數據的。繞了幾個彎,還是使用老A的一張圖比較好說明:

   看了這張圖,比較激動的童鞋肯定想創建一個CustomModelMetadataProvider繼承AssociatedMetadataProvider類,那么就讓我們試着重寫CreateMetadata方法,下面就以我IsApproved為例吧,因為翻譯為通知有點不妥,我想把他改為贊同。

在項目下建立一個文件夾,然后添加一個類

代碼如下:   

View Code
 public class CustomModelMetadataProvider : AssociatedMetadataProvider

    {

        protected override ModelMetadata CreateMetadata(

                                            IEnumerable<Attribute> attributes,

                                            Type containerType,

                                            Func<object> modelAccessor,

                                            Type modelType,

                                            string propertyName)

        {

            //ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor,

            //modelType, propertyName);

            ModelMetadata metadata =new ModelMetadata (this, containerType, modelAccessor,

            modelType, propertyName);

            if (propertyName != null && propertyName.Equals("IsApproved"))

            {

                metadata.DisplayName = "贊同";

            }

            return metadata;

        }

    }

 

最后在global文件中Application_Start()添加初始化代碼:

View Code
ModelMetadataProviders.Current = new CustomModelMetadataProvider();

  發現顯示的lable已經改過來了,但是其他添加的元數據不起作用了。如果想讓它起作用的話,肯定是要繼承DataAnnotationsModelMetadataProvider,然后要實現父類的方法,最后在添加自己的功能,這樣就可以看到我們想要的效果了。代碼如下:

View Code
protected override ModelMetadata CreateMetadata(

                                            IEnumerable<Attribute> attributes,

                                            Type containerType,

                                            Func<object> modelAccessor,

                                            Type modelType,

                                            string propertyName)

        {

            ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor,

            modelType, propertyName);

            //ModelMetadata metadata =new ModelMetadata (this, containerType, modelAccessor,

            //modelType, propertyName);

            if (propertyName != null && propertyName.Equals("IsApproved"))

            {

                metadata.DisplayName = "贊同";

            }

            return metadata;

        }

運行的時間,我們可以在創建元數據的時間設置斷點看看各個參數的含義,以更好的理解該類和方法。准備收工了,其他的方法就不在此一一實現。

四、總結:

  本文通過簡單的實例,來引出了模板助手,再實現了自定義模板,最終說明了模型模板的實現原理。文中可能有些名詞叫的不是很准確,一方面是因為自己的E文不好,另一個方面是為了更好的理解,如果有不足的地方請大牛們多多指點。附上文中的代碼:源碼。該代碼使用vs2010編寫。

五、參考文獻:

1、《pro asp.net mvc3 framework》

2http://home.cnblogs.com/u/mszhangxuefei/

3http://www.cnblogs.com/artech/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM