绑定属性包括和排除具有复杂类型嵌套对象的属性。

12

好的,这很奇怪。我无法在ASP.NET MVC中使用BindAttributeIncludeExclude属性来处理嵌套的复杂类型对象。

以下是我的操作:

模型:

public class FooViewModel {

    public Enquiry Enquiry { get; set; }
}

public class Enquiry {

    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

HTTP POST 操作:

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(
    [Bind(Include = "Enquiry.EnquiryId")]
    FooViewModel foo) {

    return View(foo);
}

视图:

@using (Html.BeginForm()) {

    @Html.TextBoxFor(m => m.Enquiry.EnquiryId)
    @Html.TextBoxFor(m => m.Enquiry.Latitude)

    <input type="submit" value="push" />
}

完全不起作用。如果我不像这里所述那样定义Enquiry类的BindAttribute,我能使它起作用吗?


1
我认为你最好按照你参考的另一篇帖子中所描述的方式去做。 - JoJa
是的,看起来是这样的:s 我不能在域模型项目内这样做。我应该在MVC项目内定义部分类并在那里完成。 - tugberk
在此处尝试我的解决方案:https://stackoverflow.com/questions/47644699/how-to-bind-nested-objects-on-httppost-in-asp-net-mvc/47645228#47645228 - NoWar
2个回答

16

是的,你可以这样使它工作:

[Bind(Include = "EnquiryId")]
public class Enquiry 
{
    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

并且你的操作:

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(FooViewModel foo) 
{
    return View(foo);
}
这将仅在绑定中包含EnquiryId并将Latitude保留为空。
话虽如此,我不建议使用Bind属性。我的建议是使用视图模型。在这些视图模型中,只包括对于特定视图有意义的属性。
因此,只需重新调整视图模型即可:
public class FooViewModel 
{
    public EnquiryViewModel Enquiry { get; set; }
}

public class EnquiryViewModel 
{
    public int EnquiryId { get; set; }
}

好了,你不再需要担心绑定的问题了。


谢谢@darin。这似乎是唯一的方法。我还没有深入研究,但我们能否通过创建新的自定义模型绑定器来调整DefaultModelBinder类以使其正常工作呢? - tugberk
2
@tugberk,我真的不会走那条路。使用视图模型是解决这些问题的方法。不仅仅是这些问题。在ASP.NET MVC应用程序中,您应该始终使用视图模型。 - Darin Dimitrov
对于你的第二个建议,我总是在这之间:s - tugberk
4
@tugberk,我的想法是在您的视图模型中定义验证逻辑。这个验证逻辑并不总是与您在域模型上定义的验证逻辑相同。您在视图模型上定义的验证逻辑仅在给定视图的上下文中有效,而域上的验证逻辑则适用于整个应用程序。 - Darin Dimitrov
我的基础模型是使用EF数据库优先生成的,因此我无法对它们进行注释。我的ViewModel是两个模型的组合(父子关系),并且每种方法都有不同的绑定需求(编辑需要ID,创建则不需要)。这相当丑陋。 - Christopher G. Lewis
显示剩余2条评论

0

在我看来,有一种更好的方法来完成这个任务。

实际上,如果你在视图模型中有多个模型,那么发布控制器的签名将包含相同的模型,而不是视图模型。

例如:

public class FooViewModel {
    public Bar BarV { get; set; }
    public Enquiry EnquiryV { get; set; }
    public int ThisNumber { get; set; }
}

public class Bar {
    public int BarId { get; set; }
}

public class Enquiry {
    public int EnquiryId { get; set; }
    public string Latitude { get; set; }
}

而控制器中的后续操作将如下所示。

[ActionName("Foo"), HttpPost]
public ActionResult Foo_post(
    [Bind(Include = "EnquiryId")]
    Enquiry EnquiryV,
    [Bind(Include = "BarId"])]
    Bar BarV,
    int ThisNumber
{
    return View(new FooViewModel { Bar = BarV, Enquiry = EnquiryV, ThisNumber = ThisNumber });
}

所有这些都是在视图仍然保持这样的情况下完成的

@using (Html.BeginForm()) {

    @Html.TextBoxFor(m => m.EnquiryV.EnquiryId)
    @Html.TextBoxFor(m => m.EnquiryV.Latitude)
    @Html.TextBoxFor(m => m.BarV.BarId)
    @Html.TextBoxFor(m => m.ThisNumber)

    <input type="submit" value="push" />
}

请记住,此表单仍将返回纬度(以您设置的方式),但是由于它未包含在Enquiry的绑定包含字符串中,因此该操作不会接受结果查询中的新值。建议将纬度设为禁用或非表单元素,以防止额外的发布数据。
在任何其他情况下,您都可以正常使用bind,但出于某种原因,它不喜欢复杂模型的点符号表示法。
顺便说一句,我不会直接将绑定属性放在类上,因为它可能会导致其他问题,例如代码重复,并且不能考虑您可能希望具有不同绑定的某些情况。
(我修改了一些变量名称以使其更清晰。我也知道您的问题相当陈旧,但在寻找答案时,这是我在尝试自己的解决方案并得出我发布的解决方案之前遇到的第一个SO。我希望它能够帮助寻求解决相同问题的其他人。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接