I have registered a custom model binder for MyList in global.asax. However the model binder does not fire for nested properties, for simple types it works fine. In the example below, it fires for Index() but not does not fire for Index2()
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(MyList), new MyListBinder());
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Code:
public class MyListBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
return new MyList();
}
}
public class MyList
{
public List<int> items { get; set; }
}
public class MyListWrapper
{
public MyList listItems { get; set; }
}
public class TestController : Controller
{
public ActionResult Index(MyList list) // ModelBinder fires :-)
{
return View();
}
public ActionResult Index2(MyListWrapper wrapper) // ModelBinder does not fire! :-(
{
return View();
}
}
Model binders are used to allow actions to accept complex object types as parameters. These complex types should be generated via POST requests, for example, by submitting a form. If you have a highly complex object that cannot be binded by the default model binder (or it wouldn't be effective), you can use custom model binders.
To answer your question:
if you don't add a custom model binder for the MyListWrapper
class too, the BindModel(of the MyListBinder)
won't be called in a GET request, this is how ASP.NET MVC works.
However, if you modify your code by adding a POST action with the MyListWrapper
parameter, you can see that the BindModel method is called properly.
[HttpGet]
public ActionResult Index2() // ModelBinder doesn't fire
{
return View();
}
[HttpPost]
public ActionResult Index2(MyListWrapper wrapper) // ModelBinder fires
{
return View();
}
And the Index2 view
@model fun.web.MyListWrapper
@using (Html.BeginForm())
{
@Html.HiddenFor(m => m.listItems)
<input type="submit" value="Submit" />
}
If you'd like "control" the action parameters in a GET request, you should use action filters.
You defined binder for MyList
, so it triggers only when action method input parameter is of type MyList
.
ModelBinders.Binders.Add(typeof(MyList), new MyListBinder());
If you want model binder to trigger even when your MyList
is nested into other model, you have to do this:
[ModelBinder(typeof(MyListBinder))]
public class MyList
{
public List<int> items { get; set; }
}
Then, model binder triggers whenever it encounters MyList
type, even when it's nested.
Add this to your global:
ModelBinders.Binders.Add(typeof(MyListWrapper), new MyListWrapperBinder());
And then create a MyListWrapperBinder that can handle the binding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With