.NET/ASP.NETMVC 大型站點架構設計—遷移Model元數據設置項(自定義元數據提供程序)

閱讀目錄:數據庫

  • 1.需求背景介紹(Model元數據設置項應該與View綁定而非ViewModel)
    • 1.1.肯定問題域範圍(可使用DSL管理問題域前提是鎖定領域模型) 
  • 2.遷移ViewModel設置到外部配置文件(擴展Model元數據提供程序)
    • 2.1.實現元數據提供程序(簡單示例)

1.需求背景介紹(Model元數據設置項應該與View綁定而非ViewModel)

使用ASP.NETMVC構建普通的中小型站點可使用簡單的Model元數據設置方式來控制ViewModel如何顯示在View中,可是複雜的應用場景不會這麼簡單的就能完成;大型站點的ViewModel的體積很是大,真的大的超乎咱們的想象(固然這裏面有歷史緣由),這麼大的一個顯示實體咱們須要在不一樣的頁面中呈現它會很是的棘手;然而小型站點不太會碰見ViewModel在幾十個頁面中顯示的狀況出現,通常頁面也就是幾十個差很少了;編程

在大型電子商務應用中,UI層的一個ViewModel不只用來呈現數據還充當着與遠程SOA接口通信的DTO做用,若是爲告終構清晰徹底能夠將ViewModel與DTO分開,可是有時候咱們確實須要考慮額外的性能開銷(有時候咱們只能接受歷史遺留的問題,技術債務累積多久就要還多久);緩存

這篇文章將講解如何利用ASP.NETMVC開發大型站點時ViewModel中設置的元數據設置項隨着不一樣的業務View不一樣而調用不一樣的元數據設置項,簡單的講也就是咱們不會直接在ViewModel上應用元數據控制特性,而是經過將Model元數據設置項與具體的View綁定的方式來控制它在不一樣的View中運用不一樣的元數據控制項,元數據控制特性不會和具體的ViewModel綁定而是和具體的View綁定,由於只有View纔是固定呈現什麼內容,而ViewModel是用來共用的顯示數據項的容器,我將經過本篇文章來說解如何設計這樣的高擴展性的ASP.NETMVC ViewModel使用結構;服務器

1.2.肯定問題域範圍(可使用DSL管理問題域前提是鎖定領域模型)

在考慮使用配置文件將所須要的東西配置起來的時候,咱們須要先肯定到底須要將什麼配置起來;這就須要咱們先肯定問題域,其實這也就是面向DSL編程的方法;app

DSL:簡單理解爲面向特定領域的語言;該語言主要用來解決特定領域的實現問題,剛開始咱們能夠會把這個概念定義的過於龐大,但願能經過DSL解決一切領域問題,其實這是錯誤的;DSL實際上是一小部分領域問題的提煉,如:咱們這裏的將ModelMetadata設置特性從原來定義在ViewModel上的遷移到外部去,這其中的主要問題域就是將ModelMetadata設置項與View綁定,而不是ViewModel;框架

只有先準確的找到問題域以後咱們才能設計DSL來充分的表達這個問題域,經過XML能很好的表達任何特定領域結構的模型,固然你徹底能夠本身去設計DSL;ide

目前對ViewModel中設置的元數據控制特性都會做用於使用該ViewModel的全部View,咱們要解決的問題是將上圖中的ModelMetadata域提取出去與View進行綁定,從而獲得一個乾淨的ViewModel和靈活的面向View的元數據控制功能;當咱們成功遷移以後,咱們將獲得下圖中的結構;工具

最終咱們會得出這樣的一個知足實際需求的結構;性能

2.遷移ViewModel設置到外部配置文件(擴展Model元數據提供程序)

要想成功遷移設置項咱們必需要搞清楚ASP.NETMVC中Model元數據提供程序的原理,這樣咱們才能將原來獲取元數據的方式改變成咱們本身的獲取策略;在元數據提供程序對象模型中主要的功能分爲兩部分(這裏咱們只介紹獲取元數據過程):優化

咱們須要將BuildModelMetadata功能區域換成咱們本身的策略;

這樣咱們就能夠將一組強大的元數據提供程序植入到ASP.NETMVC框架的內部;

經過CustomModelMetadataProviderFactory建立用於獲取任何一個外部類型的元數據提供程序對象,好比:CustomModelMetadataProviderWithDb(用於數據庫的接口),CustomModelMetadataProviderWithConfig(用戶配置文件),CustomModelMetadataProviderWithService(遠程Service);

遷移ModelMetadate緩存數據(緊要關頭能夠進行內存優化)

在ASP.NETMVC內部提供了用來獲取某個ViewModel的ModelMetadata的提供程序,經過該入口咱們將能夠把Model元數據緩存在咱們本身的容器中,固然絕佳的緩存位置就是當前應用服務器的本地進程,這裏是最好的緩存位置,咱們緩存元數據主要不是爲了改變它的存放位置而是要改變它獲取的途徑和方式,這樣其實會有不少好處,好比:經過工具化管理內存中的緩存數據,對其進行壓縮等等,由於你已經能夠控制其獲取元數據的過程,這在緊要關頭可能就是救命稻草,這裏只是一點擴展性的介紹,固然要看具體的需求了,不過這確實是一個好的思路;

