HttpPost编辑操作中的参数为null

时间:2013-10-13 18:41:13

标签: c# asp.net-mvc asp.net-mvc-4 razor

单击“保存”以提交表单时,我的HTTPPost编辑操作会收到IEnumerable<MedicalProduct> productList的空值。我不确定我的编辑Action使用什么参数来防止空值。我会尝试用一些断点确定原因但在参数被赋值为null之前找不到该位置。

旁注:

我正在调整我的代码以使用这个新的Model / ViewModel层次结构。我的Controller尚未使用这些新模型进行完全测试,但我开始测试编辑操作,并在尝试在编辑后期操作中使用参数IEnumerable<MedicalProduct> productList时收到空引用异常。

另一方注意:

我在ViewModel MedicalProductViewModelLineItem中使用了一个子ViewModel类MedicalProductViewModel(尚未找到更好的名称)因为我需要一种从数据库中检索所有品牌名称的方法一个数据库调用,然后逐个分配给MedicalProductViewModelLineItem

编辑:CODE UPDATE 10/22/13 5:14 pm CST 。现在修复了HttpPost FormCollection.Keys操作方法中Edit参数中生成的值。现在,像“Products [0] .Name”或“Products [0] .ID”这样的值是在FormCollection.Keys参数而不是“p.Name”或“Name”中生成的。 但是 productList参数仍为null


模型类


MedicalProductViewModel

public class MedicalProductViewModel
{
    public List<MedicalProductViewModelLineItem> Products { get; private set; }

    //public SelectListItem BrandSelectListItem { get; private set; }

    public void BuildViewModel(IEnumerable<MedicalProductViewModelLineItem> productsList, IEnumerable<Brand> brandList)
    {
        BuildProducts(productsList, brandList);
    }

    public void BuildViewModel(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
    {
        BuildProducts(productsList, brandList);
    }
    private IEnumerable<SelectListItem> BuildSelectListItems(IEnumerable<Brand> brandList)
    {              
        return brandList.Select(b => new SelectListItem()
        {
            Text = b.Name,
            Value = b.ID.ToString()
        });
    }

    private void BuildProducts(IEnumerable<MedicalProductViewModelLineItem> productList, IEnumerable<Brand> brandList)
    {
        var medicalProducts = productList.Select(p => new MedicalProduct()
        {
            BrandID = p.BrandID,
            ID = p.ID,
            Name = p.Name,
            Price = p.Price
        });

        BuildProducts(medicalProducts, brandList);
    }

    private void BuildProducts(IEnumerable<MedicalProduct> productsList, IEnumerable<Brand> brandList)
    {
        Products = productsList.Select(p => new MedicalProductViewModelLineItem()
        {
            BrandID = p.BrandID,
            BrandName = brandList.Single(b => b.ID == p.BrandID).Name,
            BrandSelectListItem = BuildSelectListItems(brandList),
            ID = p.ID,
            Name = p.Name,
            Price = p.Price
        }).ToList();
    }
}

MedicalProductViewModelLineItem

// Sub-ViewModel of MedicalProductViewModel
// It gets displayed as one row on a view.
public class MedicalProductViewModelLineItem 
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    // is a foreign key
    public int BrandID { get; set; }

    public string BrandName { get; set; }
}

MedicalProduct

// DOMAIN MODEL
public class MedicalProduct 
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    // is a foreign key
    public int BrandID { get; set; }
}

控制器


MedicalProductController

public class MedicalProductController : Controller
{
    private MvcMedicalStoreDb _db = new MvcMedicalStoreDb()

    //
    // GET: /MedicalSupply/Edit/5

    public ActionResult Edit(int id = 0)
    {
        MedicalProduct product = _db.Products.Find(id);
        if (product == null)
        {
            return HttpNotFound();
        }
        var productList = new List<MedicalProduct> { product }; 
        var viewModel = GetMedicalProductViewModel(productList);
        return View(viewModel);
    }

    // ==========================================
    // NULL REFERENCE EXCEPTION OCCURS IN THIS ACTION
    // ==========================================
    // POST: /MedicalSupply/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(IEnumerable<MedicalProductViewModelLineItem> productList, FormCollection values)
    {
        if (ModelState.IsValid)
        {
            foreach (var product in productList)
                _db.Entry(product).State = EntityState.Modified;

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

        var productViewModelList = GetMedicalProductViewModel(productList);

        return View(productViewModelList);
    }

    protected override void Dispose(bool disposing)
    {
        _db.Dispose();
        base.Dispose(disposing);
    }
}

浏览


Edit.cshtml

@model MvcMedicalStore.Models.MedicalProductViewModel

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

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

    <fieldset>
        <legend>MedicalProduct</legend>
        @for (int i = 0; i < Model.Products.Count(); i++)
        {
            @Html.EditorFor(m => m.Products[i])        
        }
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

EditorTemplates \ MedicalProductViewModelLineItem.cshtml

@model MvcMedicalStore.Models.MedicalProductViewModelLineItem


@Html.HiddenFor(item => Model.ID)

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

<div class="editor-label">
    @Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
    @Html.EditorFor(item => Model.Price)
    @Html.ValidationMessageFor(item => Model.Price)
</div>

<div class="editor-label">
    @Html.LabelFor(item => Model.BrandID)
</div>
<div class="editor-field">
    @Html.DropDownListFor(item => Model.BrandID, Model.BrandSelectListItem)
    @Html.ValidationMessageFor(item => Model.BrandID)
</div>

编辑:(图片已过时)

MedicalProductViewModel.cshtml中使用的foreach方法和for方法的两张图片,以及FormsCollection参数values的结果键值

Using the for approach Using the foreach approach

2 个答案:

答案 0 :(得分:2)

在您的控制器操作中使用BindAttribute Prefix,如下所示:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Prefix = "Products")] IEnumerable<MedicalProductViewModelLineItem> productList)
{
    if (ModelState.IsValid)
    {
        foreach (var product in productList)
            _db.Entry(product).State = EntityState.Modified;

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

    var productViewModelList = GetMedicalProductViewModel(productList);

    return View(productViewModelList);
}

答案 1 :(得分:0)

将您的视图从foreach循环更改为for循环

@foreach (MvcMedicalStore.Models.MedicalProductViewModelLineItem p in Model.Products)
{
    @Html.HiddenFor(item => p.ID)
   //rest of the code.
}

to 

@for ( i = 0;  i < Model.count(); i++)
{
  @Html.LabelFor(m => m[i].Name)
  @Html.HiddenFor(m => m[i].Name)
  @Html.LabelFor(m => m[i].Price)
  @Html.EditorFor(m => m[i].Price)
   //rest of the code.
}

[HttpPost]
 public MedicalProductViewModel GetMedicalProductViewModel(ICollection<MedicalProduct> productList)
    {
        var brandList = _db.Brands.ToArray();

        var mapper = new MedicalProductMapper();

        return mapper.MapMedicalProductViewModel(productList, brandList);            
    }

使其更明确,指定

  

FormMethod.Post

@using (Html.BeginForm()) { }

@using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { id = "forId" }))
    {}