I'm working on an internet lesson plans application for a project. The lesson plan is built from following models (generated with Entity Framework in Database First approach):
public partial class Subject
{
public int Id { get; set; }
public string Hour { get; set; }
public string Name { get; set; }
public int DayId { get; set; }
[Required]
public int PlanId { get; set; }
public virtual Day Day { get; set; }
public virtual Plan Plan { get; set; }
}
public partial class Plan
{
public Plan()
{
this.Przedmiot = new HashSet<Subjects>();
}
public int Id { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Subject> Subject { get; set; }
}
I have no problems with displaying both models in one View, but I can't seem to figure out how to post both models to database when creating new plan. I want my View to look something like this:
So my question is what's the best approach here, and how can I create one record in Plan table in database, and many Subject records linked to it in this one view.
Edit: Code with my display View as requested (omitted unnecessary parts because it's rather long):
@model IEnumerable<Lesson_plan.DAL.Subject>
<table style="border: 1px solid black; margin: 40px; width: 100%;">
<tr>
<th>Hours</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
<th>Sunday</th>
</tr>
@{
if (Model != null)
{
var r = 1;
var t = 1;
List<string> hours = new List<string>();
foreach (var subject in Model)
{
if (!hours.Contains(subject.Hour))
{
<tr>
<td>
<textarea>
@Html.DisplayFor(modelItem => subjest.Hour)
@{ hours.Add(subject.Hour); }
</textarea>
</td>
<td>
<textarea>
@foreach (var subjectName in Model)
{
if (subjectName.Day.DayOfTheWeek.Equals("Monday") &&
subject.Hour.Equals(subjetName.Hour))
{
@Html.DisplayFor(modelItem => subject.Name)
}
}
</textarea>
</td>
//and so on for every day
}
</tr>
r++;
}
}
}
}
</table>
Code of my Controller class (I did some experiments with Create method, but I'm posting original method here):
namespace Lesson_plan.Controllers
{
public class PlansController : Controller
{
private readonly LessonPlanEntities db = new LessonPlanEntities();
// GET: Plans
public ActionResult Index()
{
var plans = db.Plan.ToList();
return View(plans);
}
// GET: Plans/Details/5
public ActionResult Details(int? id)
{
if (id == null)
return Create();
var subjects = db.Subject.
Where(x => x.PlanId == id).
OrderByDescending(x => x.Hour).ToList();
if (subjects.Count > 0)
ViewBag.Title = subjects[0].Plan.Name;
return View(subjects);
}
// GET: Plans/Create
public ActionResult Create()
{
return View();
}
[HttpPost]
public ActionResult Create(Plan plan)
{
if (ModelState.IsValid)
{
db.Plan.Add(plan);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(plan);
}
// GET: Plans/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var plan = db.Plan.Find(id);
if (plan == null)
return HttpNotFound();
return View(plan);
}
// POST: Plans/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Id,Nazwa")] Plan plan)
{
if (ModelState.IsValid)
{
db.Entry(plan).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(plan);
}
// GET: Plans/Delete/5
public ActionResult Delete(int? id)
{
if (id == null)
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
var plan = db.Plan.Find(id);
if (plan == null)
return HttpNotFound();
return View(plan);
}
// POST: Plans/Delete/5
[HttpPost]
[ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
var plan = db.Plan.Find(id);
db.Plan.Remove(plan);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
if (disposing)
db.Dispose();
base.Dispose(disposing);
}
}
}
Edit2 Code for the Create View with form:
@model Lesson_plan.DAL.Plan
@using (Html.BeginForm("Create", "Plans"))
{
<div>
@Html.LabelFor(plan => plan.Name)<br/>
@Html.TextAreaFor(plan => plan.Name)
</div>
<table style="border: 1px solid black; margin: 40px; width: 100%;">
<tr>
<th>Hours</th>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
<th>Sunday</th>
</tr>
@{
for (int i = 0; i < 7; i++)
{
<tr>
//what sould i put here ?
<td><textarea></textarea></td>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
<th><textarea></textarea></th>
</tr>
}
}
</table>
<p>
<a type="button" class="btn btn-info" [email protected]("Index")>Back</a>
<input type="submit" value="Create"/>
</p>
}
The best approach is to design a ViewModel class that can hold all the data on your Form that needs to be submitted, for both Plan(s) and/or Subject(s).
In the Action Method that receives this ViewModel you can pick apart the data, and based on that do whatever inserts and/or updates are needed to store it in the database.
Only the Controller should have knowledge of your database classes. All mapping between ViewModel and Database objects should be done by the controller, and the View should not be bothered or hampered by this.