DefaultModelBinder Проблема с вложенными уровнями + другие связующие - PullRequest
7 голосов
/ 16 февраля 2010

У меня есть, как мне кажется, несколько нормальная ситуация, когда мне нужно привязать посты формы к модели "заказа". Эта модель имеет несколько уровней информации:

Order.Billing.FirstName
Order.Billing.Address.City
Order.Billing.Address.Country

Используя DefaultModelBinder, если я отправлю форму в действие, которое принимает эту модель заказа в качестве параметра, следующие поля JustWork (TM):

<%=Html.TextBox("Billing.FirstName")%>
<%=Html.TextBox("Billing.Address.City")%>

Это поле не:

<%=Html.TextBox("Billing.Address.Country")%>

Морщина, которая у меня есть, принадлежит собственности страны. В нашем случае Address.Country возвращает экземпляр класса Country (логика ISO2 / 3 / Name / Code). Это не строка. Не удивительно, что по умолчанию это не работает.

Моей первой мыслью было создать CountryModelBinder (наследовать DefaultModelBinder) и ModelBinders.Binders. Добавить его к типу Country. Когда я это делаю, CountryModelBinder никогда не вызывается в описанном выше сценарии.

Моя вторая мысль состояла в том, чтобы создать AddressModelBinder (наследовать DefaultModelBinder) и связать его с нашим типом адреса. Хотя это вызывается, вызов SetProperty для «Country» имеет пустое значение, даже если в форме размещено поле с именем «Billing.Address.Country».

После некоторой обработки кажется, что поведение привязки модели вызывает CreateModel только тогда, когда модель является классом верхнего уровня, который требуется для действия, и для всех других связывателей их BindPropery / SetProperty вызывается для дочерних свойств.

Другими словами, если я создаю связующие модели для Order, OrderAddress (Billing), Address и Country. Для действия, которое принимает заказ, вызывается только OrderModelBinder.CreateModel. Для некоторых вещей вызываются ORderAddress и Address.BindProperty / SetProperty, и иногда аргумент значения SetProperty является пустым, если он явно размещен в имени, соответствующем другим сопоставлениям свойств поля.

Достаточно просто добавить код в OrderModelBinder, чтобы вытащить Billing.Address.Country из Request.Form. Но у меня есть несколько моделей, которые используют Address, и все они работают, что кажется сломанным.

Что мне здесь не хватает? Есть ли способ, чтобы CountryModelBinder действительно вызывался в этом случае? Я думаю, что CountryModelBinder должен вызываться, когда Billing.Address.Country сопоставлен со свойством Country подшивки Address.

1 Ответ

0 голосов
/ 28 февраля 2011

Я пытался сделать то, что вы сделали здесь, очевидно, на MVC3 это действительно работает, если я предоставлю связыватель модели для этого типа.

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

Модель:

public class SimpleModel
    {
        public string Value { get; set; }
        public int Other { get; set; }
    }

    public class ComplexModel
    {
        public SimpleModel Complexity {get;set;}
        public string StrVal { get; set; }
    }

некоторые связующие вещества:

public class MBinder : IModelBinder
        {
            public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if ( bindingContext.ModelType == typeof(SimpleModel))
                {
                    var simpleModel= new SimpleModel();
                    simpleModel.Other = 1;
                    simpleModel.Value = controllerContext.HttpContext.Request.Form["Complexity"];

                    return cm;
                }
                return null;
            }
        }

в глобальном asax:

ModelBinders.Binders.Add(typeof (SimpleModel), new MBinder());

код в представлении:

    @model ComplexModel

    @using ( Html.BeginForm() )
{ 
    <fieldset>
        @Html.LabelFor(x => x.Complexity)
        @Html.TextBoxFor(x => x.Complexity)
    </fieldset>

    <fieldset>
        @Html.LabelFor(x => x.StrVal)
        <br />
        @Html.EditorFor(x => x.StrVal)
    </fieldset>
    <input type="submit" />
}

Контроллер:

public ActionResult Index()
        {
            return View();
        }

        [HttpPost]
        public ActionResult Index(ComplexModel model)
        {
            return RedirectToAction("Index");

        }

Кстати, в MVC 3 лучшим вариантом было бы использование интерфейса IModelBinderProvider, но я просто хотел показать что-то, что будет работать.

...