MVC View ViewModel HttpPost返回值始终为NULL

时间:2014-05-13 19:21:30

标签: asp.net-mvc asp.net-mvc-4 razor asp.net-mvc-viewmodel

我通过表单HttpPost将ViewModel从我的View传回Controller。但是,返回的值始终为NULL。

视图模型

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: /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);
    }

控制器(帖子):

[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浏览器中进行了检查,并且所有值都以JSON格式发回。

非常感谢。

------------编辑---------------

以下是我可以从Google Chrome网络监视器中看到的内容

Company.Name:ABC123 Company.CreatedDate:2014/05/13 00:00:00 ....

所以它肯定会被退回。

4 个答案:

答案 0 :(得分:11)

我能够重现你的问题而感到困惑,因为我知道默认的MVC Model Binder理解复杂的类型。我剥离了大部分代码,只是试图用公司对象来做,但仍然失败了。然后我注意到在vmCompanyAddress中,类的名称也是属性的名称:

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

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

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

答案 1 :(得分:10)

我们今天遇到了同样的问题。这个问题中接受的答案只是实际问题的一个肮脏的解决方法。

表单模型中的ClassName和PropertyName可以相同,模型绑定器中没有限制。限制是控制器中操作的参数。您不能将参数命名为表单模型中具有复杂类型的属性。导致绑定程序尝试将HTTP POST表单值company绑定到控制器中的此参数。它不适合您,因为活页夹尝试将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 documentation的一个很好的例子,它使用相同的技术:

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

此处Id的{​​{1}}属性映射到控制器中的Product参数。这将起作用,导致在操作中使用与模型类中相同的原始数据类型。

答案 2 :(得分:2)

我自己没有尝试过,但很久以前有很多类似的问题我用自定义的ModelBinder解决了:我不推荐使用。

我猜你的数据看起来不像:{公司:{...},地址:{...}}?

我认为解决方案是让MVC使用模板和EditorFor()来理解数据的结构。请参阅http://lostechies.com/jimmybogard/2011/09/07/building-forms-for-deep-view-model-graphs-in-asp-net-mvc/以获得一个好例子!

答案 3 :(得分:2)

确保您的ViewModel公开属性而不仅仅是字段。

这有效:

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

这不是:

public DAL.Models.Account acct;
相关问题