Asp.net MVC source code analysis-action defaultmodelbinder

Source: Internet
Author: User

next, go to Asp.net MVC source code analysis-controller valueprovider. Now let's take a look at the modelbindingcontext object.

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
};

Generally, the value of fallbacktoemptyprefix should be true. The default value of parameterdescriptor. bindinginfo. prefix is null. The modelmetadata attribute in is an instance of modelmetadata.

Let's look at the definition of its constructor,

Public modelmetadata (modelmetadataprovider provider, type containertype, func <Object> modelaccessor, type modeltype, string propertyname) involves a lot of things, and I don't quite understand some of them now, I only know what they do, so this class is just a simple mention here.

It has an attribute

Public Virtual bool iscomplextype {
Get {
Return! (Typedescriptor. getconverter (modeltype). canconvertfrom (typeof (string )));
}
}
Check whether the current parameter object can be converted to a string object. If yes, it is a simple object; otherwise, it is a complex object.

Modelmetadataproviders. Current is a dataannotationsmodelmetadataprovider instance.Dataannotationsmodelmetadataprovider inherits from associatedmetadataprovider and from associatedmetadataprovider to modelmetadataprovider,Here, getmetadatafortype is called to obtain modelmetadata. The createmetadata created in modelannotationsmodelmetadataprovider is defined as follows:

Protected override modelmetadata createmetadata (ienumerable <attribute> attributes, type containertype, func <Object> modelaccessor, type modeltype, string propertyname)

This method creates a dataannotationsmodelmetadata instance, which is a subclass of modelmetadata.

Next let's take a look at the modelstate attribute = controllercontext. Controller. viewdata. modelstate

Among them, modelstate is very simple.

