1. 程式人生 > >通過例項模擬ASP.NET MVC的Model繫結的機制:集合+字典

通過例項模擬ASP.NET MVC的Model繫結的機制:集合+字典

在本系列的前面兩篇文章(《簡單型別+複雜型別》、《陣列》)我們通過建立的例項程式模擬了ASP.NET MVC預設使用的DefaultModelBinder對簡單型別、複雜型別以及陣列物件的Model繫結。現在我們按照相同的方式來分析基於集合和字典型別的Model繫結是如何實現的。[原始碼從這裡下載][本文已經同步到《How ASP.NET MVC Works?》中]

一、集合

這裡的集合指的是除陣列和字典之外的所有實現IEnumerable<T>介面的型別。和基於陣列的Model繫結類似,ValueProvider可以將多個同名的資料項作為集合的元素,基於索引(基零整數和字串)的資料項命名方式同樣適用

。我們對自定義的DefaultModelBinder作了如下的完善使之支援集合型別的Model繫結。

   1: public class DefaultModelBinder
   2: {
   3:     //其他成員
   4:     public object BindModel(Type parameterType, string prefix)
   5:     {
   6:         if (!this.ValueProvider.ContainsPrefix(prefix))
   7:         {
   8:             return null;
   9:         }
  10:         ModelMetadata modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => null, parameterType);
  11:         if (!modelMetadata.IsComplexType)
  12:         {
  13:             return this.ValueProvider.GetValue(prefix).ConvertTo(parameterType);
  14:         }
  15:         if (parameterType.IsArray)
  16:         {
  17:             return BindArrayModel(parameterType, prefix);
  18:         }
  19:         object model = CreateModel(parameterType);
  20:         Type enumerableType = ExtractGenericInterface(parameterType, typeof(IEnumerable<>));
  21:         if (null != enumerableType)
  22:         {
  23:             return BindCollectionModel(prefix, model, enumerableType);
  24:         }
  25:         foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(parameterType))
  26:         {                
  27:             string key = prefix == "" ? property.Name : prefix + "." + property.Name;
  28:             property.SetValue(model, BindModel(property.PropertyType, key));
  29:         }
  30:         return model;
  31:     }
  32:  
  33:     private object BindCollectionModel(string prefix, object model, Type enumerableType)
  34:     {
  35:         List<object> list = new List<object>();
  36:         bool numericIndex;
  37:         IEnumerable<string> indexes = GetIndexes(prefix, out numericIndex);
  38:         Type elementType = enumerableType.GetGenericArguments()[0];
  39:  
  40:         if (!string.IsNullOrEmpty(prefix) && this.ValueProvider.ContainsPrefix(prefix))
  41:         {
  42:             IEnumerable enumerable = this.ValueProvider.GetValue(prefix).ConvertTo(enumerableType) as IEnumerable;
  43:             if (null != enumerable)
  44:             {
  45:                 foreach (var value in enumerable)
  46:                 {
  47:                     list.Add(value);
  48:                 }
  49:             }
  50:         }      
  51:         foreach (var index in indexes)
  52:         {
  53:             string indexPrefix = prefix + "[" + index + "]";
  54:             if (!this.ValueProvider.ContainsPrefix(indexPrefix) && numericIndex)
  55:             {
  56:                 break;
  57:             }
  58:             list.Add(BindModel(elementType, indexPrefix));
  59:         }
  60:         if (list.Count == 0)
  61:         {
  62:             return null;
  63:         }
  64:         ReplaceHelper.ReplaceCollection(elementType, model, list);
  65:         return model;
  66:     }
  67:     
  68:     private Type ExtractGenericInterface(Type queryType, Type interfaceType)
  69:     {
  70:         Func<Type, bool> predicate = t => t.IsGenericType && (t.GetGenericTypeDefinition() == interfaceType);
  71:         if (!predicate(queryType))
  72:         {
  73:             return queryType.GetInterfaces().FirstOrDefault<Type>(predicate);
  74:         }
  75:         return queryType;
  76:     }
  77: }

