嵌套级别 + 其他绑定器的 DefaultModelBinder 问题

DefaultModelBinder Problem with nested levels + other binders

提问人:claco 提问时间:2/16/2010 最后编辑:tereškoclaco 更新时间:8/31/2013 访问量:956

问:

我有一种我认为有点正常的情况,我需要将表单帖子绑定到“订单”模型。此模型包含以下几个层次的信息:

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.Add 它到 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 映射到 Address 绑定器的 Country 属性时,应该调用 CountryModelBinder。

ASP.NET-MVC 嵌套 ModelBinder 默认ModelBinder

评论

0赞 4/2/2010
这可能会对您有所帮助:stackoverflow.com/questions/2462506/...
0赞 UpTheCreek 6/29/2010
我在嵌套模型结构上遇到了同样的问题,在我看来,顶层和下层的属性正在被绑定,但任何低于此值的属性似乎都被忽略了。这是模型活页夹的正常行为吗?似乎相当武断的行为。

答:

0赞 Linkgoron 3/1/2011 #1

我尝试过做你在这里做过的事情,似乎在 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 接口,但我只是想展示一些可以工作的东西。