Here is a sample of my models. There is a Supply Receipt that holds custom models for Block, Grower, and a collection of SupplyReceiptLine:
public class SupplyReceipt
{
[Key]
public int Id { get; set; }
[Required]
public string LoadNumber { get; set; }
[Required]
public DateTime Date { get; set; }
public string Notes { get; set; }
[Required]
public Block Block { get; set; }
[Required]
public Contact Grower { get; set; }
[Required]
public ICollection<SupplyReceiptLine> SupplyReceiptLines { get; set; }
}
And I don't think it's necessary for my question but here is an example for my Contact model (used for Grower):
public class Contact
{
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Note { get; set; }
[Display(Name="Billing Address")]
public virtual Address BillingAddress { get; set; }
[Display(Name = "Shipping Address")]
public virtual Address ShippingAddress { get; set; }
[Display(Name = "Show as Supplier")]
public bool IsSupplier { get; set; }
[Display(Name = "Show as Customer")]
public bool IsCustomer { get; set; }
[Display(Name = "Show as Grower")]
public bool IsGrower { get; set; }
[Display(Name = "Show as Bin Owner")]
public bool IsBinOwner { get; set; }
}
Now I'm having multiple problems:
- Regular MVC Scaffolding (Entity Framework Model) doesn't like Grower, Block, or Supply Receipt Lines. I understand Supply Receipt Lines needs logic behind it, but I don't understand why I had to work so hard to create a drop down select box for Grower and Block. These types are well defined (with Ids and Key attributes) and there is data in the database for these items.
Could anyone explain what I'm missing in my model above that MVC / EF Scaffolding doesn't understand? A lot of tutorials online show this automatically being done. I had to resort to manually placing an Html.DropDownListFor
in my view, passing it a new SelectList
built from my database context. I also had to update the controller post method to include an integer for Grower and integer for Block, and manually lookup the Grower & Block by id, assign them back to the model, before adding my Supply Receipt to the database.
- I couldn't figure this out. Supply Receipt Lines is an ICollection, it should hold multiple Supply Receipt Line. How do I update my create view, and my controller, so that I can add multiple supply receipt lines within this same form?
Here is an example screenshot to go along with number 2:
Here is some of the other code associated with this "create" method, including the editors. Thanks for your time.
(Controller Post Method)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(SupplyReceipt supplyReceipt, int Grower, int Block, ICollection<SupplyReceiptLine> supplyReceiptLines)
{
// supplyReceiptLines turns up null! So does supplyReceipt.SupplyReceiptLines
// Need Grower and Block For Some reason
supplyReceipt.Grower = db.Contacts.Where(model => model.Id.Equals(Grower)).First();
supplyReceipt.Block = db.Blocks.Where(model => model.Id.Equals(Block)).First();
if (ModelState.IsValid)
{
db.SupplyReceipts.Add(supplyReceipt);
if (supplyReceiptLines != null && supplyReceiptLines.Any())
{
db.SupplyReceiptLines.AddRange(supplyReceiptLines);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(supplyReceipt);
}
(Create Form)
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SupplyReceipt</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.LoadNumber, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.LoadNumber, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.LoadNumber, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Date, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Date, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Date, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Grower, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.Grower, new SelectList(Db.Contacts, "Id", "Title"), "Select a Grower", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Grower, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Block, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.Block, new SelectList(Db.Blocks, "Id", "Title"), "Select a Block", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Block, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Notes, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Notes, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Notes, "", new { @class = "text-danger" })
</div>
</div>
<hr />
<div id="supplyReceiptLines">
@Html.EditorFor(model => model.SupplyReceiptLines, "SupplyReceiptLines")
</div>
<div class="form-group text-right">
<a href="#" class="-action-add-supply-receipt-line btn btn-primary">Add a row</a>
</div>
<hr />
<div class="form-group">
<div class="text-right">
<button type="submit" value="Create" class="btn btn-default btn-success">Create Supply Receipt</button>
</div>
</div>
</div>
}
(Editor Template)
@model SupplyReceiptLine
@{
ModelContext Db = new ModelContext();
}
<div class="form-group form-inline">
@Html.HiddenFor(model => model.Id)
<div class="col-sm-1">
@Html.LabelFor(model => model.Qty, htmlAttributes: new { @class = "" })
@Html.EditorFor(model => model.Qty, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Qty, "", new { @class = "text-danger" })
</div>
<div class="col-sm-1">
@Html.LabelFor(model => model.Pack, htmlAttributes: new { @class = "" })
@Html.DropDownListFor(model => model.Pack, new SelectList(Db.Packs, "Id", "Title"), "", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Pack, "", new { @class = "text-danger" })
</div>
<div class="col-sm-1">
@Html.LabelFor(model => model.Size, htmlAttributes: new { @class = "" })
@Html.DropDownListFor(model => model.Size, new SelectList(Db.Sizes, "Id", "Title"), "", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Size, "", new { @class = "text-danger" })
</div>
<div class="col-sm-3">
@Html.LabelFor(model => model.Variety, htmlAttributes: new { @class = "" })
@Html.DropDownListFor(model => model.Variety, new SelectList(Db.Varieties, "Id", "Title"), "", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Variety, "", new { @class = "text-danger" })
</div>
<div class="col-sm-3">
@Html.LabelFor(model => model.Grade, htmlAttributes: new { @class = "" })
@Html.DropDownListFor(model => model.Grade, new SelectList(Db.Grades, "Id", "Title"), "", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.Grade, "", new { @class = "text-danger" })
</div>
<div class="col-sm-3">
@Html.LabelFor(model => model.BinOwner, htmlAttributes: new { @class = "" })
@Html.DropDownListFor(model => model.BinOwner, new SelectList(Db.Contacts, "Id", "Title"), "", new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.BinOwner, "", new { @class = "text-danger" })
</div>
</div>