假設在數據庫中有兩個表: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(); }