2

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 Posts. 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();
}

2 Answers 2

2

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();
}
Sign up to request clarification or add additional context in comments.

5 Comments

When I attach the blog to the second context with db.Blogs.Attach(blog); I get a 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. The link you posted is only vaguely related.
then try doing it the "hard" way. Blogs have a one-to-many relationship with Posts. So then Posts will contain a field like blogId to know it's parent. Try then to just set that id instead of adding the Post to the blog's navigation property collection and just do an explicit Add in the second context scope.
This works somewhat, but only when I make post accessible from the second using statement. Although this is what I'm trying to avoid, it might be a suitable trade-off.
It seems like I had some major issues understanding how the EF works internally. What I was trying to 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 by setting the the BlogId property of the new Post object. Hence, I will mark your answer as correct, when you remove the compilation error due to the curly braces {} around the creation of post.
@dutzu Not true, Dependent collections (navigation properties, one to many children) are not updated.
2

EDIT 3:

Model:

enter image description here

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.

b.t.w. 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);
            }

        }

3 Comments

db.Attach() does not exist. But db.Blogs.Attach() does. However, I don't want to do anything with post inside the second using statement. The class that will do the storing does not know what exactly happend with blog, it only knows that something happend and it needs to be updated. I hope the EF is capable to figure out the rest somehow. I'll clarify my question accordingly.
Thanks for your effort, but the problem remains the same: the update code also has to know the newly created and added Post object. I want it to only know the Blog object that has been updated. Otherwise I'll have to reproduce all the changes a user did to a Blog in the code that finally updates that Blog.
Yeah, i misunderstood the question. See my edit. The solution is to first add a shallow copy of the blog instance and then add the new posts.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.