问题
-I Want to know the Difference between updating an entity by using foreign key and using the navigation properties in Entity Framework.
-Is there a way to update a complex object with only one call to the database?
回答1:
Using the Foreign key to update an entity is preferable over the navigation property, simple because you won't have to deal with the state of the entities. The most common problem with updating entities using navigation properties is getting duplicates records when you probably expect the contrary. Suppose you have this simple model:
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;}
}
Now let's suppose you have a controller action which updates a Post. Think that the View has a ListBox listing all categories and the user can change it.
PostController Update
[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)
}
Now, in your PostRepository, you do something like this:
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();
}
}
Probably you would think Entity Framework would "catch up" that your Category object already exists in the database, and will only update the foreign key id in the Post table. Wrong. It will actually create a new Category, and that's because when you retrieved the Category from another context, this context doesn't recognize it as part of the graph and unrecognized entities have a default state of Added. So when you call SaveChanges it will create a new Category.
You can manage the state of the entities by yourself to get around this problem, but this can get very complicated and you can easily ended up overwhelmed with a lot of weird code that no one can understand. That's when Foreign Keys come in handy.
The Update method above can be rewriten to something like this:
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();
}
}
This way you update only the Post table. A new category won't be created and everything will work as expected.
This is something that I found happening in my own workplace very often, and I always refer to people this amazing article written by Julie Lerman:
Why Does Entity Framework Reinsert Existing Objects into My Database?
来源:https://stackoverflow.com/questions/35018582/difference-between-updating-an-entity-by-using-a-foreign-key-and-using-the-navig