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

时间:2015-11-19 09:37:17

标签: c# entity-framework entity-framework-6

  

增加(1):其中一份回复说它应该有效。所以我在这篇文章末尾添加了完整的例外

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

MSDN Code First to a new DataBase

我看到的是,如果我创建一个博客,我可以添加一些帖子,将博客添加到数据库并调用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; }
}

用法如下:

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

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

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

blog = context.Blogs.First();
var lastPost = blog.Posts.Last();

实体框架因此很聪明,它不会选择不需要的项目。如果我不使用Post集合,则不会从数据库中检索帖子

因此,我原以为以下方法可行:

blog.Posts.Remove(lastPost);
context.AddOrUpdate(blog);
context.SaveChanges();

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

  

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

     

添加:当然我可以从DbSet中删除该项,但我的问题是:如果Entity Framework足够智能,我不必将帖子添加到DbSet,为什么我必须删除它? / p>

有人要求提供例外文字

System.InvalidOperationException未处理

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

  • 的HResult = -2146233079

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

堆栈跟踪: 在System.Data.Entity.Core.Objects.ObjectContext.PrepareToSaveChanges(SaveOptions选项) at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternal(SaveOptions options,Boolean 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()        at c:\ Users \ Harald \ Documents \ Visual Studio 2013 \ Projects \ EntityFramework \ TryInMemoryDbSet \ TryInMemoryDbSet \ TestDirectDbContext.cs中的TryInMemoryDbSet.TestDirectDbContext.Test2():第75行        在TryInMemoryDbSet.Program.Main(String [] args)中的c:\ Users \ Harald \ Documents \ Visual Studio 2013 \ Projects \ EntityFramework \ TryInMemoryDbSet \ TryInMemoryDbSet \ Program.cs:第19行   InnerException:

2 个答案:

答案 0 :(得分:1)

  

问题:客户不必知道该问题是否正确   创建数据库模型实体框架,除非是数据   去掉?

     

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

Anwser:

您应该从DbSet中删除它,因为当您从集合中删除它时,实际上是删除了&#34;关系&#34;它们之间。但是,关系(在您的情况下)是强制性的,Post的BlogId属性不是NULL,这意味着所有帖子必须属于博客。当您从集合中删除时,EF会尝试将FK设置为​​空值,这就是您获得异常的原因。

这是从Post

中删除Blog时EF执行的生成的SQL
UPDATE [dbo].[Posts]
SET [BlogId] = NULL
WHERE ([Id] = @0)

如果BlogId可以为空,那就可以了。

答案 1 :(得分:0)

blog.Posts.Remove(lastPost);

lastPost集合中删除blog.Posts,而不是从数据库中删除。

正如我想象的那样,帖子需要博客,帖子不能存在,PostId的值为null。

至少2个解决方案:

  • context.Set()。删除(lastPost)或
  • 在Post的复合PK中使用BlogId。因此,当设置为null时,PostId将在Db中触发删除。