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

时间:2012-03-07 18:11:24

标签: entity-framework code-first

我目前正在使用EF4.3和Code First。创建我的对象(通过我的视图 - 只使用自动生成的创建),但是当我尝试编辑对象时,它不会保存任何与我的导航属性完全相关的更改。我一直是reading on relationships,但我不明白如何告诉我的情况,这种关系已经改变了。

以下是我实施的一些示例代码。

@* 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>

这是我的Controller实现(编辑的POST):

    [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);
    }

在我的Project对象中:

public class Project
{
    public int ProjectID { get; set; }
    [Required]
    public string Name { get; set; }

    // Navigation Properties
    public virtual User Manager { get; set; }
}

以下是来自存储库(我的上下文所在的位置)的相应方法:

public void InsertOrUpdateProject(Project project)
    {
        if (program.ProjectID == default(int))
        {
            context.Projects.Add(project);
        }
        else
        {
            context.Entry(project).State = EntityState.Modified;
        }
    }

为了清楚起见,这确实可以更新我的属性,但它不会更新我的导航属性(在本例中为Manager)。感谢任何帮助。

3 个答案:

答案 0 :(得分:11)

将状态设置为Modified仅将标量属性标记为已修改,而不是导航属性。您有几种选择:

  • 黑客(你不会喜欢)

    //...
    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
    }
    
  • 从数据库重新加载项目,包括(急切加载)当前管理器。然后设置属性。更改跟踪将再次检测到经理的更改并写入更新。

  • 在模型中公开Manager导航属性的外键属性:

    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; }
    }
    

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

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

答案 1 :(得分:4)

这里有几个选项,我将列出其中3个:

选项1:使用GraphDiff

*这需要将您的上下文的Configuration.AutoDetectChangesEnabled设置为true。

只需使用NuGet

安装GraphDiff即可
Install-Package RefactorThis.GraphDiff

然后

using (var context = new Context())
{
    var customer = new Customer()
    {
        Id = 12503,
        Name = "Jhon Doe",
        City = new City() { Id = 8, Name = "abc" }
    };

    context.UpdateGraph(customer, map => map.AssociatedEntity(p => p.City));
    context.Configuration.AutoDetectChangesEnabled = true;

    context.SaveChanges();
}

有关GraphDiff的详细信息,请查看here

选项2:查找和编辑

使用EF搜索您的实体,将其跟踪到上下文。然后编辑属性。

*这需要将您的上下文的Configuration.AutoDetectChangesEnabled设置为true。

var customer = new Customer()
{
    Id = 12503,
    Name = "Jhon Doe",
    City = new City() { Id = 8, Name = "abc" }
};

using (var context = new Contexto())
{
    var customerFromDatabase = context.Customers
                                      .Include(x => x.City)
                                      .FirstOrDefault(x => x.Id == customer.Id);

    var cityFromDataBase = context.Cities.FirstOrDefault(x => x.Id == customer.City.Id);

    customerFromDatabase.Name = customer.Name;
    customerFromDatabase.City = cityFromDataBase;                

    context.Configuration.AutoDetectChangesEnabled = true;
    context.SaveChanges();
}

选项3:使用标量属性

在性能方面,这是最好的方法,但它会让您的课程陷入数据库问题。因为您需要创建一个标量(原始类型)属性来映射Id。

*这样就不需要将Configuration.AutoDetectChangesEnabled设置为true。而且你也不需要对数据库进行查询以检索实体(因为前两个选项是 - 是的,GraphDiff在幕后做它!)。

var customer = new Customer()
{
    Id = 12503,
    Name = "Jhon Doe",
    City_Id = 8,
    City = null
};

using (var contexto = new Contexto())
{
    contexto.Entry(customer).State = EntityState.Modified;
    contexto.SaveChanges();
}

答案 2 :(得分:-2)

我不确定你的导航属性是什么意思?你的意思是外键关系吗?如果是,请尝试以下数据注释:

public class Project
{
    public int ProjectID { get; set; }

    [Required]
    public string Name { get; set; }

    [ForeignKey("YourNavigationProperty")]
    public virtual UserManager { get; set; }
}

更新你的EF背景,看看会发生什么?

更新

public class Project
{
    public int ProjectID { get; set; }

    [Required]
    public string Name { get; set; }

    [ForeignKey("ManagerId")]
    public ManagerModel UserManager { get; set; }
}

public class ManagerModel
{
    [Key]
    public int ManagerId { get; set; }

    public String ManagerName { get; set; }
}

看看是否有效?