Reputation: 5886
So I have this demo project almost completely working.
public class Project
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public virtual ICollection<Task> Tasks { get; set; }
}
public class Task
{
public int ID { get; set; }
[Required]
public string Name { get; set; }
public int ProjectID { get; set; }
public virtual Project Project { get; set; }
}
Controller
public ActionResult Edit(int id)
{
var project = db.Projects.Where(p=>p.ID==id).Single();
return View(project);
}
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
var dbProject = db.Projects.Where(p => p.ID == project.ID).Single();
UpdateModel(dbProject);
db.SaveChanges();
TempData["Success"] = "Modelo Valido";
}
return RedirectToAction("Index");
}
View//strongly typed for project
@using (Html.BeginForm())
{
@Html.ValidationSummary(true)
<fieldset>
<legend>Project</legend>
@Html.HiddenFor(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
<h1>Tasks</h1>
@Html.EditorFor(m => m.Tasks)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
EditorTemplate
@model MvcApplication2.Models.Task
<span>Task</span>
<br />
@Html.LabelFor(m => m.Name)
@Html.EditorFor(m => m.Name)
@Html.HiddenFor(m => m.ID)
@Html.HiddenFor(m => m.ProjectID)
@Html.ValidationMessageFor(m => m.Name)
The view displays this
The problem is that when I submit the form the Tasks are populated with everything except the virtual Project property... so the error i get it is
The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.
Here is a pic of my debugging breakpoint result
Please Help.
UPDATE:
I have changed my controller action to this
[HttpPost]
public ActionResult Edit(Project project)
{
if (ModelState.IsValid)
{
db.Entry(project).State = EntityState.Modified;
db.SaveChanges();
TempData["Success"] = "Modelo Valido";
return RedirectToAction("Index");
}
return View(project);
}
it is still not working correctly. Now changes made to the Name of the project are updated correctly in the database. but changes made to any Task Name are ignored completely.
Upvotes: 4
Views: 5640
Reputation: 5886
I have found a way to get this to work but Im not completely happy with the approach.
see this question on how to refactor my current code to see how I am currently (hopefully temporarily doing it)
Help improving (refactoring) my code. Automapper - EF - asp.net mvc-3
Upvotes: 1
Reputation: 17784
i believe @Html.EditorFor(m => m.Tasks)
is generating html like (approximately)
<label>Name</label>
<input type="text" name="Tasks[0].Name" id="auto-gen-id"/>
<input type="hidden" name="Tasks[0].ID" id = "auto-gen-id"/>
<input type="hidden" name="Tasks[0].ProjectID" id = "auto-gen-id"/>
<!--html for validation span-->
Above is the approximate html generated for first Task in Collection and similar html will be generated for each task in the collection. The only difference is that index will be incremented in name attributes of all inputs i.e Tasks[1].Name
, Tasks[1].ProjectID
etc. This portion will actually bind to the Collection<Task> Tasks
property of Project
but you can see that in detail portion you don't have any inputs like
<input type="whatever" name="Tasks[0].Project.ProjectID" .../>
<input type="whatever" name="Tasks[0].Project.Name" ..../>
Modelbinder needs input elements with proper naming conventions to bind values to all properties of action method parameters. For testing purpose you can inlude these two lines in your Editor template for Task
@Html.TextBoxFor(x=>x.Project.ID)
@Html.TextBoxFor(x=>x.Project.Name)
input proper values for them in the form and you will have Project property of Task populated with these values. But may not be what you desire i.e entering project information twice and this may not be needed (if u are using Linq to sql its sure not needed). When you call your ORM for attaching entities to db entities it will know which Project elements, current Task belongs to.
Side Note: When you have problems with modelbinding, always pay attention to generated html. Generated html will dictate which form values will map to which properties of the model as long as you are using default modelbinder. it becomes especially important if you are having master detail kind of scenario as in your example.
Upvotes: 4