Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
354 views
in Technique[技术] by (71.8m points)

code first - Cannot get relationship to update for navigation properties in entity framework

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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)

    //...
    else
    {
        var manager = project.Manager;
        project.Manager = null;
        context.Entry(project).State = EntityState.Modified;
        // the line before did attach the object to the context
        // with project.Manager == null
        project.Manager = manager;
        // this "fakes" a change of the relationship, EF will detect this
        // and update the relatonship
    }
    
  • 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:

    public class Project
    {
        public int ProjectID { get; set; }
        [Required]
        public string Name { get; set; }
    
        public int ManagerID { get; set; }
        public virtual User Manager { get; set; }
    }
    

    Now ManagerID is a scalar property and setting the state to Modified 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:

    Project project = new Project();
    project.ProjectID = projectViewModel.ProjectID;
    project.Name = projectViewModel.Name;
    project.ManagerID = projectViewModel.ManagerID;
    repository.InsertOrUpdateProject(project);
    repository.Save();
    

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...