EF 6 Code First,如何在引用的ICollection中持久删除操作

时间:2015-04-04 16:36:18

标签: c# entity-framework ef-code-first

我正在努力删除内存中没有推送到数据库的实体。

博客实体引用帖子集合中的0..n条目。

添加或更改帖子条目将在Context对象上的SaveChanges之后生成更新或插入语句。

但是当我删除帖子时,这个删除操作不会被推送到数据库。

为了解决我的问题,我使用下面显示的Blog / Posts模型创建了这个小样本。

我的场景是基于网络的应用,其中应该在第一个请求中加载博客条目和所有相关帖子,然后应修改帖子集合,并应在另一个请求中保存到数据库。

我尝试使用2个不同的DbContext实例来模拟这个场景readCtx& writeCtx在下面的示例代码中。

添加或更改帖子条目工作正常,我为每个帖子条目获得更新语句。

但是:我删除单个帖子的方法没有被推送到数据库,我不确定,EF是如何用于此目的的。

如果我删除了如下所示的帖子条目,那么EF会忽略它并且不会发出删除声明。

posts集合应该绑定到UI,所以我想将任何内部的更改反映到数据库中。

我做错了什么?

using System.Collections.Generic;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;

namespace EFDeleteTest
{


    public class Blog
    {
        public Blog()
        {
            Posts = new List<Post>();
        }
        public int BlogId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public virtual Blog Blog { get; set; }
    }

    public class BlogSystemDbContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        public BlogSystemDbContext() {     }
    }

    class Program
    {
        private static void LogSql(string sqlStmt)
        {
            System.Diagnostics.Debug.WriteLine(sqlStmt);
        }
        static void Main(string[] args)
        {
            Blog blog = null; // Should be used in the web app for binding it to the UI

            using (var readCtx = new BlogSystemDbContext())
            {
                blog = readCtx.Blogs.Include(b1 => b1.Posts).Where(b2 => b2.Name == "Blog X").SingleOrDefault();
            }

            // Simulate the next HTTP Request
            using (var writeCtx = new BlogSystemDbContext())
            {
                writeCtx.Database.Log = LogSql;
                // delete the last post:
                int count = blog.Posts.Count;
                var postToDelete = blog.Posts.Skip(count - 1).Take(1).SingleOrDefault();
                blog.Posts.Remove((Post)postToDelete);

                writeCtx.Blogs.Attach(blog);
                writeCtx.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified;


                foreach (var post in blog.Posts)
                {
                    writeCtx.Entry(post).State = post.PostId == 0 ? EntityState.Added : EntityState.Modified;
                }
                writeCtx.SaveChanges();
            }
        }
    }
}

的web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
        <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </configSections>

    <connectionStrings>
        <add name="BlogSystemDbContext" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=BlogSystemDB;Integrated Security=true;MultipleActiveResultSets=true " />
    </connectionStrings>

    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>

    <entityFramework>
        <providers>
            <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
        </providers>
    </entityFramework>
</configuration>

1 个答案:

答案 0 :(得分:0)

问题在于......

writeCtx.Blogs.Attach(blog);
writeCtx.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified;

您只将blog标记为已修改,而不是其Posts集合。 EF甚至根本没有跟踪该集合。这是一个设计事实。

有几种方法可以解决这个问题。一个昂贵的解决方案是从数据库中重新获取blog,包括其Posts,并从现在更改跟踪的集合中删除帖子。

更便宜的解决方案是创建存根实体 ...

var post = new Post { ID = blog.Posts.Skip(count - 1).Take(1).ID };

...并将其作为Deleted附加到上下文中。

我无法判断哪个选项在实际应用中适合您,但当然您应该尝试便宜的选项。