Using ModelBinder attribute vs. ModelBinders.Add()

2019-01-23 02:42发布

问题:

Can someone tell me the pros/concs to using [ModelBinder()] attribute vs. registering model binders via ModelBinders.Add() in global.asax?

One advantage I can think of is that it's more explicit, whereas registering in the global ModelBinders is not as abvious to someone inspecting the action method.

One tradeoff I can think of is that it's not reusable, since you would have to add this attribute to all action methods that need to use this model binder, whereas registering in the global ModelBinders will make it available for all action methods receiving that model.

Is this the only difference?

In other words, would stating this be correct:

  • If you only use the model in one action method (maybe two, get + post), then use [ModelBinder()].
  • If you use the model in more than one action method, then register it in the global ModelBinders.

回答1:

The result of those techniques will be the same so it is mostly a matter of what the team feels more comfortable with. So you could come up with a convention like the one you stated.

Personally, I prefer not having to set the attribute on every action method that uses that model. So I would choose one of the following options:

  • Set the attribute on the model class like:

    [ModelBinder(typeof(MyModelBinder))]
    public class MyModel
    {
        ...
    }    
    
  • Globally register the binder

    ModelBinders.Binders.Add(typeof(MyModel), new MyModelBinder())
    

Another reason why I prefer one of those is because if you ever have to manually trigger the model binding process, you may also want your custom model binder to be used:

public ActionResult SomeActionMethod()
{
     MyModel model = ...

     //manually invoke the model binding process considering only query string data
     //The custom model binder will be used only if it was globally registered
     //in the binders dictionary or set in an attribute of the model class
     TryUpdateModel(model, new QueryStringValueProvider())

     ...
}

You also have an option of implementing your own logic for selecting model binders by implementing the interface IModelBinderProvider and registering in the global.asax as in

ModelBinderProviders.BinderProviders.Add(new CustomModelBinderProvider()) 

One way of using the attribute in the method parameters could be overriding for that particular method the model binder that would otherwise be used. So you could globally register a model binder for your class and override it in one particular action method using the attribute.

In the end there are quite a few options for selecting the model binder. In asp MVC 3 this will be resolved in the following way (assuming you are using the default ControllerActionInvoker)

  1. The attribute on the parameter of the action. See GetParameterValue method of the ControllerActionInvoker class

  2. The Binder returned from the IModelBinderProvider. See GetBinder method in the ModelBinderDictionary class

  3. The Binder globally registered in the ModelBinders.Binders dictionary.

  4. The Binder defined in the [ModelBinder()] attribute for the model type.

  5. The DefaultModelBinder.



回答2:

It looks to me like the advantage of using an attribute rather than adding to the model binders collection in Global.asax is that you can tell the method (or class) which specific binder to use rather than associating the binder with a specific type. Then you can create the model based on a context rather than a type.