Alright, it's going to be a long one. To allow this to happen in "one page" (through POST, or you could use Ajax, technically), you need a combination of a Get and Post version of the method and to construct your view model correctly. Below are the classes that I will use for demonstration purposes:
public class NewCourse
{
[Required]
public string Name { get; set; }
// And your other properties
public int[] LecturerIds { get; set; }
}
public class ViewLecturer
{
public int Id { get; set; }
public int Name { get; set; }
}
public class NewCourseViewModel
{
public NewCourse Course { get; set; }
public IEnumerable<ViewLecturer> Lecturers { get; set; }
}
NewCourseViewModel
will be the model for the View (see below). ViewLecturer
will give you a lighter mapping between your available Lecturer
and the information required to Add to them.
As for the Controller
:
public class CourseController : Controller, IDisposable
{
private Lazy<YourContext> lazyContext =
new Lazy<YourContext>(() => new YourContext());
private YourContext Context
{
get { return lazyContext.Value; }
}
public ActionResult New()
{
var model = new NewCourseViewModel {
Course = new NewCourse(),
Lecturers = Context.Lecturers
.Select(l => new ViewLecturer { Id = l.Id, Name = l.Name })
};
return View(model);
}
[HttpPost]
public ActionResult New(NewCourse course)
{
if(ModelState.IsValid)
{
var lecturers = course.Lecturers
.Select(l => new Lecturer { Id = l.Id })
.ToList();
foreach(var lecturer in lecturers)
Context.Lecturers.Attach(lecturer);
var newCourse = new Course {
Name = course.Name,
// ... and the rest of the mapping
Lecturer = lecturers
};
context.Courses.Add(newCourse);
context.SaveChanges();
// Could have to handle DbUpdateException if you want
return RedirectToAction(...);
}
return View(new NewCourseViewModel {
Course = course,
Lecturers = Context.Lecturers
.Select(l => new ViewLecturer { Id = l.Id, Name = l.Name })
});
}
public void Dispose()
{
if(lazyContext.IsValueCreated)
lazyContext.Value.Dispose();
}
}
Your first New
method will give you the entry point for your Course
creation page. The rest of the validation and actual adding will be done through the [HttpPost]
overload. As for your View
(that should be in the ~/Views/Course/New.cshtml
):
@model NewCourseViewModel
// ... Then when you are ready to begin the form
@using(Html.BeginForm("New", "Course", FormMethod.Post))
{
// Your List of Lecturers
@Html.ListBoxFor(m => m.Course.LecturerIds,
new MultiSelectList(
Model.Lecturers,
"Id",
"Name",
m.Course.LecturerIds ?? new int[0]
))
// Your Other Model binding
}
When the submit button will be pressed, the action matched will be the New(NewCourse course)
. The names are important because of the way the HtmlHelper
s generate their Ids. Because we are only included one property of the whole view model, it will match the parameter name course
based on the view model's Course
property. You will get a list of Id
s for the Lecturers which you will be able to use to attach to the DbContext
and add directly to the new Course
model (Entity Framework will do the rest). In cases where there was a problem, we can get back the list of lecturers and re-use the same NewCourse
in the view model.
Now this is example is very basic but it should give you a good starting point as to how you can structure your view model.