如上面的程式碼片斷所示,在BindModel方法中我們通過呼叫ExtractGenericInterface判斷目標型別是否實現了IEnumerable<T>介面,如果實現了該介面則提取泛型元素型別。針對集合的Model繫結實現在方法BindCollectionModel中,我們按照陣列繫結的方式得的針對目標集合物件的所有元素物件,並將其新增到一個List<object>物件中,然後呼叫ReplaceHelper 的靜態方法ReplaceCollection將該列表中的元素拷貝到預先建立的Model物件中。定義在ReplaceHelper的靜態方法ReplaceCollection定義如下:

   1: internal static class ReplaceHelper
   2: {
   3:     private static MethodInfo replaceCollectionMethod = typeof(ReplaceHelper).GetMethod("ReplaceCollectionImpl", BindingFlags.Static |BindingFlags.NonPublic);
   4:  
   5:      public static void ReplaceCollection(Type collectionType, object collection, object newContents)
   6:     {
   7:         replaceCollectionMethod.MakeGenericMethod(new Type[] { collectionType }).Invoke(null, new object[] { collection, newContents });
   8:     } 
   9:     private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents)
  10:     {
  11:         collection.Clear();
  12:         if (newContents != null)
  13:         {
  14:             foreach (object obj2 in newContents)
  15:             {
  16:                 T item = (obj2 is T) ? ((T)obj2) : default(T);
  17:                 collection.Add(item);
  18:             }
  19:         }
  20:     }
  21: }

