2

使用实体框架 (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();
}

在我的数据库中,我有 1Blog个 100Post秒的对象。如您所见,我想为此添加一个新PostBlog. 不幸的是,db.Blogs.Attach(blog);在保存之前做,会抛出一个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();
}
4

2 回答 2

2

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

供参考:MSDN 将实体附加到上下文


或者尝试显式添加它并直接设置关系所需的信息,而不是像这样通过导航属性:

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();
}
于 2012-12-14T12:31:48.213 回答
2

编辑 3:

模型:

在此处输入图像描述

我现在明白了这个问题,我想。如何将实体附加到数据库上下文并正确设置它们的状态。

您的问题是,在附加博客实例时,它包含新帖子和现有帖子的集合。解决方案是首先附加博客实例的浅表副本(没有填充的帖子集合),然后添加新帖子。您也可以只将现有实例加载到 changetracker 中,但这会导致到数据库的往返,并且会吸引一些 SO 批评者参加聚会。

顺便说一句,我首先使用了正确版本的 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);
            }

        }
于 2012-12-14T12:33:12.740 回答