C# 如何在不同的上下文中加载/修改/保存实体对象?

C# 如何在不同的上下文中加载/修改/保存实体对象?,c#,entity-framework,C#,Entity Framework,使用Entity Framework EF,我想从数据库中加载一个对象,修改它并将其保存回去。但是,加载和保存发生在不同的上下文中,我通过向对象的集合属性添加另一个对象来修改它 根据来自MSDN的代码,考虑以下代码: Blog blog; using (BloggingContext db = new BloggingContext()) { blog = db.Blogs.Include("Posts").Single(); } // No one else knows the `

使用Entity Framework EF,我想从数据库中加载一个对象,修改它并将其保存回去。但是,加载和保存发生在不同的上下文中,我通过向对象的集合属性添加另一个对象来修改它

根据来自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();
}
在我的数据库中,我有一个博客对象和100篇文章。如你所见,我想在这个博客上添加一篇新文章。不幸的是,执行db.Blogs.Attachblog;保存前,引发InvalidOperationException,说明:发生了引用完整性约束冲突:定义引用约束的属性值在关系中的主体对象和从属对象之间不一致

我该怎么做才能让EF更新这个博客

更新:

我认为我试图实现的将实体的数据库更新与修改及其相关子实体解耦的目标是不可能的。相反,我认为现在相反的方向更为可行:将子实体的更新/创建与父实体解耦。这可以通过以下方式完成:

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

您必须将实体附加到上下文,然后更改跟踪应该启动,剩下的工作将由保存更改来完成

供参考:

或者尝试显式添加它,直接设置关系所需信息,而不是通过导航属性,如下所示:

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

您必须将实体附加到上下文,然后更改跟踪应该启动,剩下的工作将由保存更改来完成

供参考:

或者尝试显式添加它,直接设置关系所需信息,而不是通过导航属性,如下所示:

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();
}
编辑3:

型号:

我想,我现在明白这个问题了。如何将实体附加到db上下文并正确设置其状态

您的问题是,当附加博客实例时,它包含一个新的和现有的帖子的集合。解决方案是首先附加一个blog实例的浅层副本,而不包含已填充的帖子集合,然后添加新帖子。您也可以只将现有实例加载到changetracker中,但这会导致到db的往返,并会吸引一些批评人士

b、 t.w.我首先使用了EF的正确版本,然后在添加更正代码之前再现了您的问题

        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:

型号:

我想,我现在明白这个问题了。如何将实体附加到db上下文并正确设置其状态

您的问题是,当附加博客实例时,它包含一个新的和现有的帖子的集合。解决方案是首先附加一个blog实例的浅层副本,而不包含已填充的帖子集合,然后添加新帖子。您也可以只将现有实例加载到changetracker中,但这会导致到db的往返,并会吸引一些批评人士

b、 t.w.我首先使用了EF的正确版本,然后在添加更正代码之前再现了您的问题

        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);
            }

        }

db.Attach不存在。但是db.Blogs.Attach会。但是,我不想在第二个using语句中使用post。负责存储的类不知道blog到底发生了什么,它只知道发生了什么,需要更新。我希望EF能够以某种方式解决剩下的问题。我将相应地澄清我的问题。感谢您的努力,但问题仍然是一样的:更新代码还必须知道新创建和添加的Post对象。我希望它只知道已经更新的Blog对象。否则,我将不得不在最终更新博客的代码中重现用户对博客所做的所有更改。是的,我误解了这个问题。请参阅我的编辑。解决方案是首先添加blog实例的浅层副本,然后添加新的posts.db.Attach不存在。但是db.Blogs.Attach会。但是,我不想在第二个using语句中使用post。负责存储的类不知道blog到底发生了什么,它只知道发生了什么,需要更新。我希望EF能够以某种方式解决剩下的问题。我将相应地澄清我的问题。感谢您的努力,但问题仍然是一样的:更新代码还必须知道新创建和添加的Post对象。我希望它只知道已经更新的Blog对象。否则,我将不得不在最终更新博客的代码中重现用户对博客所做的所有更改。是的,我误解了这个问题。请参阅我的编辑。解决方案是首先添加blog实例的浅层副本,然后添加新帖子;我得到一个InvalidOperationException,它说:发生了引用完整性约束冲突:定义引用约束的属性值
关系中的主体对象和从属对象之间不一致。你发布的链接只是一个模糊的关联。然后试着用艰难的方式去做。博客与帖子有一对多的关系。因此,Posts将包含一个类似blogId的字段,以了解它的父项。然后尝试设置该id,而不是将文章添加到博客的导航属性集合中,并在第二个上下文范围中进行显式添加。这在某种程度上是有效的,但只有在我使用第二个using语句使文章可访问时才有效。虽然这是我试图避免的,但这可能是一个合适的权衡。似乎我在理解EF如何在内部工作时遇到了一些重大问题。我试图实现将实体的数据库更新与修改及其相关子实体解耦的目标是不可能的。相反,我认为现在相反的方向更为可行:将子实体的更新/创建与父实体解耦。这可以通过设置新Post对象的BlogId属性来实现。因此,当您删除由于创建帖子时出现的大括号{}导致的编译错误时,我会将您的答案标记为正确。@dutzu不是true,依赖集合导航属性,一对多子项不会更新。当我使用db.Blogs.Attachblog将博客附加到第二个上下文时;我得到一个InvalidOperationException,它说:发生了引用完整性约束冲突:定义引用约束的属性值在关系中的主体对象和从属对象之间不一致。你发布的链接只是一个模糊的关联。然后试着用艰难的方式去做。博客与帖子有一对多的关系。因此,Posts将包含一个类似blogId的字段,以了解它的父项。然后尝试设置该id,而不是将文章添加到博客的导航属性集合中,并在第二个上下文范围中进行显式添加。这在某种程度上是有效的,但只有在我使用第二个using语句使文章可访问时才有效。虽然这是我试图避免的,但这可能是一个合适的权衡。似乎我在理解EF如何在内部工作时遇到了一些重大问题。我试图实现将实体的数据库更新与修改及其相关子实体解耦的目标是不可能的。相反,我认为现在相反的方向更为可行:将子实体的更新/创建与父实体解耦。这可以通过设置新Post对象的BlogId属性来实现。因此,当您删除编译错误时,我会将您的答案标记为正确,这是由于创建post时出现大括号{}造成的。如果不正确,依赖集合导航属性,一对多子项不会更新。