Пользовательская модель Binder не срабатывает

Я зарегистрировал собственное связующее устройство для MyList в global.asax. Однако модельное связующее не срабатывает для вложенных свойств, для простых типов оно отлично работает. В приведенном ниже примере он запускается для Index(), но не запускается для 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);
}

Код:

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

Ответ 1

Взаимодействия с образцами используются, чтобы позволить действиям принимать сложные типы объектов в качестве параметров. Эти сложные типы должны генерироваться посредством запросов POST, например, путем отправки формы. Если у вас есть очень сложный объект, который не может быть привязан связующим устройством по умолчанию (или он не будет эффективным), вы можете использовать пользовательские привязки модели.

Чтобы ответить на ваш вопрос: если вы не добавите настраиваемое связующее устройство для класса MyListWrapper, BindModel (из MyListBinder) не будет вызываться в запросе GET, так работает ASP.NET MVC. Однако, если вы измените свой код, добавив действие POST с параметром MyListWrapper, вы увидите, что метод BindModel вызывается правильно.

[HttpGet]
public ActionResult Index2()  // ModelBinder doesn't fire
{
    return View();
}

[HttpPost]
public ActionResult Index2(MyListWrapper wrapper) // ModelBinder fires
{
    return View();
}

И представление Index2

@model fun.web.MyListWrapper

@using (Html.BeginForm())
{
    @Html.HiddenFor(m => m.listItems)
    <input type="submit" value="Submit" />
}

Если вы хотите "контролировать" параметры действия в запросе GET, вы должны использовать фильтры действий.

Ответ 2

Добавьте это в свой мир:

ModelBinders.Binders.Add(typeof(MyListWrapper), new MyListWrapperBinder());

И затем создайте MyListWrapperBinder, который может обрабатывать привязку.

Ответ 3

Вы определили связующее для MyList, поэтому он запускается только тогда, когда параметр ввода метода действия имеет тип MyList.

ModelBinders.Binders.Add(typeof(MyList), new MyListBinder());

Если вы хотите, чтобы связующее устройство было запущено, даже если ваш MyList вложен в другую модель, вы должны сделать это:

[ModelBinder(typeof(MyListBinder))] 
public class MyList
{
    public List<int> items { get; set; }
}

Затем, связывание модели запускается всякий раз, когда встречается тип MyList, даже когда он вложен.

Ответ 4

Ваше связующее устройство соответствует вашему классу MyList, а не MyListWrapper. MyListBinder используется только с классом MyList или классом, который унаследован от MyClass.