在MVC的Model中,我們可以定義很多與視圖相關的元數據,這些元數據對我們開發視圖起著相當重要 的作用,特別是在數據驗證方面。這些元數據一般情況下我們是不會定義在業務實體(或持久化實體)上 面,所以很多情況下,我們會需要開發兩種實體:View Model和Business Model。這樣就造成,在Action 與View的溝通當中,我們需要使用View Model,然後在業務邏輯處理時,我們需要再將View Model映射到 Business Model,這將會使我們的開發框架變得繁瑣。因為一般情況下,View Model和Business Model在 很多情況下,都是很雷同的對象,只是View Model會有很多與視圖相關的元數據。在這種情況下,只要我 們能把View Model作為Business Model的元數據描述對象(MetadataType)來使用,而不直接參與Action與 View的溝通,讓這些工作都由Business Model來承擔,這樣就可以有效的避免很多重復工作。
在System.ComponentModel.DataAnnotations內部,提供了MetadataTypeAttribute這個標簽,讓我們 可以為Business Model指定它對應的視圖元數據類型。特別是當我們使用LINQ2SQL、EF等框架來生成實體 框架時,我們可以以partial類的形式來提供它對應的視圖元數據類型:
1 [MetadataType(typeof(Product_Metadata))]
2 public partial class Product
3 {}
4
5 public class Product_Metadata
6 {
7 }
這樣做在大多數情況下是沒有問題的。但是僅僅是這樣,還不能解決所有問題。一般情況下Business Model和MetadataType是不在同一個Assembly裡面,這時候你就無法以partial類的形式來擴展Business Model。所以我們就需要有一套機制來延遲注冊Business Model與MetadataType的映射關系。通過MVC源碼 的分析,我們可以通過擴展 DataAnnotationsModelMetadataProvider的GetTypeDescriptor方法來解決這 個問題。
首先,我們先定義一個Business Model與MetadataType的映射容器:
01 public static class TypeDescriptorHelper
02 {
03 static Hashtable hashtable = new Hashtable();
04 static ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
05 static TypeDescriptorHelper()
06 {
07
08 }
09 public static void RegisterMetadataType(Type type, Type metadataType)
10 {
11 locker.EnterWriteLock();
12
13 hashtable[type] = metadataType;
14
15 locker.ExitWriteLock();
16 }
17 public static ICustomTypeDescriptor Get(Type type)
18 {
19 locker.EnterReadLock();
20 var metadataType = hashtable[type] as Type;
21 ICustomTypeDescriptor descriptor = null;
22 if (metadataType != null)
23 {
24 descriptor = new AssociatedMetadataTypeTypeDescriptionProvider(type, metadataType).GetTypeDescriptor (type);
25 }
26 locker.ExitReadLock();
27 return descriptor;
28 }
29 }