With the Entity Framework (EF) I want to load an object from my database, modify it and save it back. However, loading and saving happens in different contexts and I modify it by adding another object to a collection property of the object.
Consider the following code based on the famous blog/posts example from MSDN:
Blog blog;
using (BloggingContext db = new BloggingContext())
{
blog = db.Blogs.Include("Posts").Single();
}
// No one else knows the `post` object directly.
{
Post post = new Post {Blog = blog, Title = "Title", Content = "Content"};
blog.Posts.Add(post);
}
using (BloggingContext db = new BloggingContext())
{
// No idea what I have to do before saving...
// Can't do anything with `post` here, since this part will not know this
// object directly.
//db.Blogs.Attach(blog); // throws an InvalidOperationException
db.SaveChanges();
}
In my database I have 1 Blog
object with 100 Post
s. As you can see, I want to add a new Post
to this Blog
. Unfortunately, doing db.Blogs.Attach(blog);
before saving, throws an InvalidOperationException
saying: "A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship."
What do I have to do to let the EF update this blog?
UPDATE:
I think what I was trying to achieve (decoupling the database update of an entity from the modifications and its related child entities) is not possible. Instead, I consider the opposite direction more feasible now: decoupling the update/creation of a child entity from its parent entity. This can be done the following way:
Blog blog;
using (BloggingContext db = new BloggingContext())
{
blog = db.Blogs.Single();
}
Post post = new Post {BlogId = blog.BlogId, Title = "Title", Content = "..."};
using (BloggingContext db = new BloggingContext())
{
db.Posts.Add(post);
db.SaveChanges();
}
You have to attach the entity to the context and then change tracking should kick in and save changes will do the rest.
For reference: MSDN Attach Entities to Context
Or try adding it explicitly and set the relationship needed information directly and not through the navigation property like so:
Blog blog;
using (BloggingContext db = new BloggingContext())
{
blog = db.Blogs.Include("Posts").Single();
Post post = new Post {Blog = blog, Title = "Title", Content = "Content"};
post.blogId = blog.Id;
db.Posts.Add(post);
db.SaveChanges();
}
EDIT 3:
Model:
I understand the question now, me thinks. How do you attach entities to a db context and set their state correctly.
Your problem is that when attaching the blog instance it contains an collection of new and existing posts. The solution is to first attach a shallow copy of the blog instance (without a populated collection of posts) and then add the new posts. You can also just load the existing instance into the changetracker but this incurs a roundtrip to the db and will attract some SO critics to the party.
btw i first used the correct version of EF and then reproduced your problem before adding the correcting code.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
Blog blog;
private void fetchBlogData_Click(object sender, EventArgs e) {
using (var db = new StackOverflowEntities()) {
blog = db.Set<Blog>().Include("Posts")
.FirstOrDefault();
}
}
private void commitAllPosts_Click(object sender, EventArgs e) {
using (var db = new StackOverflowEntities()) {
// load existing blog into ChangeTracker (but not efficient)
// var existingBlog = db.Set<Blog>().First(b => b.BlogId == blog.BlogId);
// make shallow copy of existing blog and attach it
Blog existingBlog = new Blog {
BlogId = blog.BlogId,
Name = blog.Name
};
db.Set<Blog>().Attach(existingBlog);
// if the root blog record must be updated
//db.Entry(existingBlog).State == EntityState.Modified;
// add new posts to tracked Blog entity
foreach (var post in blog.Posts) {
if (post.PostId == 0) {
existingBlog.Posts.Add(post);
}
}
db.SaveChanges();
}
}
private void createArbPosts_Click(object sender, EventArgs e) {
var post = new Post {
Text = "Today I read but never understood a StackOverflow question.... again."
};
blog.Posts.Add(post);
var postPS = new Post {
Text = "Actually, i'm not sure i understand it yet."
};
blog.Posts.Add(postPS);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.