DefaultModelBinder 嵌套级别的问题 +其他粘合剂

发布于 2024-08-22 03:11:17 字数 1424 浏览 4 评论 0原文

我认为这是一种正常的情况,我需要将表单帖子绑定到“订单”模型。该模型有几个级别的信息:

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

使用 DefaultModelBinder,如果我将表单发布到将此 Order 模型作为参数的操作,则以下字段 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)并将其绑定到我们的 Address 类型。虽然确实被调用,但对“Country”的 SetProperty 调用具有空值,即使表单已发布名为“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 实际上被调用?我认为当 Billing.Address.Country 映射到地址绑定器的 Country 属性时,应该调用 CountryModelBinder。

I have what I would think is a somewhat normal situation where I need to bind form posts to an "order" model. This model has a few levels of information to it:

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

Using the DefaultModelBinder, if I POST a form to an action that takes this Order model as the param, the following fields JustWork(TM):

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

This field does not:

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

The wrinkle I have is with the country property. In our case, Address.Country returns a Country class instance (ISO2/3/Name/Code logic). It is not a string. Not surprise that it doesn't work by default.

My first thought was to create a CountryModelBinder (inherit DefaultModelBinder) and ModelBinders.Binders.Add it to the type of Country. When I do that, CountryModelBinder never gets called in the scenerio above.

My second thought was to create an AddressModelBinder (inherit DefaultModelBinder) and bind it to our Address type. While that does get called, the SetProperty call for "Country" has an empty value, even though the form has posted a field called "Billing.Address.Country".

After some tinkering, it appears that the model binding behavior only calls CreateModel when the model is the top level class the action wants, and all other binders have their BindPropery/SetProperty called for child properties.

In other words, if I create model binders for Order, OrderAddress(Billing), Address, and Country. For the action that takes an order, only OrderModelBinder.CreateModel is called. ORderAddress and Address.BindProperty/SetProperty are called for some things, and sometimes SetProperty value argument is empty when it was clearly posted in a name that matches the other field property mappings.

It's easy enough to just add code to OrderModelBinder to pull Billing.Address.Country out of Request.Form. But I have multiple models that use Address and having all of them do that seems broken.

What am I missing here? Is there a way to have the CountryModelBinder actually get called in this case? I would think that the CountryModelBinder should get called when Billing.Address.Country is mapped to the Country property of the Address binder.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

凤舞天涯 2024-08-29 03:11:17

我已经尝试做你在这里所做的事情,看来在 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");

        }

BTW in MVC 3 a better option会使用 IModelBinderProvider 接口,但我只是想展示一些可行的东西。

I've tried doing what you've done here, appearntly on MVC3 it does indeed work if I provide a model binder for that type.

This is just a proof of concept to show that it DOES WORK, and shouldn't be seen as even close to production level code:

Models:

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

some binder:

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

in global asax:

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

code in View:

    @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" />
}

Controller:

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

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

        }

BTW in MVC 3 a better option would be to use the IModelBinderProvider interface, but I just wanted to show something that would work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文