首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> ASP.NET 基礎 >> 關於ASP.NET

ASP.NET MVC Model 繫結機制模擬實作:集合與字典型別綁定

     編輯:關於ASP.NET

系列脈絡

承接前文「簡單/複雜型別」與「陣列」繫結模擬,本文實作 IEnumerable<T> 集合與 IDictionary<TKey, TValue> 字典型別的繫結邏輯。

集合型別繫結:BindCollectionModel

核心步驟:

  1. 偵測型別實作 IEnumerable<T>,取得元素型別 T。
  2. 取得所有索引鍵(GetIndexes),支援數字索引(list[0])與字串鍵(list[key])。
  3. 優先嘗試直接轉換同前綴值為 IEnumerable(如表單多同名欄位)。
  4. 逐一索引繫結元素:BindModel(elementType, prefix + "[" + index + "]")。
  5. 若為數字索引且連續中斷則停止(break)。
  6. 收集至 List<object>,最後呼叫 ReplaceHelper.ReplaceCollection 填入目標集合實例。

關鍵程式碼片段

private object BindCollectionModel(string prefix, object model, Type enumerableType)
{
    var list = new List<object>();
    bool numericIndex;
    var indexes = GetIndexes(prefix, out numericIndex);
    Type elementType = enumerableType.GetGenericArguments()[0];

    // 1. 直接轉換同前綴值(多同名欄位)
    if (!String.IsNullOrEmpty(prefix) && ValueProvider.ContainsPrefix(prefix))
    {
        var enumerable = ValueProvider.GetValue(prefix).ConvertTo(enumerableType) as IEnumerable;
        if (enumerable != null) foreach (var v in enumerable) list.Add(v);
    }

    // 2. 索引式繫結
    foreach (var index in indexes)
    {
        string indexPrefix = prefix + "[" + index + "]";
        if (!ValueProvider.ContainsPrefix(indexPrefix) && numericIndex) break;
        list.Add(BindModel(elementType, indexPrefix));
    }

    if (list.Count == 0) return null;
    ReplaceHelper.ReplaceCollection(elementType, model, list);
    return model;
}

ReplaceHelper:泛型集合填充

透過反射呼叫泛型方法 ReplaceCollectionImpl<T>:

internal static class ReplaceHelper
{
    private static MethodInfo replaceCollectionMethod = typeof(ReplaceHelper).GetMethod("ReplaceCollectionImpl", BindingFlags.Static | BindingFlags.NonPublic);
    
    public static void ReplaceCollection(Type collectionType, object collection, object newContents)
    {
        replaceCollectionMethod.MakeGenericMethod(collectionType).Invoke(null, new[] { collection, newContents });
    }
    
    private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents)
    {
        collection.Clear();
        if (newContents != null)
            foreach (var obj in newContents)
                collection.Add(obj is T ? (T)obj : default(T));
    }
}

字典型別繫結:BindDictionaryModel

邏輯類似集合,差異在於:

  • 需解析 IDictionary<TKey, TValue> 兩個泛型參數。
  • 索引鍵為字典的 Key,ValueProvider 鍵格式為 prefix[Key]。
  • 繫結每個 Key-Value 配對,最後填入 IDictionary 實例。

字典繫結關鍵點

private object BindDictionaryModel(string prefix, object model, Type dictionaryType)
{
    var args = dictionaryType.GetGenericArguments();
    Type keyType = args[0], valueType = args[1];
    var dict = (IDictionary)model;
    
    bool numericIndex;
    var indexes = GetIndexes(prefix, out numericIndex); // 這裡 indexes 實為字典的 Keys
    
    foreach (var key in indexes)
    {
        string keyPrefix = prefix + "[" + key + "]";
        if (!ValueProvider.ContainsPrefix(keyPrefix) && numericIndex) break;
        
        object keyObj = ConvertTo(key, keyType); // 字串轉 TKey
        object valObj = BindModel(valueType, keyPrefix);
        dict[keyObj] = valObj;
    }
    return model;
}

GetIndexes:索引鍵解析

從 ValueProvider 解析所有 prefix[xxx] 形式的鍵,提取 xxx 部分,判斷是否為純數字索引。

實驗驗證

表單提交:

<input name="Tags[0]" value="C#" />
<input name="Tags[1]" value="MVC" />
<input name="Scores[Math]" value="90" />
<input name="Scores[English]" value="85" />

Action:

public ActionResult Save(List<string> Tags, Dictionary<string, int> Scores) { ... }

模擬繫結器正確產生 Tags = ["C#", "MVC"]、Scores = { {"Math", 90}, {"English", 85} }。

總結

集合繫結支援「同名多值」與「索引式」兩種來源,字典繫結擴展為 Key-Value 配對。ReplaceHelper 以反射泛型方法實現型別安全填充。掌握此機制可自訂繫結器處理特殊集合/字典結構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved