I am currently using EF4.3 and Code First. Creation of my objects works (via my views - just using the auto-generated Create), but when I attempt to edit an object, it does not save any changes that, utlimately, tie back to my navigation properties. I have been reading on relationships, but I don't understand how to tell my context that the relationship has changed.
Here is some example code of my implementation.
@* Snippet from my view where I link into my ViewModel. *@
<div class="row">
<div class="editor-label">
@Html.LabelFor(model => model.ManagerID)
</div>
<div class="editor-field">
@Html.DropDownListFor(model => model.ManagerID, ViewBag.Manager as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.ManagerID)
</div>
</div>
Here is my Controller implementation (POST of my Edit):
[HttpPost]
public ActionResult Edit(ProjectViewModel projectViewModel)
{
if (ModelState.IsValid)
{
Project project = new Project();
project.ProjectID = projectViewModel.ProjectID;
project.Name = projectViewModel.Name;
project.ProjectManager = repository.GetUser(projectViewModel.ManagerID);
repository.InsertOrUpdateProject(project);
repository.Save();
return RedirectToAction("Index");
}
ViewBag.Manager = new SelectList(repository.GetUsers(), "UserID", "FullName", projectViewModel.ManagerID);
return View(projectViewModel);
}
Within my Project object:
public class Project
{
public int ProjectID { get; set; }
[Required]
public string Name { get; set; }
// Navigation Properties
public virtual User Manager { get; set; }
}
Here is the corresponding method from the repository (where my context resides):
public void InsertOrUpdateProject(Project project)
{
if (program.ProjectID == default(int))
{
context.Projects.Add(project);
}
else
{
context.Entry(project).State = EntityState.Modified;
}
}
Just to be clear, this does work to update my properties, but it does not update my navigation properties (in this case, Manager). Appreciate any help.
Setting the state to
Modified
only marks scalar properties as modified, not navigation properties. You have several options:A hack (you won't like it)
Reload the project from the database including (eager loading) the current manager. Then set the properties. Change tracking will detect a change of the manager again and write an UPDATE.
Expose a foreign key property for the
Manager
navigation property in your model:Now
ManagerID
is a scalar property and setting the state toModified
will include this property. Moreover you don't need to load the Manager user from the database, you can just assign the ID you get from your view:There are several options here, I will list 3 of them:
Option 1: Using GraphDiff
*This needs the
Configuration.AutoDetectChangesEnabled
of your context set to true.Just install GraphDiff with NuGet
Then
For more details about GraphDiff look here.
Option 2: Find and Edit
Searching your entity with EF to track it to the context. Then edit the properties.
*This needs the
Configuration.AutoDetectChangesEnabled
of your context set to true.Option 3: Using a scalar property
In a matter of performance this is the best way, but it mess your class with database concerns. Because you will need to create a scalar (primitive type) property to map the Id.
*In this way there is no need to set the
Configuration.AutoDetectChangesEnabled
to true. And also you won't need to do a query to the database to retrieve the entities (as the first two options would - yes GraphDiff does it behind the scenes!).I am not sure exactly what you mean by navigation properties? Do you mean like a foreign key relationship? If so then try the following data annotation:
Update your EF Context, and see what happens?
UPDATE
See if that works?