您好,登錄后才能下訂單哦!
閱讀目錄:
1.需求背景介紹(Model元數據設置項應該與View綁定而非ViewModel)
1.1.確定問題域范圍(可以使用DSL管理問題域前提是鎖定領域模型)
2.遷移ViewModel設置到外部配置文件(擴展Model元數據提供程序)
2.1.實現元數據提供程序(簡單示例)
使用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使用結構;
在考慮使用配置文件將所需要的東西配置起來的時候,我們需要先確定到底需要將什么配置起來;這就需要我們先確定問題域,其實這也就是面向DSL編程的方法;
DSL:簡單理解為面向特定領域的語言;該語言主要用來解決特定領域的實現問題,剛開始我們可以會把這個概念定義的過于龐大,希望能通過DSL解決一切領域問題,其實這是錯誤的;DSL其實是一小部分領域問題的提煉,如:我們這里的將ModelMetadata設置特性從原來定義在ViewModel上的遷移到外部去,這其中的主要問題域就是將ModelMetadata設置項與View綁定,而不是ViewModel;
只有先準確的找到問題域之后我們才能設計DSL來充分的表達這個問題域,通過XML能很好的表達任何特定領域結構的模型,當然你完全可以自己去設計DSL;
目前對ViewModel中設置的元數據控制特性都會作用于使用該ViewModel的所有View,我們要解決的問題是將上圖中的ModelMetadata域提取出去與View進行綁定,從而得到一個干凈的ViewModel和靈活的面向View的元數據控制功能;當我們成功遷移之后,我們將得到下圖中的結構;
最終我們會得出這樣的一個滿足實際需求的結構;
要想成功遷移設置項我們必須要搞清楚ASP.NETMVC中Model元數據提供程序的原理,這樣我們才能將原來獲取元數據的方式改變成我們自己的獲取策略;在元數據提供程序對象模型中主要的功能分為兩部分(這里我們只介紹獲取元數據過程):
我們需要將BuildModelMetadata功能區域換成我們自己的策略;
這樣我們就可以將一組強大的元數據提供程序植入到ASP.NETMVC框架的內部;
通過CustomModelMetadataProviderFactory創建用于獲取任何一個外部類型的元數據提供程序對象,比如:CustomModelMetadataProviderWithDb(用于數據庫的接口),CustomModelMetadataProviderWithConfig(用戶配置文件),CustomModelMetadataProviderWithService(遠程Service);
遷移ModelMetadate緩存數據(緊要關頭可以進行內存優化)
在ASP.NETMVC內部提供了用來獲取某個ViewModel的ModelMetadata的提供程序,通過該入口我們將可以把Model元數據緩存在我們自己的容器中,當然絕佳的緩存位置就是當前應用服務器的本地進程,這里是最好的緩存位置,我們緩存元數據主要不是為了改變它的存放位置而是要改變它獲取的途徑和方式,這樣其實會有很多好處,比如:通過工具化管理內存中的緩存數據,對其進行壓縮等等,因為你已經可以控制其獲取元數據的過程,這在緊要關頭可能就是救命稻草,這里只是一點擴展性的介紹,當然要看具體的需求了,不過這確實是一個好的思路;
View、ViewModel、ModelMetadata 映射設計:
using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace MvcApplication4.Seed { public enum View { HomePage_Index, HomePage_Edit } public enum ViewModel { Customer } public class ViewMappingModelMetadata { public View View { get; set; } public ViewModel ViewModel { get; set; } public ModelMetadata Metadata { get; set; } } public class ViewMappingModelMetadataCollection : Dictionary<View, List<ViewMappingModelMetadata>> { private static ViewMappingModelMetadataCollection coll = new ViewMappingModelMetadataCollection(); static ViewMappingModelMetadataCollection() { //在Homepage下的視圖———來自外部文件的接口,這里只是示例顯示 coll.Add(View.HomePage_Index, new List<ViewMappingModelMetadata>()); coll[View.HomePage_Index].Add(new ViewMappingModelMetadata() { View = View.HomePage_Index, ViewModel = ViewModel.Customer, Metadata = new ModelMetadata(CustomModelMetadataProviderWithConfig.CurrentProvider, typeof(Models.Customer), () => { return new Models.Customer().CustomerId; }, typeof(string), "CustomerId") { DisplayFormatString = @"HomePage\DisplayName:{0}" } }); //在EditCustomer下的視圖——來自外部文件的接口,這里只是示例顯示 coll.Add(View.HomePage_Edit, new List<ViewMappingModelMetadata>()); coll[View.HomePage_Edit].Add(new ViewMappingModelMetadata() { View = View.HomePage_Edit, ViewModel = ViewModel.Customer, Metadata = new ModelMetadata( CustomModelMetadataProviderWithConfig.CurrentProvider, typeof(Models.Customer), () => { return new Models.Customer().CustomerId; }, typeof(string), "CustomerId") { DisplayFormatString = @"Edit\DisplayName:{0}" } }); } public static ViewMappingModelMetadataCollection Current { get { return coll; } } public ModelMetadata GetMetadataByView(View view, ViewModel model) { var metaList = from item in coll[view] where item.ViewModel == model select item.Metadata; return metaList != null && metaList.Count() > 0 ? metaList.LastOrDefault() : null; } } }
這兩段是要被放到框架內部去完成的,這里只是為了演示其元數據的設置原理,所以簡單這么寫;
System.Web.Mvc.ModelMetadataProvider 實現自定義元數據提供程序:
using System; using System.Collections.Generic; using System.Web.Mvc; namespace MvcApplication4.Seed { public class CustomModelMetadataProviderWithConfig : System.Web.Mvc.ModelMetadataProvider { private static CustomModelMetadataProviderWithConfig provider = new CustomModelMetadataProviderWithConfig(); public static CustomModelMetadataProviderWithConfig CurrentProvider { get { return provider; } } public override IEnumerable<ModelMetadata> GetMetadataForProperties(object container, Type containerType) { throw new NotImplementedException();//復雜類型實現,屬性的循環獲取 } public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName) { throw new NotImplementedException();//復雜類型實現,屬性的循環獲取 } public override ModelMetadata GetMetadataForType(Func<object> modelAccessor, Type modelType) { if (modelAccessor == null) return null; if (System.Web.HttpContext.Current.Session["viewname"] == null) return null; var result = ViewMappingModelMetadataCollection.Current.GetMetadataByView( (View)System.Web.HttpContext.Current.Session["viewname"], (ViewModel)System.Web.HttpContext.Current.Session["viewmodel"]); if (modelAccessor != null) result.Model = modelAccessor().GetType().GetProperty("CustomerId").GetValue(modelAccessor()); return result; } } }
Customer模型定義:
public class Customer { public string CustomerId { get; set; } }
在模型上我們沒有應用任何一個 元數據控制特性,但是我們將在界面上看到效果;
View 視圖定義:
@model MvcApplication4.Models.Customer <table> <tr> <td> <h3>編輯模式.</h3> <h4>@Html.DisplayForModel(Model.CustomerId)</h4> </td> </tr> </table> @model MvcApplication4.Models.Customer <table> <tr> <td> <h3>顯示模式.</h3> <h4>@Html.EditorForModel(Model.CustomerId)</h4> </td> </tr> </table>
這是兩種模型的呈現方式;
我們自動設置的元數據已經起到效果了;
作者:王清培
出處:http://wangqingpei557.blog.51cto.com/
本文版權歸作者和51CTO共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。