将集合数据从局部视图传递到控制器

时间:2017-05-24 20:05:47

标签: entity-framework model-view-controller partial-views icollection

我需要在主窗体提交时将动态创建的数据从局部视图传递给控制器​​。

这里是返回局部视图的Action:

[HttpGet]
public virtual PartialViewResult AddItem()
{
    var item = new QuotedItem();
    ViewBag.StockID = new SelectList(db.StockItems.OrderBy(s => s.Name), "StockID", "Name");
    return PartialView("EditorTemplates/QuotedItem", item);
}

这里是QuotedItem的EditorTemplate:

@model StockSystem.Models.QuotedItem

<div id="itemRow">

    <span>
        @Html.DropDownListFor(model => model.StockID, null)
        @Html.ValidationMessageFor(model => model.StockID, "", new { @class = "text-danger" })
    </span>
    <span>
        @Html.EditorFor(model => model.Price)
        @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
    </span>
    <span>
        @Html.EditorFor(model => model.Quantity)
        @Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
    </span>

    <a href="#" class="deleteSmall"></a>

</div>

这是观点:

@model StockSystem.Models.Quote

@{
    ViewBag.Title = "Create";
}

<h2>New Quote for @ViewBag.Customer</h2>

<hr />
@using (Html.BeginForm()) 
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    @Html.Hidden("CustomerID", (object)ViewBag.CustomerID)

    <div class="addItem">
        @Ajax.ActionLink(" ", "AddItem",
        new AjaxOptions
        {
            UpdateTargetId = "editorRows",
            InsertionMode = InsertionMode.InsertAfter,
            HttpMethod = "GET"
        })
    </div>

    <div id="editorRows">
        @Html.EditorFor(q => q.QuotedItems)
    </div>

    <p></p>

    <div>
        <input class="add" type="submit" value="" />
        <a class="back" href="@Url.Action("Index", "Quotes", new { id = ViewBag.CustomerID })"></a>
    </div>
}

这里是创建行动:

 public ActionResult Create(int id)
 {
     var customer = db.Customers.Find(id);
     ViewBag.CustomerID = id;
     ViewBag.Customer = customer.CustomerName;
     var quote = new Quote() { QuotedItems = new List<QuotedItem>() };
     return View(quote);
 }

这是QuotedItem的EF模型:

public partial class QuotedItem
{
    public int QuotedItemID { get; set; }
    public int QuoteID { get; set; }
    public int StockID { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }

    public virtual Quote Quote { get; set; }
    public virtual StockItem StockItem { get; set; }
}

并引用:

public partial class Quote
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Quote()
    {
        this.QuotedItems = new HashSet<QuotedItem>();
    }

    public int QuoteID { get; set; }
    public int CustomerID { get; set; }
    public int UserID { get; set; }
    public System.DateTime QuoteDate { get; set; }
    public string QuoteRef { get; set; }

    public virtual Customer Customer { get; set; }
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<QuotedItem> QuotedItems { get; set; }
    public virtual User User { get; set; }
}

我可以向页面添加项目,但不会将它们添加到提交的报价中。

 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Create([Bind(Include = "QuoteID,CustomerID,UserID,QuoteDate,QuoteRef,QuotedItems")] Quote quote) //, List<QuotedItem> items
 {
     if (ModelState.IsValid)
     {
         //quote.QuotedItems is empty, how do I bind this data and save to database?         
         db.Quotes.Add(quote);
         db.SaveChanges();
         return RedirectToAction("Index", new { id = quote.CustomerID });
     }

     return View(quote);
 }

集合未发送到控制器。这是怎么做到的?

由于

编辑:我正在考虑尝试这个:

 [HttpPost]
 [ValidateAntiForgeryToken]
 public ActionResult Create([Bind(Include = "QuoteID,CustomerID,UserID,QuoteDate,QuoteRef,QuotedItems")] Quote quote, List<QuotedItem> items)
 {
     if (ModelState.IsValid)
     {
         quote.QuotedItems = items;
         db.Quotes.Add(quote);
         db.SaveChanges();
         return RedirectToAction("Index", new { id = quote.CustomerID });
     }

     return View(quote);
 }

但我认为必须有更好的方法。如果我尝试这种方法,我仍然不完全确定如何将其发送到控制器,我假设作为Html.BeginForm()中的参数?我对MVC很新,并且仍然掌握着惯例。

1 个答案:

答案 0 :(得分:0)

通过在方法签名中使用Bind属性,可以阻止QuotedItems属性发布到控制器操作。

public ActionResult Create([Bind(Include = "QuoteID,CustomerID,UserID,QuoteDate,QuoteRef")] Quote quote)

QuotedItems属性名称添加到包含列表中应解决此问题:

public ActionResult Create([Bind(Include = "QuoteID,CustomerID,UserID,QuoteDate,QuoteRef,QuotedItems")] Quote quote)

修改

如果没有看到您的QuotedItem编辑器模板,很难确定,但听起来您的问题可能是添加到列表中的每个项目都没有在元素上设置唯一ID弥补那个项目。如果没有这个,模型绑定器将很难确定哪个元素属于集合中的哪个对象。

看看这两篇文章,看看它们是否有帮助:

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/

https://www.mattlunn.me.uk/blog/2014/08/how-to-dynamically-via-ajax-add-new-items-to-a-bound-list-model-in-asp-mvc-net/

修改2

模板中的每个元素都应该有一个唯一的ID,索引是一个很好的方法,项目GUID也是如此(如果你有的话);但只要每个元素ID对该项唯一,它就应该有效。

@model StockSystem.Models.QuotedItem

<div id="itemRow">
<span>
    @Html.DropDownListFor(model => model.StockID, null)
    @Html.ValidationMessageFor(model => model.StockID, "", new { @class = "text-danger" })
</span>
<span>
    @Html.EditorFor(model => model.Price, new { htmlAttributes = new { id = $"[{YOURINDEX}].Price" } })
    @Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</span>
<span>
    @Html.EditorFor(model => model.Quantity, new { htmlAttributes = new { id = $"[{YOURINDEX}].Quantity" } })
    @Html.ValidationMessageFor(model => model.Quantity, "", new { @class = "text-danger" })
</span>

<a href="#" class="deleteSmall"></a>

在上面的代码中,对于添加到集合中的每个新项目,[YOURINDEX]值都需要递增。

如何实现和增加该索引取决于您,但我建议您阅读我之前链接的两篇文章,以了解为什么这是必要的。