[Serializable]
Public class modelstate {
Private modelerrorcollection _ errors = new modelerrorcollection ();
PUblic valueproviderresult Value{Get; set ;}
Public modelerrorcollection errors {get {return _ errors ;}
}

The modelstate of viewdatadictionary is a modelstate dictionary collection class modelstatedictionary. By default, this attribute only has no modelstate in an instance.

In the following sentence, binder. bindmodel (controllercontext, bindingcontext) is the place where the parameters are actually bound. We know that the default binder is defamodelmodelbinder, so let's take a look at your bindmodel method:

Public Virtual Object bindmodel (controllercontext, modelbindingcontext bindingcontext) {If (bindingcontext = NULL) {Throw new argumentnullexception ("bindingcontext");} bool implements medfallback = false; If (! String. isnullorempty (bindingcontext. modelname )&&! Bindingcontext. valueprovider. containsprefix (bindingcontext. modelname) {// we couldn't find any entry that began with the prefix. if this is the top-level element, fall back // to the empty prefix. if (bindingcontext. fallbacktoemptyprefix) {bindingcontext = new modelbindingcontext () {modelmetadata = bindingcontext. modelmetadata, modelstate = bindingcontext. modelstate, propertyfilter = bindingcon Text. propertyfilter, valueprovider = bindingcontext. valueprovider}; inclumedfallback = true;} else {return NULL ;}// simple model = int, String, etc .; determined by calling typeconverter. canconvertfrom (typeof (string) // or by seeing if a value in the request exactly matches the name of the model we're binding. // complex type = everything else. if (! Descrimedfallback) {bool descrimrequestvalidation = validate (controllercontext, bindingcontext); valueproviderresult vpresult = bindingcontext. unvalidatedvalueprovider. getvalue (bindingcontext. modelname, skipvalidation :! Extends mrequestvalidation); If (vpresult! = NULL) {return bindsimplemodel (controllercontext, bindingcontext, vpresult) ;}} if (! Bindingcontext. modelmetadata. iscomplextype) {return NULL;} return bindcomplexmodel (controllercontext, bindingcontext );}

There is an IF (! String. isnullorempty (bindingcontext. modelname) &&! Bindingcontext. valueprovider. containsprefix (bindingcontext. modelname) ) Of course, under normal circumstances
Bindingcontext. modelname is not empty, Bindingcontext. valueprovider. containsprefix (bindingcontext. modelname) is used to check whether all keys in all valueproviders have a parameter name containing action,Generally, we use childactionvalueproviderfactory, formvalueproviderfactory, and querystringvalueproviderfactory most. childactionvalueproviderfactory is usually used becauseCodeHTML. renderaction. Normally, childactionvalueproviderfactory should contain the bindingcontext. modelname; when our actual parameter values are in formvalueproviderfactory and querystringvalueproviderfactory, if our action parameter is of the simple data type, then valueproviderfactory also contains the bindingcontext. modelname. For example, our action is defined as public actionresult index (string name, string age). The access URL is http: // localhost: 7503/home/index? Name = majiang & age = 27. The running process is basically the same as calling html. renderaction. The value of bindingcontext. valueprovider. containsprefix (bindingcontext. modelname) is true..

So let's take a closer look.

Bool required mrequestvalidation = should1_mrequestvalidation (controllercontext, bindingcontext );
Valueproviderresult vpresult = bindingcontext. unvalidatedvalueprovider. getvalue (bindingcontext. modelname, skipvalidation :! Extends mrequestvalidation );
If (vpresult! = NULL ){
Return bindsimplemodel (controllercontext, bindingcontext, vpresult );
}

By default, the parameter mrequestvalidation is true, which indicates the verification result data, while the modelbindingcontext unvalidatedvalueprovider

Internal iunvalidatedvalueproviderUnvalidatedvalueprovider{
Get {
Return (valueprovider as iunvalidatedvalueprovider )?? New unvalidatedvalueproviderwrapper (valueprovider );
}
}

Here, the bindingcontext. unvalidatedvalueprovider. getvalue method is easy to understand. Normally, vpresult is not null.

Internal object bindsimplemodel (controllercontext, modelbindingcontext bindingcontext, valueproviderresult) {bindingcontext. modelstate. setmodelvalue (bindingcontext. modelname, valueproviderresult); // if the value provider returns an instance of the requested data type, we can just short-circuit // The Evaluation and return that instance if (bindingcontext. mode Ltype. isinstanceoftype (valueproviderresult. rawvalue) {return valueproviderresult. rawvalue;} // since a string is an ienumerable <char>, we want it to skip the two checks immediately following if (bindingcontext. modeltype! = Typeof (string) {// conversion results in 3 cases, as below if (bindingcontext. modeltype. isarray) {// Case 1: User asked for an array // valueproviderresult. convertize () understands array types, so pass in the array type directly object modelarray = convertproviderresult (bindingcontext. modelstate, bindingcontext. modelname, valueproviderresult, bindingcontext. modeltype); Return modelarray;} type Enumerabletype = typehelpers. extractgenericinterface (bindingcontext. modeltype, typeof (ienumerable <>); If (enumerabletype! = NULL) {// Case 2: User asked for a collection rather than array // need to call convertize () on the array type, then copy the array to the Collection object modelcollection = createmodel (controllercontext, bindingcontext, bindingcontext. modeltype); Type elementtype = enumerabletype. getgenericarguments () [0]; Type arraytype = elementtype. makearraytype (); object modelarray = convertproviderresult (bindingcontext. modelstate, bindingcontext. modelname, valueproviderresult, arraytype); Type collectiontype = typeof (icollection <> ). makegenerictype (elementtype); If (collectiontype. isinstanceoftype (modelcollection) {collectionhelpers. replacecollection (elementtype, modelcollection, modelarray);} return modelcollection;} // Case 3: User asked for an individual Element Object Model = convertproviderresult (bindingcontext. modelstate, bindingcontext. modelname, valueproviderresult, bindingcontext. modeltype); Return Model ;}

The bindsimplemodel method is relatively simple, but it is still complicated. Let me first look at the first sentence.

Bindingcontext. modelstate. setmodelvalue (bindingcontext. modelname, valueproviderresult );

Public void setmodelvalue (string key, valueproviderresult value ){
Getmodelstateforkey (key). value = value;

}

private modelstate getmodelstateforkey (string key) {
If (Key = NULL) {
throw new argumentnullexception ("key ");
}< br> modelstate;
If (! Trygetvalue (Key, out modelstate) {
modelstate = new modelstate ();
This [Key] = modelstate;
}< br> return modelstate;
}< br> from here, we can see that a key corresponds to a modelstate corresponding to a valueproviderresult ;

The last sentence of this method
Object Model = convertproviderresult (bindingcontext. modelstate, bindingcontext. modelname, valueproviderresult, bindingcontext. modeltype); The convertproviderresult method is actually just one sentence.
 Object convertedvalue = valueproviderresult. convertize (destinationtype); this means to convert the data to the data type we need.

If convertedvalue is null and bindingcontext. if modeltype is the owner type, we need to call bindcomplexmodel once. We also know bindingcontext with the previous analysis. valueprovider. containsprefix (bindingcontext. modelname) if false is returned, the bindcomplexmodel method is also called.

The bindcomplexmodel method is very complex. Here is an object model = bindingcontext. Model; while bindingcontext. model is actually return modelmetadata. model; the specific implementation is as follows:

Public object model {
Get {
If (_ modelaccessor! = NULL ){
_ Model = _ modelaccessor ();
_ Modelaccessor = NULL;
}
Return _ model;
}
Set {
_ Model = value;
_ Modelaccessor = NULL;
_ Properties = NULL;
_ Realmodeltype = NULL;
}
}

By default, this _ modelaccessor = NULL, In the controlleractioninvoker. getparametervalue method, bindingcontext

Modelmetadata = modelmetadataproviders. Current. getmetadatafortype (Null, Parametertype). If the parameter is null, the specific implementation of getmetadatafortype is as follows:

Private ienumerable <modelmetadata> getmetadataforpropertiesimpl (object container, type containertype ){
Foreach (propertydescriptor property in gettypedescriptor (containertype). getproperties ()){
Func <Object> modelaccessor = Container = NULL? Null: getpropertyvalueaccessor (container, property );
Yield return getmetadataforproperty (modelaccessor, containertype, property );
}
}

Now we will return to the bindcomplexmodel of defamodelmodelbinder.

If (model = NULL ){
Model = createmodel (controllercontext, bindingcontext, modeltype );
}

Therefore, in general, bindcomplexmodel does not return NULL values..

Bindcomplexmodel converts the current data type to typeof (idictionary <,>). If the data type is successful, the updatedictionary method is called. If the data type is converted to typeof (idictionary <,> if the conversion fails, it is converted to typeof (ienumerable <>) and processed according to the set. The updatecollection method is called. If the conversion fails, the bindcomplexelementalmodel method is called according to the normal strong type, this type of binding is the most widely used in scenarios with strong types. The core code in bindcomplexelementalmodel is to call

Bindproperties (controllercontext, newbindingcontext); method,

Private void bindproperties (controllercontext, modelbindingcontext bindingcontext ){
Ienumerable <propertydescriptor> properties = getfilteredmodelproperties (controllercontext, bindingcontext );
Foreach (propertydescriptor property in properties ){
Bindproperty (controllercontext, bindingcontext, property );
}
}

The specific implementation of defamodelmodelbinder is very complicated. When writing an action, we should know whether bindsimplemodel or bindcomplexmodel is used in the bindmodel, and which valueproviderdictionary provides the specific parameter?.

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.