ViewModel中类似的属性名没有绑定到控制器动作方法中

本文关键字:绑定 控制器 方法 属性 ViewModel | 更新日期: 2023-09-27 18:09:39

最近我们更新了我们的应用程序从MVC3到MVC4。在MVC4中,我们发现在ViewModel中拥有像StudioStudioExecutive这样的属性名称会在发布时导致问题。在控制器方法中,当StudioExecutive被填充时,我们总是得到Studio = null

这是我们问题的一个例子,希望有一个解决这个问题的答案。

数据类:

public class TestContact
{
    public List<TestContactItem> Studio { get; set; }
    public List<TestContactItem> StudioExecutive { get; set; }
    public TestContact()
    {
        Studio = new List<TestContactItem>();
        StudioExecutive = new List<TestContactItem>();
    }
}
public class TestContactItem
{
    public int Id { get; set; }
    public string Name { get; set; }
}
控制器方法:

public ActionResult TestContactView()
{
    var vm = new TestContact();
    vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
    vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
    return View(vm);
 }
[HttpPost]
public ActionResult SaveTestContact(TestContact model)
{
    return Content("success");
}

View/JavaScript with Ajax POST:

@using System.Web.Script.Serialization
@model TestContact
<button type="button" onclick="SaveTestContact();">Click Here to Post</button>
<script type="text/javascript">
    $(document).ready(function() {
         globalTestModel = @Html.Raw(new JavaScriptSerializer().Serialize(Model));
    });
    function SaveTestContact() {
        // passing additional studio & studio executive parameter to controller because it was not mapping correctly to the server side viewmodel without it
        $.ajax({
            type: "POST",
            url: "/Test/SaveTestContact",
            data: JSON.stringify(globalTestModel),
            dataType: "json",
            contentType: "application/json; charset=utf-8",
            success: function (msg) {
            }
        });
    }
</script>

在这个例子中,我们用至少一个Studio和一个StudioExecutive填充我们的对象,并渲染视图。当单击视图中的按钮时,我们将相同的对象POST到控制器方法,但ViewModel没有正确绑定(Studio属性设置为null)。

*不幸的是,我无法发布图像,我有一个对象的屏幕截图,显示Studio计数为0,StudioExecutive计数为1

我们确实在POST之前放置了一个断点,以确保JavaScript上的序列化是正确的,并且对象被填充。

我们已经得出结论,这与两个属性的命名约定有关,一个属性是另一个属性的子字符串。任何一个遇到过同样问题的人,都可以为我们指出正确的方向。

ViewModel中类似的属性名没有绑定到控制器动作方法中

我知道这不是这个问题的"完整答案"但我觉得这会给这个问题提供更多的见解,(我希望)最终会导致"完整的解决方案"。

假设我们在StudioStudioExecutive字段中各有一个项目,如OP示例所示,并在js中执行以下操作:

globalTestModel = '@Html.Raw(new JavaScriptSerializer().Serialize(Model))';
$.ajax({
    type: "POST",
    url: "/Test/SaveTestContact",
    data: JSON.stringify(globalTestModel),
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function (msg) {
    }
});

控制器方法将只接收前面提到的StudioExecutive。现在,如果我们做同样的事情,并在js中构建对象,如下所示,我们得到了相同的结果

o = {
    Studio: [{
        Id: 1,
        Name: 'Studio Contact ID=1',
    }],
    StudioExecutive: [{
        Id: 2,
        Name: 'Studio Exec Contact ID=2',
    }]            
};
$.ajax({
    type: "POST",
    url: "/Test/SaveTestContact",
    data: JSON.stringify(o),
    dataType: "json",
    contentType: "application/json; charset=utf-8",
    success: function (msg) {
    }
});

现在有趣的部分来了。如果我们将另一项添加到Studio字段并以相同的方式发布,我们将得到相同的结果

// Do this in the controller and do the same ajax post
public ActionResult TestContactView()
{
    var vm = new TestContact();
    vm.Studio.Add(new TestContactItem(){Id=1, Name = "Studio Contact ID=1"});
    vm.Studio.Add(new TestContactItem(){Id=3, Name = "Studio Contact ID=3"});
    vm.StudioExecutive.Add(new TestContactItem() { Id = 2, Name = "Studio Exec Contact ID=2" });
    return View(vm);
 }

但是,如果我们将在js中构建对象,如下所示,控制器将接收Studio(包含2个项目)和StudioExecutive(包含一个项目)。

o = {
    Studio: [{
        Id: 1,
        Name: 'Studio Contact ID=1',
    },
    {
        Id: 2,
        Name: 'Studio Contact ID=2',
    }],
    StudioExecutive: [{
        Id: 2,
        Name: 'Studio Exec Contact ID=2',
    }]            
};
// then do the ajax post

我们学到了什么

这告诉我们两件事:

  1. 默认模型绑定器有问题,因为它不能绑定两个或多个以相同名称开头的字段
  2. JSON.stringify也可以NOT这样做,如果只有一个项目,但它可以这样做,如果在字段中有多个项目具有最短的名称(例如Studio)