zoukankan      html  css  js  c++  java
  • EF Core中如何通过实体集合属性删除从表的数据

    假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系

    Person表数据:

    Book表数据:

    可以看到数据库Book表中所有的数据都属于Person表中"F"这个人

    Person表,下面的Person类是该表在EF Core中的实体类型:

    public partial class Person
    {
        public Person()
        {
            Book = new HashSet<Book>();
        }
    
        public int Id { get; set; }
        public string Code { get; set; }
        public string Name { get; set; }
        public DateTime? CreateTime { get; set; }
        public DateTime? UpdateTime { get; set; }
    
        public ICollection<Book> Book { get; set; }
    }

    因为一个Person对应多个Book,所以Person类中有个集合属性public ICollection<Book> Book { get; set; }

    此外Person类的Code属性为EF Core中实体的Key属性:

    modelBuilder.Entity<Person>(entity =>
    {
        entity.HasKey(e => e.Code);//设置Code为EF Core实体Person的Key属性
    
        entity.HasIndex(e => e.Code)
            .HasName("IX_Person")
            .IsUnique();
    
        entity.Property(e => e.Id).HasColumnName("ID");
    
        entity.Property(e => e.Code)
            .IsRequired()
            .HasMaxLength(50);
    
        entity.Property(e => e.CreateTime)
            .HasColumnType("datetime")
            .HasDefaultValueSql("(getdate())");
    
        entity.Property(e => e.Name).HasMaxLength(50);
    
        entity.Property(e => e.UpdateTime).HasColumnType("datetime");
    });

    Book表,下面的Book类是该表在EF Core中的实体类型:

    public partial class Book
    {
        public int Id { get; set; }
        public string BookCode { get; set; }
        public string BookName { get; set; }
        public string PersonCode { get; set; }
    
        public Person PersonCodeNavigation { get; set; }
    }

    同样因为一个Book只对应一个Person,所以Book类中有个导航属性public Person PersonCodeNavigation { get; set; }

    此外Book类的BookCode属性为EF Core中实体的Key属性:

    modelBuilder.Entity<Book>(entity =>
    {
        entity.HasKey(e => e.BookCode);//设置BookCode为EF Core实体Book的Key属性
    
        entity.Property(e => e.Id).HasColumnName("ID");
        entity.Property(e => e.Id).ValueGeneratedOnAddOrUpdate();
    
        entity.Property(e => e.BookCode).HasMaxLength(50);
    
        entity.Property(e => e.BookName).HasMaxLength(50);
    
        entity.Property(e => e.PersonCode).HasMaxLength(50);
    
        entity.HasOne(d => d.PersonCodeNavigation)
            .WithMany(p => p.Book)
            .HasPrincipalKey(p => p.Code)
            .HasForeignKey(d => d.PersonCode)
            .OnDelete(DeleteBehavior.Cascade)
            .HasConstraintName("FK_Book_Person");
    });

    现在假如我们想通过new一个Book b3,然后在Person类的集合属性Book中来删除b3,结果数据库Book表中的数据并不会被删除,原因见下面注释:

    using (TestDBContext testDBContext = new TestDBContext())
    {
        var person = testDBContext.Person.Where(P => P.Code == "F").Include(p => p.Book).First();
    
        Book b3 = new Book() { BookCode = "B3" };
    
        var f = person.Book.Remove(b3);//不起作用,结果BookCode为"B3"的数据并没有从数据库Book表中删除,这是因为我们在上面的testDBContext.Person.Where(P => P.Code == "F").Include(p => p.Book).First()中调用了Include方法,导致BookCode为"B3"的实体已经被加载到EF Core的DbContext中被Track了,这时上面一行的Book b3 = new Book() { BookCode = "B3" }其BookCode也为"B3",Book b3将无法在person.Book.Remove(b3)时被Attach到DbContext内进行Track,因为DbContext中被Track的实体必须是Key属性值唯一的,前面已经有一个BookCode为"B3"实体实例在DbContext中了,就无法再加入另一个BookCode为"B3"的实体实例了,所以删除不会起任何作用。
                                       //此外这里的删除不起作用还有个很重要的原因,那就是person.Book.Remove(b3)方法会先尝试在位于内存中的person.Book集合中匹配Book b3,但是由于Book b3是我们自己new出来的,它和person.Book中的集合成员指向的是不同的Book对象实例,所以Book b3无法和person.Book中的任何集合成员匹配,尽管它们的Key属性BookCode都为"B3"。由于Book b3和person.Book的集合成员匹配都失败了,那么person.Book.Remove(b3)不会删除数据库中的任何数据,只有当以Book b3和person.Book的一个集合成员匹配成功时,person.Book.Remove(b3)才会在数据库中删除对应的数据。
    
        testDBContext.SaveChanges();
    }

    接下来,我们通过从Person.Book集合中找出BookCode为"B3"的实体实例赋值给Book b3,然后从Person.Book集合中再删除该实例,这次我们会发现数据库Book表中的数据就被删除了,原因见下面注释:

    using (TestDBContext testDBContext = new TestDBContext())
    {
        var person = testDBContext.Person.Where(P => P.Code == "F").Include(p => p.Book).First();
    
        Book b3 = person.Book.First(b => b.BookCode == "B3");
    
        EntityState s = testDBContext.Entry(b3).State;//状态为EntityState.Unchanged
    
        var f = person.Book.Remove(b3);//起作用,因为这时上面一行的Book b3指向的就是testDBContext.Person.Where(P => P.Code == "F").Include(p => p.Book).First()中Include方法加载的实体实例,所以相当于Book b3已经被加载到EF Core的DbContext中被Track了,所以这里调用person.Book.Remove(b3)之后Book b3的State将被标记为EntityState.Deleted,然后执行下面的testDBContext.SaveChanges()后,从数据库Book表中将BookCode为 "B3"的数据行删除掉
    
        s = testDBContext.Entry(b3).State;//状态为EntityState.Deleted
    
        testDBContext.SaveChanges();
    }

    注意采用Person.Book集合删除Book b3这种方式,会根据EF Core中DeleteBehavior的不同设置会有不同的结果,因为本例中我们设置了DeleteBehavior.Cascade,所以EF Core会从数据库Book表中将BookCode为 "B3"的数据行删除掉,但是如果采用DeleteBehavior的其它设置,那么会得到不同的结果,详情可以查看:Cascade Delete

    最后我们来看看new一个Book b3,通过DbContext的DbSet属性来删除b3,同样这次数据库Book表中的数据也会被成功删除,原因见下面注释:

    using (TestDBContext testDBContext = new TestDBContext())
    {
        Book b3 = new Book() { BookCode = "B3" };
    
        EntityState s = testDBContext.Entry(b3).State;//状态为EntityState.Detached
        var f = testDBContext.Book.Remove(b3);//起作用,因为testDBContext.Book.Remove(b3)是通过DbContext的DbSet<Book>属性来删除数据的,在此之前也没有BookCode为"B3"的实体实例被加载到DbContext进行Track,所以这里调用testDBContext.Book.Remove(b3)会把Book b3加载到DbContext进行Track,并将Book b3的State标记为EntityState.Deleted,然后执行下面的testDBContext.SaveChanges()后,从数据库Book表中将BookCode为 "B3"的数据行删除掉
    
        s = testDBContext.Entry(b3).State;//状态为EntityState.Deleted
    
        testDBContext.SaveChanges();
    }
  • 相关阅读:
    如何隐藏DLL中,导出函数的名称?
    排序算法之0-1、0-1-2排序
    在Vista以上版本运行WTL程序,有时候会提示“这个程序可能安装补正确...”的错误
    编码格式(未完待续......)
    WinDbg分析DUMP文件
    自己捣鼓了一个12306抢票软件,欢迎大家使用,并讨论改进方法!
    Cocos2D-X扫盲之坐标系、锚点
    Spring核心组件剖析
    走进Java中的持有对象(容器类)【二】Collection
    走进JVM【二】理解JVM内存区域
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9770142.html
Copyright © 2011-2022 走看看