使用外键更新实体和使用导航属性之间的区别 In Entity Framework

Difference between updating an entity by using a foreign key and using the navigation properties In Entity Framework

-我想知道使用外键更新实体和使用 Entity Framework 中的导航属性之间的区别。

-有没有一种方法可以只调用一次数据库来更新一个复杂的对象?

使用外键更新实体优于导航 属性,简单是因为您不必处理实体的状态。使用导航属性更新实体的最常见问题是在您可能期望相反的情况下获取重复记录。假设你有这个简单的模型:

public class Post
{
    public int Id {get; set;}
    public string Title {get; set;}

    [ForeignKey("CategoryId")]
    public Category Category {get; set;}

    public int CategoryId {get; set;}
}


public class Category
{
    public int Id {get; set;}
    public string Name {get; set;}
}

现在假设您有一个更新 Post 的控制器操作。认为视图有一个列出所有类别的列表框,用户可以更改它。

Post控制器更新

[HttpPost]
public ActionResult UpdatePost(PostDTO, post)
{   
    //Retrived the chosen category using its own "repository" class
    Category newCategory = categoryRepository.Get(post.CategoryId); 

    //other validations here..  

    //Call method to update passing postDTO and the existing category
    postRepository.Update(post, newCategory)
}

现在,在您的Post存储库中,您可以执行如下操作:

public void Update(PostDTO postDTO, Category category)
{
    using (var context = new ScreencastContext())
    {
    //Get the post in this context
      var post = context.Post.SingleOrDefault(x => x.Id == postDTO.Id);

      post.Name = postDTO.Name;
      //set other Post fields...

      //Set the new category to the post
      //this category already exists in the database and was retrieved by another "context"
      post.Category = category;

      context.Post.Attach(post);
      context.Entry(post).State - EntityState.Modified;

      context.SaveChanges();
    }
}

可能你会认为 Entity Framework 会 "catch up" 你的 Category 对象已经存在于数据库中,并且只会更新 Post table 中的外键 ID . 错了。 它实际上会创建一个新类别,这是因为当您从另一个上下文中检索类别时,此上下文不会将其识别为图形的一部分,并且无法识别的实体具有默认状态 Added。因此,当您调用 SaveChanges 时,它将创建一个新类别。

你可以自己管理实体的状态来解决这个问题,但这会变得非常复杂,你很容易被一大堆没人能理解的奇怪代码弄得不知所措。这时候外键就派上用场了。

上面的Update方法可以改写成这样:

public void Update(PostDTO postDTO, Category category)
{
    using (var context = new ScreencastContext())
    {
      //Get the post in this context
      var post = context.Post.SingleOrDefault(x => x.Id == postDTO.Id);

      post.Name = postDTO.Name;
      //set other Post fields...

      //Set the new category to a post
      //this category already exists in the database and was retrived by another "context"
      post.CategoryId = category.Id;

      //This just prevent any accidental navigation property being set.
      post.Category = null;

      context.Post.Attach(post);
      context.Entry(post).State = EntityState.Modified;

      context.SaveChanges();
    }
}

这样你只更新 Post table。不会创建新类别,一切都会按预期运行。

这是我经常在自己的工作场所发现的事情,我总是向人们推荐朱莉·勒曼 (Julie Lerman) 撰写的这篇 惊人 文章:

Why Does Entity Framework Reinsert Existing Objects into My Database?