Next, the first Asp.net MVC source code analysis-action parameterdescriptorArticleThis is available in reflectedparameterbindinginfoPublic override imodelbinder BinderIn the controlleractioninvoker, there is a similarProtected internal modelbinderdictionary BindersWe should know that modelbinderdictionary is a collection class of imodelbinder,Public class modelbinderdictionary: idictionary <type, imodelbinder>This is a dictionary set.
Let's first look at the binder attribute of reflectedparameterbindinginfo:
Public override imodelbinder binder {
Get {
Imodelbinder binder = modelbinders. getbinderfromattributes (_ parameterinfo,
() => String. Format (cultureinfo. currentculture, mvcresources. reflectedparameterbindinginfo_multipleconverterattributes,
_ Parameterinfo. Name, _ parameterinfo. Member ));
Return binder;
}
}
There is an attribute in modelbinders.Public static modelbinderdictionary BindersThe binders content is as follows:
Modelbinderdictionary binders =New modelbinderdictionary (){
{Typeof (httppostedfilebase), new httppostedfilebasemodelbinder ()},
{Typeof (byte []), new bytearraymodelbinder ()},
{Typeof (Binary), new linqbinarymodelbinder ()}
};
It indicates that by default, three binders are provided, but this attribute is static. Therefore, we can add our own binder class to it, like Filiter mentioned in the previous article and valueprovider mentioned later,
For example, in application_start () method Modelbinders. binders. Add (XXX, xxxx) It is not very convenient to expand.
The getbinderfromattributes method of modelbinders can guess its logic at a glance,
Custommodelbinderattribute [] attrs = (custommodelbinderattribute []) element.Getcustomattributes (typeof (custommodelbinderattribute ),True/* inherit */);
Obtain the custommodelbinderattribute attribute of the current parameter,If this feature is available, call the getbinder () method of the first feature and return its value. If there is no feature, null is returned. If there are multiple features, an exception is thrown, it indicates that one parameter cannot have multiple mmmodelbinderattribute features., The binder attribute of reflectedparameterbindinginfo is set.
The following is the binders attribute of controlleractioninvoker,
Protected internal modelbinderdictionary binders {
Get {
If (_ binders = NULL ){
_ Binders = modelbinders. binders;
}
Return _ binders;
}
Set {
_ Binders = value;
}
}
By default, modelbinders. Binders is returned.
Next, let's take a look at how the imodelbinder binder = getmodelbinder (parameterdescriptor) Statement returns the binder,
Return parameterdescriptor. bindinginfo. Binder ?? Binders. getbinder (parameterdescriptor. parametertype );
This is too simple. First, check whether the parameter has the binder feature. If yes, return the corresponding binder. Otherwise, obtain the corresponding binder Based on the parameter type.
The method is as follows:
Public imodelbinder getbinder (type modeltype) {return getbinder (modeltype, true/* fallbacktodefault */);} Public Virtual imodelbinder getbinder (type modeltype, bool fallbacktodefault) {If (modeltype = NULL) {Throw new argumentnullexception ("modeltype");} return getbinder (modeltype, (fallbacktodefault )? Defaultbinder: NULL);} 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 ;}
Note the priority selected by the binder. (1) Find the corresponding binder from _ modelbinderproviders.
Private modelbinderprovidercollection _ modelbinderproviders;
Public modelbinderdictionary ()
: This (modelbinderproviders. binderproviders ){
}
Internal modelbinderdictionary (modelbinderprovidercollection modelbinderproviders ){
_ Modelbinderproviders = modelbinderproviders;
}
Public static class modelbinderproviders {
Private readonly static modelbinderprovidercollection _ binderproviders = new modelbinderprovidercollection {
};
Public static modelbinderprovidercollection binderproviders {
Get {
Return _ binderproviders;
}
}
}
From theseCodeWe can know that there is no data in _ modelbinderproviders by default. When will this set have data? When will we call modelbinderproviders in application_start. binderproviders. add (XXX) has data,
(2) retrieve data from _ innerdictionary. When will the data be added? after reading the add of modelbinderdictionary, you will understand it.
Public void add (type key, imodelbinder value ){
_ Innerdictionary. Add (Key, value );
}
In fact, many internal methods of modelbinderdictionary operate around the _ innerdictionary set.
(3) obtain the binder from the parameter data type
(4) The default defaultbinder for returning goods. The default attribute is new defamodelmodelbinder ()
Summary 1LowerBinder priority (1) custommodelbinderattribute attribute of the parameter; (2) modelbinderproviders. binderproviders. add (XXX) registered imodelbinderprovider; (3) binders of modelbinders; (4) custommodelbinderattribute attribute of parameter type; (5) returned default defamodelmodelbinder
Ivalueprovider valueprovider = controllercontext. Controller. valueprovider; let's talk about this sentence in the following articles,
String parametername = parameterdescriptor. bindinginfo. prefix ?? Parameterdescriptor. parametername; get the parameter name,
Predicate <string> propertyfilter = getpropertyfilter (parameterdescriptor); this only controls the value that needs to be bound to this parameter.
Private Static predicate <string> getpropertyfilter (parameterdescriptor ){
Parameterbindinginfo bindinginfo = parameterdescriptor. bindinginfo;
Return propertyname => bindattrid. ispropertyallowed (propertyname, bindinginfo. Include. toarray (), bindinginfo. Exclude. toarray ());
}
Bindattrid. ispropertyallowed is as follows:
Internal static bool ispropertyallowed (string propertyname, string [] includeproperties, string [] excludeproperties ){
// We allow a property to be bound if its both in the include list and not in the exclude list.
// An empty include list implies all properties are allowed.
// An empty exclude list implies no properties are disallowed.
Bool includeproperty = (includeproperties = NULL) | (includeproperties. Length = 0) | includeproperties. Contains (propertyname, stringcomparer. ordinalignorecase );
Bool excludeproperty = (excludeproperties! = NULL) & excludeproperties. Contains (propertyname, stringcomparer. ordinalignorecase );
Return includeproperty &&! Excludeproperty;
}
Now we can see that bindattri is used in the Declaration.
Now we will do how to create a custom modelbinder class.
Public Class Userinfo { Public String Name { Set ; Get ;} Public String Age { Set ; Get ;}} Public Class Userinfomodelbinder: imodelbinder { Public Object Bindmodel (controllercontext, modelbindingcontext bindingcontext ){ Object OBJ = Activator. createinstance (bindingcontext. modeltype ); Foreach (Propertyinfo P In Bindingcontext. modeltype. getproperties () {valueproviderresult vpresult = Bindingcontext. valueprovider. getvalue (P. Name ); If (Vpresult! = Null ){ Object Value = Vpresult. convertize (P. propertytype); p. setvalue (OBJ, value, Null );}} Return OBJ ;}} Public Class Homecontroller: controller { Public Actionresult index ([modelbinder ( Typeof (Userinfomodelbinder)] userinfo ){ Return Content ( " Name: " + Userinfo. Name + " Age: " + Userinfo. Age ); // Return view (); }}
Running result