Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 使用外键更新实体和在实体框架中使用导航属性之间的区别_.net_Entity Framework_Entity Framework 6_Repository Pattern - Fatal编程技术网

.net 使用外键更新实体和在实体框架中使用导航属性之间的区别

.net 使用外键更新实体和在实体框架中使用导航属性之间的区别,.net,entity-framework,entity-framework-6,repository-pattern,.net,Entity Framework,Entity Framework 6,Repository Pattern,-我想知道使用外键更新实体和使用实体框架中的导航属性之间的区别 -有没有一种方法只需对数据库进行一次调用就可以更新复杂对象 使用外键更新实体比使用导航属性更可取,因为您不必处理实体的状态。使用导航属性更新实体最常见的问题是在您可能预期相反的情况下获取重复记录。假设您有一个简单的模型: public class Post { public int Id {get; set;} public string Title {get; set;} [ForeignKey("Cat

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


-有没有一种方法只需对数据库进行一次调用就可以更新复杂对象

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

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;}
}
现在,让我们假设您有一个控制器操作来更新帖子。假设视图有一个列表框,列出了所有类别,用户可以对其进行更改

后控制器更新

[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)
}
现在,在您的PostRepository中,您可以执行以下操作:

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();
    }
}
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表中的外键id错误。 它实际上会创建一个新的类别,这是因为当您从另一个上下文检索该类别时,该上下文不会将其识别为图形的一部分,并且未识别的实体的默认状态为Added。因此,当您调用SaveChanges时,它将创建一个新类别

您可以自己管理实体的状态来绕过这个问题,但这可能会变得非常复杂,并且您很容易被许多没有人能够理解的奇怪代码所淹没。这时外键就派上了用场

上面的更新方法可以重写为如下内容:

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();
    }
}
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表。不会创建新类别,所有内容都将按预期工作

这是我发现在我自己的工作场所经常发生的事情,我总是指Julie Lerman写的这篇惊人的文章中提到的人:


你能分享一些例子吗?这太棒了,非常有用,谢谢。如果您只使用类别Id,为什么要调用:Category newCategory=categoryRepository.Get(post.CategoryId);仅传递Id并避免此调用不是更有效吗?是的,如果可以保证传递给控制器的Id是实际存在的Id,则可以跳过该调用。我这样做只是为了让您能够理解从另一个类查询数据并在其他类中使用它的“上下文”问题。还有一个并发性问题:如果有人在你更新帖子时删除了该类别怎么办?这就是你再次检查的原因。哇,太好了,我有一个可以随时删除的碎片。因此,在马的情况下,货币可能是一个严重的问题。非常感谢,我确认,你的文章写得很好。