EF 6 Code First,如何在引用的 ICollection 中保留删除操作
EF 6 Code First, How to persist delete operation in a referenced ICollection
我正在努力删除内存中未推送到数据库的实体。
博客实体引用 Posts 集合中的 0..n 个条目。
添加或更改 Posts 条目将在上下文对象上的 SaveChanges 之后生成更新或插入语句。
但是当我删除一个 Post 时,这个删除操作不会推送到数据库。
为了重现我的问题,我使用如下所示的博客/Posts 模型创建了这个小示例。
我的场景是一个基于网络的应用程序,其中博客条目和所有相关的 posts 应该在第一次请求时加载,然后 posts 集合应该被修改并保存根据另一个请求发送到数据库。
我尝试在下面的示例代码中使用 2 个不同的 DbContext 实例 readCtx 和 writeCtx 来模拟这种情况。
添加或更改 Post 条目工作正常,我得到每个 Post 条目的更新语句。
但是:我删除单个 post 的方式没有被推送到数据库,我不确定 EF 是如何用于此的。
如果我如下所示删除 posts 条目,则 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>
问题是...
writeCtx.Blogs.Attach(blog);
writeCtx.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified;
您仅将 blog
标记为已修改,而不是其 Posts
集合。 EF 甚至根本不跟踪该集合。这是一个设计事实。
有几种方法可以解决这个问题。一个昂贵的解决方案是从数据库中重新获取 blog
,包括它的 Posts
,并从现在已跟踪更改的集合中删除 post。
一个更便宜的解决方案是创建一个 存根实体...
var post = new Post { ID = blog.Posts.Skip(count - 1).Take(1).ID };
...并将其作为 Deleted
附加到上下文中。
我无法判断在实际应用中哪个选项适合您,但您当然应该尝试便宜的选项。
我正在努力删除内存中未推送到数据库的实体。
博客实体引用 Posts 集合中的 0..n 个条目。
添加或更改 Posts 条目将在上下文对象上的 SaveChanges 之后生成更新或插入语句。
但是当我删除一个 Post 时,这个删除操作不会推送到数据库。
为了重现我的问题,我使用如下所示的博客/Posts 模型创建了这个小示例。
我的场景是一个基于网络的应用程序,其中博客条目和所有相关的 posts 应该在第一次请求时加载,然后 posts 集合应该被修改并保存根据另一个请求发送到数据库。
我尝试在下面的示例代码中使用 2 个不同的 DbContext 实例 readCtx 和 writeCtx 来模拟这种情况。
添加或更改 Post 条目工作正常,我得到每个 Post 条目的更新语句。
但是:我删除单个 post 的方式没有被推送到数据库,我不确定 EF 是如何用于此的。
如果我如下所示删除 posts 条目,则 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>
问题是...
writeCtx.Blogs.Attach(blog);
writeCtx.Entry(blog).State = blog.BlogId == 0 ? EntityState.Added : EntityState.Modified;
您仅将 blog
标记为已修改,而不是其 Posts
集合。 EF 甚至根本不跟踪该集合。这是一个设计事实。
有几种方法可以解决这个问题。一个昂贵的解决方案是从数据库中重新获取 blog
,包括它的 Posts
,并从现在已跟踪更改的集合中删除 post。
一个更便宜的解决方案是创建一个 存根实体...
var post = new Post { ID = blog.Posts.Skip(count - 1).Take(1).ID };
...并将其作为 Deleted
附加到上下文中。
我无法判断在实际应用中哪个选项适合您,但您当然应该尝试便宜的选项。