MVC视图ViewModel HttpPost返回值总是NULL

7

我通过表单HttpPost将ViewModel从视图传回控制器。然而,返回的值总是NULL。

ViewModel

public class vmCompanyAddress
{
    public StatelyTechAdmin.Models.Company Company { get; set; }
    public StatelyTechAdmin.Models.CompanyAddress Address { get; set; }

    public SelectList Counties { get; set; }
}

公司级模型

public class Company
{
    [Key]
    public virtual long CompanyId { get; set; }

    [Required]
    [Display(Name = "Company Name")]
    public virtual string Name { get; set; }

    public virtual DateTime CreatedDate { get; set; }

    public virtual IEnumerable<CompanyAddress> CompanyAddresses { get; set; }
}

公司地址类模型

public class CompanyAddress
{
    [Key]
    public virtual long CompanyAddressId { get; set; }

    [Required]
    public virtual long CompanyId { get; set; }

    [ForeignKey("CompanyId")]
    public virtual Company Company { get; set; }

    [Required]
    public virtual int CopmanyAddressTypeId { get; set; }

    [ForeignKey("CopmanyAddressTypeId")]
    public virtual CompanyAddressType CompanyAddressType { get; set; }

    [Display(Name = "Address 1")]
    public virtual string Address1 { get; set; }

    [Display(Name = "Address 2")]
    public virtual string Address2 {get; set; }

    [Display(Name = "Town")]
    public virtual string Town { get; set; }

    [Display(Name = "City")]
    public virtual string City { get; set; }

    [Required]
    public virtual long CountyId { get; set; }

    [ForeignKey("CountyId")]
    [Display(Name = "County")]
    public virtual County County { get; set; }

    [Required]
    [Display(Name = "Postal Code")]
    public virtual string PostalCode { get; set; }

    public virtual DateTime CreatedDate { get; set; }
}

控制器(get):

// GET: /Company/Create
    public ActionResult Create()
    {
        vmCompanyAddress vm = new vmCompanyAddress();
        vm.Counties = new SelectList(db.County, "CountyId", "Name", -1);
        //vm.Address = new CompanyAddress();
        //vm.Company = new Company();

        return View(vm);
    }

控制器(POST):


[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(vmCompanyAddress company)
    {
        if (ModelState.IsValid)
        {
            db.Companies.Add(company.Company);

            //Amend Address Company & Address Type before save to DB
            company.Address.CompanyId = company.Company.CompanyId;
            company.Address.CopmanyAddressTypeId = 1;

            db.CompanyAddress.Add(company.Address);

            db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(company);
    }

查看(创建)

    @model StatelyTechAdmin.ViewModels.vmCompanyAddress

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Company</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Company.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Company.Name)
            @Html.ValidationMessageFor(model => model.Company.Name)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Company.CreatedDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Company.CreatedDate)
            @Html.ValidationMessageFor(model => model.Company.CreatedDate)
        </div>


        @* Invoice Address *@
        <div class="editor-label">
            @Html.LabelFor(model => model.Address.Address1)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.Address1)
            @Html.ValidationMessageFor(model => model.Address.Address1)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.Address2)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.Address2)
            @Html.ValidationMessageFor(model => model.Address.Address2)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.Town)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.Town)
            @Html.ValidationMessageFor(model => model.Address.Town)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.City)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.City)
            @Html.ValidationMessageFor(model => model.Address.City)
        </div>

        @*<div class="editor-label">
            @Html.LabelFor(model => model.Address.County)
        </div>
        <div class="editor-field">
            @Html.DropDownListFor(model => model.Address.CountyId, Model.Counties)
        </div>*@

        <div class="editor-label">
            @Html.LabelFor(model => model.Address.PostalCode)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Address.PostalCode)
            @Html.ValidationMessageFor(model => model.Address.PostalCode)
        </div>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

请问有人能提供建议,为什么我的返回ViewModel值是NULL,即使所有字段都填写了?
我使用Google Chrome浏览器的"Network Record"功能进行检查,所有数值都以JSON格式发送。
非常感谢。
------------编辑-------------
以下是我从Google Chrome Network Monitor中看到的部分内容:
Company.Name:ABC123 Company.CreatedDate:2014/05/13 00:00:00 ....
所以它绝对是被返回的。

有趣的是,有人遇到了与“Company.Name”回传相似的问题。为了好玩,尝试移除该字段并查看是否仍然回传所有空值。http://stackoverflow.com/questions/780026/asp-net-mvc-model-binding-returning-null-values - Erik Elkins
1
CompanyAddressTypeId 拼写错误。此外,尝试像这样定义表单 - Html.BeginForm("你的控制器名称在这里", "创建", FormMethod.Post)。 - JB06
谢谢@ErikElkins,但是从我的视图中删除Company.Name并没有产生任何影响。所有的内容仍然以NULL形式返回。 - RobHurd
@Josh 我尝试修改了BeginForm(),但是没有成功。感谢您的建议并指出拼写错误。 - RobHurd
@mxmissile 是的,在调试模式下触发了该操作,但是vmCompanyAddress.Company=NULL和vmCompanyAddress.Address=NULL。 VS2013根据Company模型自动创建了控制器和视图,而不是我的ViewModel vmCompanyAddress。可能在某个地方它期望一个Company模型而不是vmCompanyAddress模型?我修改了我的Create HTTPPost方法,使其接受vmCompanyAddress对象而不是它生成的Company模型。 - RobHurd
显示剩余3条评论
4个回答

15

我能够重现您的问题,但感到困惑,因为我知道默认的MVC模型绑定器可以理解复杂类型。我剥离了大部分代码,只尝试使用Company对象来实现,但仍然失败了。然后我注意到,在vmCompanyAddress中,类名也是属性名:

public class vmCompanyAddress
{
     public StatelyTechAdmin.Models.Company Company { get; set; }

我将属性的名称更改为与类名不同的内容,然后它开始工作:

public class vmCompanyAddress
{
     public StatelyTechAdmin.Models.Company TheCompany { get; set; }

4
非常准确!非常感谢您对此事的关注。我真的很感激。我一定不会再犯同样的错误了。 - RobHurd
3
非常欢迎。尽管编译器允许这样做,但我不是将属性命名为与定义它们的类名相同的人。我认为类名是保留字,因此在引用类和类的实例时不应该有混淆。 - Matthew at Critical Cognition

10
我们今天遇到了同样的问题。这个问题中的被接受回答只是实际问题的一个临时解决方法。
在表单模型中,类名和属性名可以相同,模型绑定器没有限制。限制在于您控制器中操作的参数。您不能将参数命名为表单模型中具有复杂类型的属性名称。因为绑定器将尝试将HTTP POST表单值的 "company" 绑定到您控制器中的此参数。这对您将不起作用,因为绑定器会尝试将“公司”类型的值绑定到“CompanyAddress”类型中。
要解决您的问题,您只需将参数 "company" 重命名为 "companyAddressModel" - 或任何不是模型类中属性的名称。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CompanyAddress company)

改为:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(CompanyAddress companyAddressModel)

更多有关模型绑定的信息,请参见:http://aspnetmvc.readthedocs.org/projects/mvc/en/latest/models/model-binding.html

MVC将尝试通过名称将请求数据绑定到操作参数。 MVC将使用参数名称和其公共可设置属性的名称查找每个参数的值。 [...]除了路由值之外, MVC还会按照一定的顺序从请求的各个部分中绑定数据。下面是模型绑定按顺序查看这些数据来源的列表:

  • 表单值:这是使用POST方法在HTTP请求中传递的表单值。
  • 路由值:由路由提供的路由值集合。
  • 查询字符串:URI的查询字符串部分。

ASP.NET WebAPI文档中使用相同技术的一个很好的例子:http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

HttpResponseMessage Put(int id, Product item) { ... }

这里将ProductId属性映射到控制器中的id参数。这会起作用,因为在操作中与模型类中使用相同的原始数据类型。


2

请确保您的ViewModel暴露属性而不仅仅是字段。

以下代码是正确的:

public DAL.Models.Account acct {get;set;}

这个不会:

public DAL.Models.Account acct;

2

感谢您的输入。@MatthewAtCriticalCogni一语中的,我的ViewModel属性与我的类名相同,因此导致它失败。 - RobHurd

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