程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> 關於ASP.NET >> 通過實例模擬ASP.NET MVC的Model綁定機制:簡單類型+復雜類型

通過實例模擬ASP.NET MVC的Model綁定機制:簡單類型+復雜類型

編輯:關於ASP.NET

總的來說,針對目標Action方法參數的Model綁定完全由組件ModelBinder來實現,在默認情況下使用的ModelBinder類型為DefaultModelBinder,接下來我們將按照逐層深入的方式介紹實現在DefaultModelBinder的默認Model綁定機制。[源代碼從這裡下載]

一、簡單類型

對於旨在綁定目標Action方法參數值的Model來說,最簡單的莫過於簡單參數類型的情況。通過《初識Model元數據》的介紹我們知道,復雜類型和簡單類型之間的區別僅僅在於是否支持針對字符串類型的轉換。由於參數值的數據源在請求中以字符串的形式存在,對於支持字符串轉換的簡單類型來說,可以直接通過類型轉換得到參數值。我們通過一個簡單的實例來模擬實現在DefaultModelBinder中針對簡單類型的Model綁定。如下所示的是我們自定義的DefaultModelBinder,其屬性ValueProvider用於從請求中提供相應的數據值,該屬性在構造函數中被初始化。

   1: public class DefaultModelBinder
2: {
3: public IValueProvider ValueProvider { get; private set; }
4: public DefaultModelBinder(IValueProvider valueProvider)
5: {
6: this.ValueProvider = valueProvider;
7: }
8: 
9: public IEnumerable<object> GetParameterValues(ActionDescriptor actionDescriptor)
10: {
11: foreach (ParameterDescriptor parameterDescriptor in actionDescriptor.GetParameters())
12: {
13: string prefix = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
14: yield return GetParameterValue(parameterDescriptor, prefix);
15: }
16: }
17: 
18: public object GetParameterValue(ParameterDescriptor parameterDescriptor, string prefix)
19: {
20: object parameterValue = BindModel(parameterDescriptor.ParameterType, prefix);
21: if (null == parameterValue && string.IsNullOrEmpty(parameterDescriptor.BindingInfo.Prefix))
22: {
23: parameterValue = BindModel( parameterDescriptor.ParameterType, "");
24: }
25: return parameterValue ?? parameterDescriptor.DefaultValue;
26: }
27: 
28: public object BindModel(Type parameterType, string prefix)
29: {
30: if (!this.ValueProvider.ContainsPrefix(prefix))
31: {
32: return null;
33: }
34: return this.ValueProvider.GetValue(prefix).ConvertTo(parameterType);
35: }
36: }

方法GetParameterValues根據指定的用於描述Action方法的ActionDescriptor獲取最終執行該方法的所有參數值。在該方法中,我們通過調用ActionDescriptor的GetParameters方法得到用於描述其參數的所有ParameterDescriptor對象,並將每一個ParameterDescriptor作為參數調用GetParameterValue方法得到具體某個參數的值。GetParameterValue除了接受一個類型為ParameterDescriptor的參數外,還接受一個用於表示前綴的字符串參數。如果通過ParameterDescriptor的BindingInfo屬性表示的ParameterBindingInfo對象具有前綴,則采用該前綴;否則采用參數名稱作為前綴。

對於GetParameterValue方法來說,它又通過調用另一個將參數類型作為參數的BindModel方法來提供具體的參數值,BindModel方法同樣接受一個表示前綴的字符串作為其第二個參數。GetParameterValue最初將通過ParameterDescriptor獲取到的參數值和前綴作為參數調用BindModel方法,如果返回值為Null並且參數並沒有顯示執行前綴,會傳入一個空字符串作為前綴再一次調用BindModel方法,這實際上模擬了之前提到過的去除前綴的後備Model綁定機制(針對於ModelBindingContext的FallbackToEmptyPrefix屬性)。如果最終得到的對象不為Null,則將其作為參數值返回;否則返回參數的默認值。

BindModel方法的邏輯非常簡單。先將傳入的前綴作為參數調用ValueProvider的ContainsPrefix方法判斷當前的ValueProvider保持的數據是否具有該前綴。如果返回之為False,直接返回Null,否則以此前綴作為Key調用GetValue方法得到一個ValueProviderResult調用,並最終調用ConvertTo方法轉換為參數類型並返回。

為了驗證我們自定義的DefaultModelBinder能夠真正地用於針對簡單參數類型的Model綁定沒我們將它應用到一個具體的ASP.NET MVC應用中。在通過Visual Studio的ASP.NET MVC項目模板創建的空Web應用中,我們創建了如下一個默認的HomeController。HomeController具有一個ModelBinder屬性,其類型正是我們自定義的DefaultModelBinder,該屬性通過方法GetValueProvider提供。

   1: public class HomeController : Controller
2: {
3: public DefaultModelBinder ModelBinder { get; private set; }
4: public HomeController()
5: {
6: this.ModelBinder = new DefaultModelBinder(GetValueProvider());
7: }
8: private void InvokeAction(string actionName)
9: {
10: ControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(typeof(HomeController));
11: ReflectedActionDescriptor actionDescriptor = (ReflectedActionDescriptor)controllerDescriptor
12: .FindAction(ControllerContext, actionName);
13: actionDescriptor.MethodInfo.Invoke(this,this.ModelBinder.GetParameterValues(actionDescriptor).ToArray());
14: }
15: public void Index()
16: {
17: InvokeAction("Action");
18: }
19: 
20: private IValueProvider GetValueProvider()
21: {
22: NameValueCollection requestData = new NameValueCollection();
23: requestData.Add("foo", "abc");
24: requestData.Add("bar", "123");
25: requestData.Add("baz", "123.45");
26: return new NameValueCollectionValueProvider(requestData, CultureInfo.InvariantCulture);
27: }
28: public void Action(string foo, [Bind(Prefix="baz")]double bar)
29: {
30: Response.Write(string.Format("{0}: {1}<br/>", "foo", foo));
31: Response.Write(string.Format("{0}: {1}<br/>", "bar", bar));
32: }
33: }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved