In the asp.net mvc framework, we can. web. mvc. binders extends the custom binder type, but it also has some other methods to implement the custom model binder. in addition, mvc also has some strategies, which are analyzed as follows:
The method for obtaining the ModelBinder object is GetParameterValue, where
IModelBinder binder = GetModelBinder (parameterDescriptor );
This Code determines the use policy of ModelBinder.
System. Web. Mvc. ControllerActionInvoker
Protected virtual object GetParameterValue (ControllerContext controllerContext, ParameterDescriptor parameterDescriptor ){
// Collect all of the necessary binding properties
Type parameterType = parameterDescriptor. ParameterType;
// Hey, please refer to the binder portal here. csdn generation segments cannot be highlighted ~!
IModelBinder binder = GetModelBinder (parameterDescriptor );
IValueProvider valueProvider = controllerContext. Controller. ValueProvider;
String parameterName = parameterDescriptor. BindingInfo. Prefix ?? ParameterDescriptor. ParameterName;
Predicate <string> propertyFilter = GetPropertyFilter (parameterDescriptor );
// Finally, call into the binder
ModelBindingContext bindingContext = new ModelBindingContext (){
FallbackToEmptyPrefix = (parameterDescriptor. BindingInfo. Prefix = null), // only fall back if prefix not specified
ModelMetadata = ModelMetadataProviders. Current. GetMetadataForType (null, parameterType ),
ModelName = parameterName,
ModelState = controllerContext. Controller. ViewData. ModelState,
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
Object result = binder. BindModel (controllerContext, bindingContext );
Return result ?? ParameterDescriptor. DefaultValue;
}
Private IModelBinder GetModelBinder (ParameterDescriptor parameterDescriptor ){
// Look on the parameter itself, then look in the global table
Return parameterDescriptor. BindingInfo. Binder ?? Binders. GetBinder (parameterDescriptor. ParameterType );
}
Here, the IModelBinder is obtained first from parameterDescriptor. BindingInfo. Next, let's take a look at how to obtain the binder from parameterDescriptor.
First, find the ReflectedActionDescriptor object. You can see that the ReflectedParameterDescriptor object is generated in the GetParameters method,
System. Web. Mvc. ReflectedActionDescriptor
Public override ParameterDescriptor [] GetParameters (){
ParameterDescriptor [] parameters = LazilyFetchParametersCollection ();
// Need to clone array so that user modifications aren't accidentally stored
Return (ParameterDescriptor []) parameters. Clone ();
}
View plain
Private ParameterDescriptor [] LazilyFetchParametersCollection (){
Return DescriptorUtil. LazilyFetchOrCreateDescriptors <ParameterInfo, ParameterDescriptor> (
Ref _ parametersCache/* cacheLocation */,
MethodInfo. GetParameters/* initializer */,
ParameterInfo => new ReflectedParameterDescriptor (parameterInfo, this)/* converter */);
}
This object calls the ModelBinders. GetBinderFromAttributes method to obtain the binder
ReflectedParameterDescriptor-> ReflectedParameterBindingInfo
Public override IModelBinder Binder {
Get {
IModelBinder binder = ModelBinders. GetBinderFromAttributes (_ parameterInfo,
() => String. Format (CultureInfo. CurrentCulture, MvcResources. ReflectedParameterBindingInfo_MultipleConverterAttributes,
_ ParameterInfo. Name, _ parameterInfo. Member ));
Return binder;
}
}
System. Web. Mvc. ModelBinders
Internal static IModelBinder GetBinderFromAttributes (ICustomAttributeProvider element, Func <string>
ErrorMessageAccessor ){
CustomModelBinderAttribute [] attrs = (CustomModelBinderAttribute []) element.
GetCustomAttributes (typeof (CustomModelBinderAttribute ),
True/* inherit */);
Return GetBinderFromAttributesImpl (attrs, errorMessageAccessor );
}
Next let's take a look
Return parameterDescriptor. BindingInfo. Binder ?? Binders. GetBinder (parameterDescriptor. ParameterType );
The branch after the statement.
System. Web. Mvc. ModelBinderDictionary
Private IModelBinder GetBinder (Type modelType, IModelBinder fallbackBinder ){
// Try to look up a binder for this type. We use this order of precedence:
// 1. Binder returned from provider
// 2. Binder registered in the global table
// 3. Binder attribute defined on the type
// 4. Supplied fallback binder
IModelBinder binder = _ modelBinderProviders. GetBinder (modelType );
If (binder! = Null ){
Return binder;
}
If (_ innerDictionary. TryGetValue (modelType, out binder )){
Return binder;
}
Binder = ModelBinders. GetBinderFromAttributes (modelType,
() => String. Format (CultureInfo. CurrentCulture, MvcResources. ModelBinderDictionary_MultipleAttributes, modelType. FullName ));
Return binder ?? FallbackBinder;
}
Internal static IModelBinder GetBinderFromAttributes (Type type, Func <string> errorMessageAccessor ){
AttributeCollection allAttrs = TypeDescriptorHelper. Get (type). GetAttributes ();
CustomModelBinderAttribute [] filteredAttrs = allAttrs. OfType <CustomModelBinderAttribute> (). ToArray ();
Return GetBinderFromAttributesImpl (filteredAttrs, errorMessageAccessor );
}
Here we can clearly understand the priority of IModelBinder.
Conclusion:
1. This implementation is called if the IModelBinder Implementation of metadata is marked in the object of the Action parameter. // Note: If this sentence is not understood, you can continue to look at the following instances.
2. If the IModelBinder implementation without metadata is marked in the parameter, the call priority is:
// (1). Search for the global ModelBinderProviders. BinderProviders. We can also register our own provider.
// (2). Search for the global System. Web. Mvc. Binders object. You can also register your own binder.
// (3). Find the IModelBinder implementation in modelbinderatrider from the attribute of the parameter object type.
// (4). If the implementation of IModelBinder is not found after the above steps, use the default defamodelmodelbinder class of MVC,
Public virtual IModelBinder GetBinder (Type modelType, bool fallbackToDefault ){
If (modelType = null ){
Throw new ArgumentNullException ("modelType ");
}
Return GetBinder (modelType, (fallbackToDefault )? DefaultBinder: null );
}
Sample Code:
The following example demonstrates the conclusion.
// Model class:
Public class FormTestModel
{
[Required]
[DataType (DataType. Text)]
[Display (Name = "Room Name")]
Public string RoomName {get; set ;}
[Required]
[DataType (DataType. Text)]
[Display (Name = "Room Count")]
Public string RoomCount {get; set ;}
}
// Action class:
[HttpPost]
Public ActionResult FormTest (FormTestModels model, string returnUrl)
{
<Span style = "white-space: pre"> </span> return view ();
}
// We have created four IModelBinder implementations;
Internal sealed class FormTestModelBinderImpl1: IModelBinder
{
Public object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DefaultModelBinder defBinder = new DefaultModelBinder ();
Return defBinder. BindModel (controllerContext, bindingContext );
}
}
Internal sealed class FormTestModelBinderImpl2: IModelBinder
{
Public object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DefaultModelBinder defBinder = new DefaultModelBinder ();
Return defBinder. BindModel (controllerContext, bindingContext );
}
}
Internal sealed class FormTestModelBinderImpl3: IModelBinder
{
Public object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DefaultModelBinder defBinder = new DefaultModelBinder ();
Return defBinder. BindModel (controllerContext, bindingContext );
}
}
Internal sealed class FormTestModelBinderImpl4: IModelBinder
{
Public object BindModel (ControllerContext controllerContext, ModelBindingContext bindingContext)
{
DefaultModelBinder defBinder = new DefaultModelBinder ();
Return defBinder. BindModel (controllerContext, bindingContext );
}
}
Public sealed class FormTestModelBinderProviderImpl: IModelBinderProvider
{
Public IModelBinder GetBinder (Type modelType)
{
If (modelType = typeof (FormTestModels ))
{
// "Provider implementition ";
Return new FormTestModelsModelBinderImpl2 ();
}
Return null;
}
}
Then we add:
// Priority 1:
[HttpPost]
Public ActionResult FormTest ([ModelBinder (typeof (FormTestModelBinderImpl1)] FormTestModel model, string returnUrl)
{
Return view ();
}
// Second priority:
ModelBinderProviders. BinderProviders. Add (new FormTestModelBinderProviderImpl2 ());
// Priority 3:
System. Web. Mvc. ModelBinders. Binders [typeof (FormTestModel)] = new FormTestModelBinderImpl3 ();
// Fourth priority:
[ModelBinder (typeof (FormTestModelBinderImpl4)]
Public class FormTestModel
{
// Codes
}
Finally, we set a breakpoint on the four binder. Let's take a look at the code execution and we can clearly see how correct our conclusion is .:)
Finally, I would like to thank you for watching. it is a coincidence that the above analysis is similar.
Thank you.
From the rain of November