目錄
-
ModelBinder
-
ModelBinderProvider
-
不同型別的Model繫結
- 簡單型別
- 複雜型別
- 其他型別
ModelBinder
ModelBinder是Model繫結的核心.
public interface IModelBinder
{
//繫結Model方法,返回繫結是否成功
bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext);
}
ModelBindingContext
public class ModelBindingContext
{
//資料來源
public IValueProvider ValueProvider { get; set; }
//最終建立的物件 繫結過程就是建立Model
public object Model { get; set; }
//引數名稱
public string ModelName { get; set; }
//引數型別
public Type ModelType { get; }
//引數後設資料
public ModelMetadata ModelMetadata { get; set; }
//屬性後設資料
public IDictionary<string, ModelMetadata> PropertyMetadata { get; }
//儲存繫結結果(包括錯誤資訊,ValueProviderResult),該值引用自ApiController的ModelState
public ModelStateDictionary ModelState { get; set; }
public bool FallbackToEmptyPrefix { get; set; }
}
當ModelBinder特性的Name(為null),FallbackToEmptyPrefix為True.
FallbackToEmptyPrefix為False時,則必須資料來源有字首才能繫結上.
Web API定義了一系列的ModelBinder,這裡先介紹
public class CompositeModelBinder : IModelBinder
{
//IModelBinder 集合
public CompositeModelBinder(params IModelBinder[] binders)
//使用內部ModelBinder進行繫結Model(預設遍歷一次,如果FallbackToEmptyPrefix為True,則會有2次遍歷機會)
public virtual bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
}
ModelBinderProvider
Web API透過ModelBinderProvider建立ModelBinder
public abstract class ModelBinderProvider
{
public abstract IModelBinder GetBinder(HttpConfiguration configuration, Type modelType);
}
Web API中預設註冊了一系列的ModelBinderProvider
this.SetMultiple<ModelBinderProvider>(new ModelBinderProvider[8]
{
(ModelBinderProvider) new TypeConverterModelBinderProvider(),
(ModelBinderProvider) new TypeMatchModelBinderProvider(),
(ModelBinderProvider) new KeyValuePairModelBinderProvider(),
(ModelBinderProvider) new ComplexModelDtoModelBinderProvider(),
(ModelBinderProvider) new ArrayModelBinderProvider(),
(ModelBinderProvider) new DictionaryModelBinderProvider(),
(ModelBinderProvider) new CollectionModelBinderProvider(),
(ModelBinderProvider) new MutableObjectModelBinderProvider()
});
對應的我們先介紹一下
//同樣都是組裝一批ModelBinderProvider
public sealed class CompositeModelBinderProvider : ModelBinderProvider
{
public CompositeModelBinderProvider(IEnumerable<ModelBinderProvider> providers)
public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
}
ModelBinderAttribute除了透過Name設定FallbackToEmptyPrefix,還有個更重要的屬性BinderType
public class ModelBinderAttribute : ParameterBindingAttribute
{
public string Name { get; set; }
//用來指定ModelBinder 或 ModelBinderProvider
public Type BinderType { get; set; }
}
不同型別的Model繫結
不同的資料型別,有不同的資料結構.
所以針對他們的繫結機制也是不同的.
簡單型別
TypeConverterModelBinder
public sealed class TypeConverterModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
model = valueProviderResult.ConvertTo(bindingContext.ModelType);
bindingContext.Model = model;
return true;
}
}
複雜型別
在介紹複雜型別的Bind前,先介紹一下下面2個型別.
ComplexModelDto包含複雜型別的後設資料和繫結結果
public class ComplexModelDto
{
public ComplexModelDto(ModelMetadata modelMetadata, IEnumerable<ModelMetadata> propertyMetadata)
public ModelMetadata ModelMetadata { get; private set; }
public Collection<ModelMetadata> PropertyMetadata { get; private set; }
//key 為屬性的後設資料,value 為繫結結果
public IDictionary<ModelMetadata, ComplexModelDtoResult> Results { get; private set; }
}
ComplexModelDtoResult
public sealed class ComplexModelDtoResult
{
//繫結結果值
public object Model { get; private set; }
//驗證資訊
public ModelValidationNode ValidationNode { get; private set; }
}
複雜型別的繫結由 MutableObjectModelBinder 和 ComplexModelDtoModelBinder共同完成.
而上面2個型別,實際就是用來在2個ModelBinder間傳遞的物件
public class MutableObjectModelBinder : IModelBinder
{
public virtual bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
//型別篩選
if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) || !MutableObjectModelBinder.CanBindType(bindingContext.ModelType))
return false;
//1. 建立空物件
bindingContext.ModelMetadata.Model = Activator.CreateInstance(bindingContext.ModelType);
//2. 建立ComplexModelDto物件
ComplexModelDto complexModelDto = new ComplexModelDto(bindingContext.ModelMetadata,bindingContext.PropertyMetadata);
//3. 進入繫結流程
this.ProcessDto(actionContext, bindingContext, complexModelDto);
return true;
}
internal void ProcessDto(HttpActionContext actionContext, ModelBindingContext bindingContext, ComplexModelDto dto)
{
//建立子ModelBindingContext
var subContext = new ModelBindingContext(bindingContext)
{
ModelName = bindingContext.ModelName,
ModelMetadata = GetMetadata(typeof(ComplexModelDto))
};
//呼叫ComplexModelDtoModelBinder 對ComplexModelDto進行繫結
actionContext.Bind(subContext);
//對複雜型別的屬性進行繫結
foreach (var result in (IEnumerable<KeyValuePair<ModelMetadata, ComplexModelDtoResult>>) dto.Results)
{
ModelMetadata key = result.Key;
ComplexModelDtoResult dtoResult = result.Value;
if (dtoResult != null)
{
var property = bindingContext.ModelType.GetProperty(key.PropertyName);
this.SetProperty(actionContext, bindingContext, key, dtoResult,property);
}
}
}
}
ComplexModelDtoModelBinder則將所有屬性再透過排程其他的ModelBinder進行繫結.並將結果儲存在ComplexModelDto的Results屬性中.
其他型別
Web API還提供了其他常用的資料型別 ModelBinder
CollectionModelBinder來實現 集合 型別(指的集合是實現了IEnumerable介面的型別)
ArrayModelBinder來實現 陣列 型別
DictionaryModelBinder來實現 字典 型別
透過對應的ModelBinderProvider可以知道對應的ModelBinder 能實現哪種型別的繫結
備註
-
文章中的程式碼並非完整WebAPI程式碼,一般是經過自己精簡後的.
-
本篇內容使用MarkDown語法編輯