Asp.net MVC source code analysis -- get the priority of ModelBinder

Source: Internet
Author: User

 

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

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.