為了讓演示針對集合型別的Model繫結,我們對例項中的HomeController作了如下的修改。Action方法的引數型別替換成IEnumerable<Contact>,該集合中的每個Contact的資訊在該方法中被呈現出來。通過GetValueProvider提供的NameValueCollectionValueProvider採用基零整數索引的方式定義資料項。

   1: public class HomeController : Controller
   2: {
   3:     private IValueProvider GetValueProvider()
   4:     {
   5:         NameValueCollection requestData = new NameValueCollection();
   6:         requestData.Add("[0].Name", "Foo");
   7:         requestData.Add("[0].PhoneNo", "123456789");
   8:         requestData.Add("[0].EmailAddress", "[email protected]");
   9:  
  10:         requestData.Add("[1].Name", "Bar");
  11:         requestData.Add("[1].PhoneNo", "987654321");
  12:         requestData.Add("[1].EmailAddress", "[email protected]");
  13:  
  14:         return new NameValueCollectionValueProvider(requestData, CultureInfo.InvariantCulture);
  15:     }
  16:          
  17:     public void Action(IEnumerable<Contact> contacts)
            
           

相關推薦

通過例項模擬ASP.NET MVC的Model機制簡單型別+複雜型別

總的來說,針對目標Action方法引數的Model繫結完全由元件ModelBinder來實現,在預設情況下使用的ModelBinder型別為DefaultModelBinder,接下來我們將按照逐層深入的方式介紹實現在DefaultModelBinder的預設Model繫結機制。[原始碼從這裡下載][本文已經

通過例項模擬ASP.NET MVC的Model機制集合+字典

在本系列的前面兩篇文章(《簡單型別+複雜型別》、《陣列》)我們通過建立的例項程式模擬了ASP.NET MVC預設使用的DefaultModelBinder對簡單型別、複雜型別以及陣列物件的Model繫結。現在我們按照相同的方式來分析基於集合和字典型別的Model繫結是如何實現的。[原始碼從這裡下載][本文已經

ASP.NET 資料概述

使用 ASP.NET 資料繫結,您可以將任何伺服器控制元件繫結到簡單的屬性、集合、表示式和/或方法。如果您使用資料繫結,則當您在資料庫中或通過其他方法使用資料時,您會具有更大的靈活性。本文討論了下列資料繫結主題: 資料繫結概要 <%# %> 語法

ASP.NET資料控制元件

資料繫結控制元件簡介 資料繫結分為:資料來源 和 資料繫結控制元件 兩部分,資料繫結控制元件通過資料來源來獲得資料,通過資料來源來 隔離資料提供者和資料使用者,資料來源有: SqlDataSource,AccessDataSource,ObjectDataSource,L

ASP.Net資料控制元件小結

  資料繫結作為ASP.Net中較為重要的一個知識點,其中涉及到了三個重要的資料繫結控制元件,Repeater DataList和GridView(DataGrid)。 Repeater控制元件:   特點:     顧名思義 就是重複繫結資料的控制元件,它沒有內建佈局。

ASP.NET-資料來源

<asp:Label ID="Label1" runat="server" Text='<%# "總金額為:" + Convert.ToString(Convert.ToDecimal(TextBox1.Text)*Convert.ToInt32(TextBox2.Text))%>' /&g

Windows 2008 server + IIS 7 設置身份模擬(ASP.NET impersonation)

oid thead tar level 現在 如果 system out 編輯框 IIS7 與 IIS 6 相比有了很大的改動,原來在 IIS 6 下可以的設置到了 IIS 7 下有的會發生變化。身份模擬的配置上,IIS7 和 IIS6有很大不同,網上IIS6的

Jquery通過append新元素之後事件問題的解決方案:

1、 重複繫結 (DOM載入時,先對DOM中存在的元素進行事件繫結,每次新增的元素時,再對新增元素繫結一次事件) 2、 直接在標籤上新增onclick屬性 3、 事件委託 4、 如果元素是用click事件append進來的,那麼功能函式必須放在這個click事件裡面; 具體程式碼不表,

模擬Vue雙向資料

事件物件 function EventEmit(){ // { // "message":['事件1','事件2'] // } this.callbacks={} } EventEmit.prototype.on=function(eventNa

ASP:Gridview中列的常見格式化字串

如下一段程式碼: <asp:boundField DataField="繫結列" headerText="列名稱" dataFormatString="格式化型別" 常見的格式化型別有: 貨幣 {0:C} 科學計數法(指數) {0:E} 輩分比 {0:P} 固定浮點數

RabbitMQ通過Exchange.Direct、同一個佇列不同的routekey實現不同的消費

通過消費者去進行Exchange和Queue通過不同的RouteKey進行繫結 消費者1: static void Main(string[] args) { ConnectionFactory factory = new ConnectionFacto

android例項 listview與sqlite資料

ListView與Sqlite資料庫繫結步驟: 1.將Sqlite資料庫的內容查詢出來並放入陣列列表中,形成ListView的資料來源; 2.介面卡繫結資料來源,顯示在ListView item中。 本文實現的ListView與Sqlite資料繫結的功能如下圖-1

通過擴充套件讓ASP.NET Web API支援JSONP

同源策略(Same Origin Policy)的存在導致了“源”自A的指令碼只能操作“同源”頁面的DOM,“跨源”操作來源於B的頁面將會被拒絕。同源策略以及跨域資源共享在大部分情況下針對的是Ajax請求。同源策略主要限制了通過XMLHttpRequest實現的Ajax請求

Android中通過Tag為View儲存資料資料

專案中有時候需要為View繫結資料,比如每一個雲標籤都對應伺服器一個標籤id,View中setTag可以方便的為控制元件繫結資料。 為控制元件繫結資料: selectCategory.setTag(R.id.tag_id, id); selectCategory.setT

android例項 listview與sqlite資料

ListView與Sqlite資料庫繫結步驟: 1.將Sqlite資料庫的內容查詢出來並放入陣列列表中,形成ListView的資料來源; 2.介面卡繫結資料來源,顯示在ListView item中。 本文實現的ListView與Sqlite資料繫結的

.NET資料說明和使用方法

資料繫結對被繫結物件有特殊要求,如果只是普通的get;set屬性的物件用在資料繫結上無法雙向繫結(只有Model值不會變或者Model變了不要求介面跟著變才可以使用普通屬性), 一般要求類實現InotifyPropertyChanged介面 INotifyProperty

Jquery通過append新元素之後事件問題的解決方案

昨天在專案中發現一個問題:在DOM載入之後為標籤繫結的事件對於新加進來的標籤並不起作用,通過查詢發現事件並沒有繫結到新加入的標籤,因此今天特意總結一下這種問題的解決方案。 在jquery中,我們通常是在DOM載入完成後,再對元素繫結事件。以下面的情景為例:製作

.NET資料時BoundField與TemplateField的區別

在.NET中,資料繫結列是通過 asp:BoundField 或 TemplateField 元素定義的。通過設定 DataField 屬性,可以將 BoundField 繫結到 GridView

通過偽靜態規則實現子目錄

如果根目錄也繫結其它域名的話,可以通過子目錄訪問子站,就會導致子站檔案多出個訪問URL,建議不要在根目錄繫結其它域名;當然,如果繫結子目錄都是一級目錄subDomain下的子目錄,可以通過robots.txt禁止收錄subDomain目錄,這樣就不用擔心上面的問題了。可以使用DisallowDirdh88

ASP.NET MVC5路由系統機制詳細講解

提交 eas 找文件 網址 自動調用 提取 ges pri stat 請求一個ASP.NET mvc的網站和以前的web form是有區別的,ASP.NET MVC框架內部給我們提供了路由機制,當IIS接受到一個請求時,會先看是否請求了一個靜態資源(.html,css,js