且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在实体框架中无法获取导航属性的更新关系

更新时间:2023-02-13 11:19:30

将状态设置为修改仅标记标量属性作为修改,而不是导航属性。你有几个选择:




  • 一个黑客(你不会喜欢)

      // ... 
    else
    {
    var manager = project.Manager;
    project.Manager = null;
    context.Entry(project).State = EntityState.Modified;
    //之前的行将对象附加到上下文
    // with project.Manager == null
    project.Manager = manager;
    //这个假改变了关系,EF会检测到这个
    //并更新相关权
    }


  • 从包含(eager加载)当前管理器的数据库重新加载项目。然后设置属性。更改跟踪将再次检测管理员的更改并写入更新。


  • 管理器$您的模型中的c $ c>导航属性:

      public class Project 
    {
    public int ProjectID {得到;组;
    [必需]
    public string Name {get;组; }

    public int ManagerID {get;组; }
    public virtual User Manager {get;组;
    }

    现在 ManagerID 是一个标量属性,将状态设置为修改将包含此属性。此外,您不需要从数据库加载Manager用户,您可以从视图中分配您获得的ID:

     项目项目=新项目(); 
    project.ProjectID = projectViewModel.ProjectID;
    project.Name = projectViewModel.Name;
    project.ManagerID = projectViewModel.ManagerID;
    repository.InsertOrUpdateProject(project);
    repository.Save();



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)

    //...
    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();