Understanding Modelbinder in ASP.

Source: Internet
Author: User

the nature of model bindings

The execution of any controller method is controlled by the Action Invoker component (hereinafter replaced with Invoker). For each action method's arguments, the invoker component gets a model binder object (ModelBuilder). The role of Model Binder involves looking for a possible value (from the HTTP request context) for the action method parameter. Each parameter can be bound to a different model binder, but in most cases we are using the default model binder-defaultmodelbinder (if we do not explicitly set the use of custom model binders).

Each model binder uses its own specific algorithm to set a value for the action method parameter. The default model binder object uses a large number of reflection mechanisms. Specifically, for each action method parameter, Model binder attempts to find a matching value in the request parameter according to the parameter name. For example, if an action method parameter is named text, then Modelbinder will look for a name value pair (Entry) with the same name in the context of the request. If found, the model binder continues to convert the value of entry to the action method parameter type. If the type conversion succeeds, the converted value is assigned to the action method argument, or an exception is thrown.

Note that the exception is thrown immediately whenever a type is encountered that cannot be successfully converted, or if a matching parameter is not found in the context of the request (and the type of the parameter is a non-nullable type). That is, the action method is called only if all declared parameters are parsed successfully by the model binder. Also note that the exception generated by the model binder cannot be captured in the action method, and we must set a global error handler in Global.asax to capture handling of these exceptions. Also note that exceptions are thrown only if the method parameter cannot be assigned a value of NULL. So for the following scenario:

