从实体框架 6 中的集合中删除

Har*_*lse 7 c# entity-framework entity-framework-6

补充(1):其中一个答复说它应该有效。所以我在这篇文章的末尾添加了完整的异常

简化:我有一系列对象,每个对象都有其他对象的集合。一个例子是包含帖子集合的博客。MSDN 经常使用这个作为例子

MSDN Code First 到新数据库

我看到的是,如果我创建一个博客,我可以添加一些帖子,将博客添加到数据库并调用 SaveChanges。实体框架认识到帖子应该位于不同的表中,并且具有博客表的外键。

如果没有实体框架,这正是创建数据库的方式。

无需了解单独的 Post 表和 Blog 表的外键,即可从博客获取所有帖子并添加帖子。

但是,当我尝试从博客中删除帖子时,突然出现有关外键的错误。

注:以下内容不涉及效率

public class Blog
{
    public int Id { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Post> Posts { get; set; } 
}

public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; } 
}

public class BloggingContext : DbContext
{
    public BloggingContext(string nameOrConnectionString)
        : base(nameOrConnectionString){}

    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

用法如下:

class Program
{
    static void Main(string[] args)
    {
        const string dbName = "MyTestDb";
        Database.SetInitializer(new DropCreateDatabaseAlways <BloggingContext> ());
        using (var context = new BloggingContext(this.DbName))
        {
            // create a blog:
            var blog = new Blog()
            {
                Name = "First Blog",
                Posts = new List<Post>()
                {
                    new Post() { Title = "My 1st Post", Content = "Hello World!" },
                    new Post() { Title = "My 2nd Post", Content = "All animals are equal but pigs are more equal"},
                    new Post() { Title = "My 3rd Post", Content = "Shall I compare thee to a summer's day" },
                 },
            };
            context.Blogs.Add(blog);
            context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

好处是,客户端不必知道数据库的实际组织方式。客户端不必知道博客和帖子位于不同的表中。对于客户来说,博客有一系列帖子。

同样,如果客户请求博客并访问博客中的帖子,实体框架知道从哪里获取帖子。客户端不必知道帖子使用外键保存在不同的表中。

blog = context.Blogs.First();
var lastPost = blog.Posts.Last();
Run Code Online (Sandbox Code Playgroud)

实体框架非常智能,它不会选择不需要的项目。如果我不使用 Post 集合,则不会从数据库中检索 Posts

因此我预计以下内容会起作用:

blog.Posts.Remove(lastPost);
context.AddOrUpdate(blog);
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

我原以为实体框架会知道最后一个帖子已被删除,因此会命令从帖子表中删除该项目。但它并没有这样做。我收到一个异常,“该关系无法更改,因为一个或多个外键属性不可为空。bla bla”,这意味着该帖子应该已从帖子表中删除。

问题:除非要删除数据,否则客户端不必知道实体框架创建的数据库模型,这是否正确?

添加:当然,我可以从 DbSet 中删除该项目,但我的问题是:如果实体框架足够智能,我不必将帖子添加到 DbSet 中,为什么我必须删除它?

有人询问异常文本

System.InvalidOperationException 未处理

  • _H结果=-2146233079
  • _message=操作失败:无法更改关系,因为一个或多个外键属性不可为空。当关系发生更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

  • HResult=-2146233079

  • 瞬态=假
  • 消息=操作失败:无法更改关系,因为一个或多个外键属性不可为空。当关系发生更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。
  • 来源=EntityFramework

StackTrace:在System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(SaveOptions选项)在System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions选项,布尔executeInExistingTransaction)在System.Data.Entity.Core。 Objects.ObjectContext.SaveChanges(SaveOptions选项)在System.Data.Entity.Internal.InternalContext.SaveChanges()在System.Data.Entity.Internal.LazyInternalContext.SaveChanges()在System.Data.Entity.DbContext.SaveChanges()在TryInMemoryDbSet.TestDirectDbContext.Test2()在c:\ Users \ Harald \ Documents \ Visual Studio 2013 \ Projects \ EntityFramework \ TryInMemoryDbSet \ TryInMemoryDbSet \ TestDirectDbContext.cs:第75行在TryInMemoryDbSet.Program.Main(String [] args)在c: \Users\Harald\Documents\Visual Studio 2013\Projects\EntityFramework\TryInMemoryDbSet\TryInMemoryDbSet\Program.cs:第 19 行 InnerException:

Fab*_*Luz 4

问题:除非要删除数据,否则客户端不必知道实体框架创建的数据库模型,这是否正确?

添加:当然,我可以从 DbSet 中删除该项目,但我的问题是:如果实体框架足够智能,我不必将帖子添加到 DbSet 中,为什么我必须删除它?

答:

您应该将其从集合中删除,DbSet因为当您从集合中删除它时,您实际上正在删除它们之间的“关系”。但是,关系(在您的情况下)是强制性的,帖子的BlogId属性不为空,这意味着所有帖子都必须属于博客。当您从集合中删除时,EF 尝试将 FK 设置为空值,这就是您收到异常的原因。

Post这是当您从 a 中删除 a 时 EF 执行的生成 SQLBlog

UPDATE [dbo].[Posts]
SET [BlogId] = NULL
WHERE ([Id] = @0)
Run Code Online (Sandbox Code Playgroud)

如果可以为空,它就会起作用BlogId

  • 你的第二个说法不正确。您不必将其从集合和DbSet中删除,您只需从DbSet中将其删除(集合不是必需的)。删除对象的唯一方法是将其从 DbSet 中删除。当您从集合中删除它时,您只是删除了它的关系。EF 识别出需要插入某些内容,并自动执行此操作。但是,可能出于安全原因,它不会自动删除某些内容。 (2认同)