简体   繁体   中英

How to load/modify/save an entity object in different contexts?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM