I am new to C# and MVC and am coming into the Visual Studio environment from a PHP background. I'm groping around a little (lot), but getting there. I need help in understanding some essential concepts, and undoubtedly this is a very hamfisted effort, but helping me solve this will help me understand how things work.
I have a SQL db, hooked up to a VS2012 web project (C#) by adding an ADO.NET Entity Class Model. Suffice to say that that end is working no problem, and the automatically generated default CRUD views work no problem also.
Use case:
I wanted a webpage where the user would be presented with a list of items drawn from the database. This was already achieved in the default Index view, but I also wanted that list to be editable then and there without having to enter an Edit view for each individual item. Therefore I changed the list HTML to a form, and set up an ActionName
for an 'Update' submission. This also works no problem:
MODEL:
namespace Library3.DAL {
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class PrmTbl_Level {
public PrmTbl_Level() {
this.Tbl_Books = new HashSet<Tbl_Book>();
}
[Required]
[Display(Name = "ID")]
public int LevelID { get; set; }
[Required]
[Display(Name = "Description")]
public string LevelDesc { get; set; }
[Required]
[Display(Name = "Entered By")]
public string EnteredBy { get; set; }
public Boolean Active { get; set; }
public virtual ICollection<Tbl_Book> Tbl_Books { get; set; }
}
}
VIEW:
@model IEnumerable<Library3.DAL.PrmTbl_Level>
@using (Html.BeginForm("Update", "ReaderLevel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<table>
<thead>
<tr>
<th> @Html.DisplayNameFor(model => model.LevelID) </th>
<th> @Html.DisplayNameFor(model => model.LevelDesc) </th>
<th> @Html.DisplayNameFor(model => model.EnteredBy) </th>
<th> @Html.DisplayNameFor(model => model.Active) </th>
</tr>
</thead>
@foreach (var item in Model) {
var thisID = item.LevelID;
var thisDesc = item.LevelDesc;
var thisEnteredBy = item.EnteredBy;
var thisActive = item.Active;
<tr>
<td class="tdsm centered fade"> @thisID </td>
<td> <input type="text" name="@(thisID + ".desc")" value="@thisDesc" /> </td>
<td class="tdmed centered fade"> @thisEnteredBy </td>
<td class="tdsm centered">
@{
if(@thisActive) {
<text><input type="checkbox" name="@(thisID + ".active")" value="true" checked="checked" /></text>
}
else {
<text><input type="checkbox" name="@(thisID + ".active")" value="true" /></text>
}
}
</td>
</tr>
}all of this
</table>
<p>
<input type="submit" value="Update" />
</p>
</fieldset>
}
CONTROLLER:
namespace Library3.Controllers {
public class ReaderLevelController : Controller {
private NSSchoolLibraryEntities db = new NSSchoolLibraryEntities();
public ActionResult Index() {
ViewBag.Title = "Reader Levels";
return View(db.PrmTbl_Levels.ToList());
}
[HttpPost, ActionName("Update")]
[ValidateAntiForgeryToken]
public ActionResult Update(FormCollection MyArray) {
(... Process UPDATE submission ...)
db.SaveChanges();
}
}
return RedirectToAction("Index");
}
}
}
Now I want to put an empty field at the top of my list, whereby new entries may be made. This basically means the one view file will now be catering for both Create and Update functions. I inserted the following HTML:
@using (Html.BeginForm("New", "ReaderLevel", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<table>
<tr>
<td class="tdsm centered fade">
@Html.EditorFor(model => model.LevelID)
@Html.ValidationMessageFor(model => model.LevelID)</td>
<td><input type="text" name="desc" />
@Html.EditorFor(model => model.LevelDesc)
@Html.ValidationMessageFor(model => model.LevelDesc)</td>
<td class="tdmed centered fade">
@Html.EditorFor(model => model.EnteredBy)
@Html.ValidationMessageFor(model => model.EnteredBy)</td>
<td class="tdsm centered">
@Html.EditorFor(model => model.Active)
@Html.ValidationMessageFor(model => model.Active)</td>
</tr>
</table>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
...the problem being the the model being referenced here should be for a NEW intance of the PrmTbl_Level class - not using the existing IEnumberable to spit out existing data from the table (at least thats what I THINK the problem is!).
The default seperate Create view references the namespace via @model
(sans IEnumberable), and the data is returned as a single object (public ActionResult Create(PrmTbl_Level prmtbl_level)
) which is then saved effortlessly to the db (db.PrmTbl_Levels.Add(prmtbl_level);
) - how can I achieve the same using the one @model
statement? (ELI5). All help - both practically and on the concepts involved - greatly appreciated!