Public ActionResult testaction (string name, Int32 age) {   // ...   return View ();}   

If the request context does not contain a entry named name, there is no problem, and the name parameter value is Modelbinder set to null. The age parameter is different, however, because the Int32 type is a base value type and cannot be assigned a null value. If we need to allow the age parameter to be passed, then we simply need to modify the code as follows:

Public ActionResult testaction (string name, Nullable<int32> age) {   // ... return View ();}   
The default model binder looks for the parameter values in the context of the HTTP request in the following order:
Request Data Source Description
Request.Form Parameters submitted through the form
Routedata.values Route parameters
Request.QueryString Query parameters, like HTTP://ABC.COM?NAME=JXQ, where the NAME=JXQ is the query parameter
Request.Files Files uploaded with the request

When we have multiple parameters to upload, we'd better not create an action method parameter for each request parameter to avoid too long a method parameter list.

For the default model binder, we can encapsulate the parameter list as a parameter type, and then the default model binder sets the properties of the parameter object by matching the same name (property name and request parameter name) as above.

To invoke model bindings manually

Model bindings are called automatically by default, but we can also manually model bindings. For example, the following code example:

[HttpPost] Public ActionResult Registermember () {   new person ();   Updatemodel (MyPerson);   return View (MyPerson);}  

The above Updatemodel (MyPerson) is the manual model binding.

The greatest benefit of manual model binding is to make the model binding process more flexible. For example, we can restrict type binding to take only the form submission parameters, so we can do the following as follows:

[HttpPost] Public ActionResult Registermember () {person   MyPerson = (person) DependencyResolver.Current.GetService (  typeof (person));   new Formvalueprovider (ControllerContext));    return View (MyPerson);}   

Formvalueprovider implements the Ivalueprovider interface, and several other parameters correspond to the Ivalueprovider implementation as follows:

Request Data Source Ivalueprovider implementation
Source Request.Form Formvalueprovider
Routedata.values Routedatavalueprovider
Request.QueryString Querystringvalueprovider
Request.Files Httpfilecollectionvalueprovider

In addition to the above method can restrict the data source of type binding, we can also use the direct use of formcollection as Ivalueprovider, as follows:

[HttpPost] Public ActionResult Registermember (formcollection formData) {person   MyPerson = (person) DependencyResolver.Current.GetService (typeof (person));   Updatemodel (MyPerson, formData);   return View (MyPerson);}  

The manual process of model binding can occur in two ways: the first method is to catch the exception directly, and the second is to take advantage of the TryUpdateModel method. The first method is as follows:

[HttpPost] Public ActionResult Registermember (formcollection formData) {person   MyPerson = (person) DependencyResolver.Current.GetService (typeof (person));   Try   {      Updatemodel (MyPerson, formData);   }   catch (InvalidOperationException e)   {      //return View (MyPerson);}     

The second method is as follows:

[HttpPost] Public ActionResult Registermember (formcollection formData) {person   MyPerson = (person) DependencyResolver.Current.GetService (typeof (person));   if (TryUpdateModel (MyPerson, formData))   {      // normal processing   }   //return View (MyPerson);}      

The first section has said that when using the default model binder, we cannot capture the exception thrown by the model binding process in the action method, and you can configure the error handler in Global.ascx to capture processing. In addition, we can also use Modelstate.isvalid to determine whether the default model bindings are successful.

Custom Model Binder System

We can customize the Ivalueprovider implementation , the Ivalueprovider interface is defined as follows:

namespace SYSTEM.WEB.MVC {   using System;   interface Ivalueprovider {      bool Containsprefix (string prefix); Valueproviderresult GetValue (string key);}}    

We customize a Ivalueprovider implementation as follows:

Using System;using system.globalization; using System.Web.Mvc; namespace customemodelbinderdemo.controllers.valueprovider{public span class= "keyword" >class myvalueprovider:ivalueprovider {public bool Containsprefix (string prefix) {return System.String.Compare ( "curtime", prefix, stringcomparison.ordinalignorecase) = = 0;} public valueproviderresult GetValue (string key) { return containsprefix (key)? new valueproviderresult (DateTime.Now, null, CultureInfo.CurrentCulture): null;}}         

The GetValue (string key) method above takes a matching value from the HTTP request context to the Valueproviderresult object, based on the action method parameter name. The Valueproviderresult type contains a ConvertTo (type type) method that converts the value it encapsulates to the specified type. This coincides with the type conversion described in the first section (also stating that the Xvalueprovider object is responsible for type conversion work during model binding, because the model binder invokes the Xvalueprovider object for type conversion).

Then we define a Valueprovider factory class:

Classcurrenttimevalueproviderfactory:valueproviderfactory {public   override Ivalueprovider Getvalueprovider (ControllerContext controllercontext) {      return new Currenttimevalueprovider ();}   } 

Then we register this factory class in Global.asax's Application_Start method:

void Application_Start () {   arearegistration.registerallareas ();   ValueProviderFactories.Factories.Insert (new Currenttimevalueproviderfactory ());   // registered Valueprovider factory object Registerglobalfilters (globalfilters.filters); RegisterRoutes (routetable.routes);}  

Finally, we use the following in action:

Public ActionResult Clock (DateTime curtime) {   return Content ("The Time is" + Curtime.tolongtimestring ());}  

We can also customize the Model binder object in two ways: the first method is to inherit the Defaultmodelbinder class, then rewrite its Createmodel method, and in the Application_ The Start method sets ModelBinders.Binders.DefaultBinder to this model binder (indicating that the default model bindings are now defined by ourselves):

Class Dimodelbinder:defaultmodelbinder {   object Createmodel (ControllerContext controllercontext, Modelbindingcontext BindingContext, Type modeltype) {      base. Createmodel (ControllerContext, BindingContext, Modeltype); }}

Then set the default model binder to Dimodelbinder in the Global.asax Application_Start method:

void Application_Start () {   // ...   new Dimodelbinder ();   // ...}

The second method inherits the Imodelbinder interface and then implements its interface method:

PublicClass Personmodelbinder:imodelbinder {PublicObject Bindmodel (ControllerContext controllercontext, Modelbindingcontext BindingContext) {//Check if there is an existing model object, if one is not created (Bindingcontext.model will not be null if manual binding is used) person model = (person) Bindingcontext.model?? (person) DependencyResolver.Current.GetService (typeof (person));//Find if the value provider has the required prefixBOOL Hasprefix = BindingContext.ValueProvider.ContainsPrefix (bindingcontext.modelname);//Bindingcontext.modelname returns the name of the current modelString searchprefix = (hasprefix)? Bindingcontext.modelname +"." :"";//Fills the Model object's field model. PersonId =Int. Parse (GetValue (BindingContext, Searchprefix,"PersonId ")); Model. FirstName = GetValue (BindingContext, Searchprefix,"FirstName "); Model. LastName = GetValue (BindingContext, Searchprefix,"LastName "); Model. BirthDate = DateTime.Parse (GetValue (BindingContext, Searchprefix,"BirthDate ")); Model. isapproved = Getcheckedvalue (BindingContext, Searchprefix,"IsApproved "); Model. role = (role) Enum.parse (typeof (Role), GetValue (BindingContext, Searchprefix,"Role "));return model; }Privatestring GetValue (modelbindingcontext context, string prefix, string key) {Valueproviderresult VPR = context. Valueprovider.getvalue (prefix + key); return vpr = null? NULL:VPR. Attemptedvalue; } private bool getcheckedvalue (modelbindingcontext context, string prefix, string key) {bool result = false; Valueproviderresult VPR = context. Valueprovider.getvalue (prefix + key); if (vpr! = null) {result = (bool) vpr. ConvertTo (typeof (bool));} return result;}}            

Then again we need to register the model binder, which can be globally registered by adding the following code to the Application_Start method:

void Application_Start () {   // ...   MODELBINDERS.BINDERS.ADD (// ...}   

You can also set the model binder for an action parameter by attribute, as follows:

Public ActionResult Index (        [Modelbinder (typeof (Datetimemodelbinder))] DateTime thedate)

Or bind the model binder to the type as follows:

[Modelbinder (typeof (Personmodelbinder))]class Person {   [Hiddeninput (displayvalue=false)]   set;}}   

Finally we look at how to customize the Modelbinderprovider , which is mainly used in cases with multiple model binders to choose which type of binder to use, we need to implement the Imodelprovider interface:

Using System; using SYSTEM.WEB.MVC; using Mvcapp.models; namespace Mvcapp.infrastructure {   null;    }}}

And then it's a cliché. Register in the Application_Start method:

MODELBINDERPROVIDERS.BINDERPROVIDERS.ADD (new Custommodelbinderprovider ());

Resources:

https://www.simple-talk.com/dotnet/asp.net/the-three-models-of-asp.net-mvc-apps/

<pro ASP. MVC3 Framework, 3rd edition>. Chapter 17:model Binding

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.