What, exactly, does a modelbinder do? How to use i

2019-04-10 10:40发布

问题:

I was researching something and came across this blog post at buildstarted.com about model binders. It actually works pretty darn well for my purposes but I am not sure exactly whats going on behind the scenes. What I did was create a custom ModelBinder called USerModelBinder:

public class UserModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");
        MyEntities db = new MyEntities();
        User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);
        return user;
    }
}

Then in my Global.asax.cs I have:

ModelBinders.Binders.Add(typeof(User), new UserModelBinder());

My understanding is that using the model binder allows me to NOT have to use the following lines in every controller action that involves a "User". So instead of passing in an "id" to the action, the modelbinder intercepts the id, fetches the correct "item"(User in my case) and forwards it to the action for processing.

        MyEntities db = new MyEntities();
        User user = db.Users.SingleOrDefault(u => u.UserName == value.AttemptedValue);

I also tried using an annotation on my User class instead of using the line in Global.asax.cs:

[ModelBinder(typeof(UserModelBinder))]
public partial class User
{
}

I'm not looking for a 30 page white paper but I have no idea how the model binder does what it does. I just want to understand what happens from when a request is made to the time it is served. All this stuff "just working" is not acceptable to me, lol. Also, is there any difference between using the annotation versus adding it in Global.asax.cs? They seem to work the same in my testing but are there any gotchas?

回答1:

Usually the Model Binder (in MVC) looks at you Action method and sees what it requires (as in, the objects types). It then tries to find the values from the HTTP Request (values in the HTTP Form, QueryString, Json and maybe other places such as cookies etc. using ValueProviders). It then creates a new object with the parameters that it retrieves.

IMO What you've done is not really "model binding". In the sense that you've just read the id and fetched the object from the DB.

example of usual model binding:

// class
public class SomeClass
{
    public int PropA {get;set;}
    public string PropB {get;set;}
}

// action

public ActionResult AddSomeClass(SomeClass classToBind)
{
  // implementation
}

// pseudo html

      <form action="">
       <input name="PropA" type="text" />
       <input name="PropB" type="text" />
      </form>

if you post a form that contains the correct values (lets say you post a form with PropA and PropB ) the model binder can identify that you've sent those values in the form and build a SomeClass object.

If you really want to create a real working example you should use a strongly typed View and use HtmlHelper's EditorFor (or EditorForModel) to create all the correct names that MVC needs.

--

for reference MVC's default binder is the DefaultModelBinder, and some (there are more, you can look around in the System.Web.Mvc namespace) ValueProviders that it uses by default are the FormValueProvider and the QueryStringValueProvider

So, as I already said, how this basically works is that the default model binder reads the model that the action is recieving (say SomeClass in the example) reads what are the values that it can read (say PropA and PropB) and asks the ValueProviders for the correct values for the properties.

Also, if I recall correctly, you can also see the value providers in runtime using the ValueProviderFactories static class.



回答2:

A ModelBinder looks at the arguments of the selected Controller action's method signature, then converts the values from the ValueProviders into those arguments.

This happens when the ControllerActionInvoker invokes the action associated with the ControllerContext, because the Controller's Execute() method told it to.

For more about the ASP.NET MVC execution process, see Understanding the MVC Application Execution Process