2.1.實現元數據提供程序(簡單示例)

View、ViewModel、ModelMetadata 映射設計:

 1 using System.Collections.Generic;
 2 using System.Linq;
 3 using System.Web.Mvc; 
 4 
 5 namespace MvcApplication4.Seed
 6 {
 7     public enum View
 8     {
 9         HomePage_Index,
10         HomePage_Edit
11     }
12     public enum ViewModel
13     {
14         Customer
15     }
16     public class ViewMappingModelMetadata
17     {
18         public View View { get; set; }
19         public ViewModel ViewModel { get; set; }
20         public ModelMetadata Metadata { get; set; }
21     } 
22 
23     public class ViewMappingModelMetadataCollection : Dictionary<View, List<ViewMappingModelMetadata>>
24     {
25         private static ViewMappingModelMetadataCollection coll = new ViewMappingModelMetadataCollection();
26         static ViewMappingModelMetadataCollection()
27         {
28             //在Homepage下的視圖———來自外部文件的接口,這裏只是示例顯示
29             coll.Add(View.HomePage_Index, new List<ViewMappingModelMetadata>());
30             coll[View.HomePage_Index].Add(new ViewMappingModelMetadata()
31             {
32                 View = View.HomePage_Index,
33                 ViewModel = ViewModel.Customer,
34                 Metadata =
35                     new ModelMetadata(CustomModelMetadataProviderWithConfig.CurrentProvider, typeof(Models.Customer),
36                     () => { return new Models.Customer().CustomerId; }, typeof(string), "CustomerId")
37                     {
38                         DisplayFormatString = @"HomePage\DisplayName:{0}"
39                     }
40             });
41             //在EditCustomer下的視圖——來自外部文件的接口,這裏只是示例顯示
42             coll.Add(View.HomePage_Edit, new List<ViewMappingModelMetadata>());
43             coll[View.HomePage_Edit].Add(new ViewMappingModelMetadata()
44             {
45                 View = View.HomePage_Edit,
46                 ViewModel = ViewModel.Customer,
47                 Metadata = new ModelMetadata(
48                     CustomModelMetadataProviderWithConfig.CurrentProvider, typeof(Models.Customer),
49                     () => { return new Models.Customer().CustomerId; }, typeof(string), "CustomerId")
50                     {
51                         DisplayFormatString = @"Edit\DisplayName:{0}"
52                     }
53             });
54         }
55         public static ViewMappingModelMetadataCollection Current
56         {
57             get { return coll; }
58         } 
59 
60         public ModelMetadata GetMetadataByView(View view, ViewModel model)
61         {
62             var metaList = from item in coll[view] where item.ViewModel == model select item.Metadata;
63             return metaList != null && metaList.Count() > 0 ? metaList.LastOrDefault() : null;
64         }
65     }
66 } 

這兩段是要被放到框架內部去完成的,這裏只是爲了演示其元數據的設置原理,因此簡單這麼寫;

System.Web.Mvc.ModelMetadataProvider 實現自定義元數據提供程序:

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Web.Mvc;
 4 
 5 namespace MvcApplication4.Seed
 6 {
 7     public class CustomModelMetadataProviderWithConfig : System.Web.Mvc.ModelMetadataProvider
 8     {
 9         private static CustomModelMetadataProviderWithConfig provider = new CustomModelMetadataProviderWithConfig();
10         public static CustomModelMetadataProviderWithConfig CurrentProvider
11         {
12             get { return provider; }
13         }
14 
15         public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType)
16         {
17             throw new NotImplementedException();//複雜類型實現,屬性的循環獲取
18         }
19 
20         public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
21         {
22             throw new NotImplementedException();//複雜類型實現,屬性的循環獲取
23         }
24 
25         public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType)
26         {
27             if (modelAccessor == null) return null;
28             if (System.Web.HttpContext.Current.Session["viewname"] == null) return null;
29             var result = ViewMappingModelMetadataCollection.Current.GetMetadataByView(
30                     (View)System.Web.HttpContext.Current.Session["viewname"], (ViewModel)System.Web.HttpContext.Current.Session["viewmodel"]);
31             if (modelAccessor != null)
32                 result.Model = modelAccessor().GetType().GetProperty("CustomerId").GetValue(modelAccessor());
33             return result;
34         }
35     }
36 }

Customer模型定義:

1 public class Customer
2 {
3     public string CustomerId { get; set; }
4 } 

在模型上咱們沒有應用任何一個 元數據控制特性,可是咱們將在界面上看到效果;

View 視圖定義:

 1 @model  MvcApplication4.Models.Customer 
 2 
 3 <table>
 4     <tr>
 5         <td>
 6             <h2>編輯模式.</h2>
 7             <h3>@Html.DisplayForModel(Model.CustomerId)</h3>
 8         </td>
 9     </tr>
10 </table> 
11 
12 @model  MvcApplication4.Models.Customer 
13 
14 <table>
15     <tr>
16         <td>
17             <h2>顯示模式.</h2>
18             <h3>@Html.EditorForModel(Model.CustomerId)</h3>
19         </td>
20     </tr>
21 </table> 
22 
23 這是兩種模型的呈現方式; 

咱們自動設置的元數據已經起到效果了;

 

相關文章
相關標籤/搜索