離線場景保存和刪除實體/實體圖集
這一節的內容是在離線場景中保存實體和實體圖集
在離線場景中,當我們保存一個離線的實體圖集或一個單獨的離線實體時,我們需要做兩件事。首先,我們要把實體附加到新的上下文中,讓上下文了知道存在這些實體。其次,我們需要手動設置每個實體的EntityState,因為新的上下文不知道這些離線實體都經過了些什么操作,所以新的上下文不能自動地給實體添加EntityState。上一節我們清楚了附加離線圖集的方法,附加離線圖集時默認添加的EntityState不一定合適,所以我們就要執行第二步:給實體或實體圖集添加合適的EntityState。
1.離線場景中保存實體
在離線場景中我們保存一個實體時,最核心的問題:我們需要知道一個實體是新建的還是本來就存在的。只有知道了這個問題的答案,我們才能給實體設置EntityState。如果實體主鍵值是0(主鍵是Int類型,默認值為0)我們認為這個實體是新建的,給它的狀態標記為Added;如果主鍵值大於0,那么我們就認為這個實體是已經存在的,將它的狀態標記為Modified。如下圖所示:
下邊是一個栗子:
// 新建的離線實體 var student = new Student(){ StudentName = "Bill" }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = student.StudentId == 0? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
因為Student是新建的,Id默認為0,所以Student被標記為Added,在數據庫中執行:
exec sp_executesql N'INSERT [dbo].[Student]([StudentName], [StandardId]) VALUES (@0, NULL) SELECT [StudentID] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity(),@0='Bill'
同樣的,如果一個實體的主鍵不是0,那么將它的狀態被標記為Modified,一個栗子:
// 雖然是新建的,但是因為Id不是0,所以EF認為是已存在的 var student = new Student(){ StudentId = 1, StudentName = "Steve" }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = student.StudentId == 0? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
在數據庫中執行如下代碼,注意:如果數據庫中沒有StudentId 為1的記錄時,會報異常
exec sp_executesql N'UPDATE [dbo].[Student] SET [StudentName] = @0 WHERE @@ROWCOUNT > 0 AND [StudentID] = @1'N'@0 varchar(50),@1 int',@0='Steve',@1=1
2.離線場景中保存實體圖集
上邊部分我們學習了通過主鍵值來設置實體的狀態,這里我們將學習怎么去保存一個實體圖集。
在離線場景中保存一個實體圖集是一件比較復雜的事,我們需要進行認真的設計。離線場景中保存實體圖集最需要解決的問題是給實體圖集中的每一個實體添加標記適當的狀態。但是怎么去設計呢?在下圖中我們可以看到新的Context根本不知道每個實體的狀態。
我們必須在執行SaveChange()方法前確定每一個實體的狀態,下邊介紹通過主鍵設置實體圖集狀態的方法
通過主鍵設置實體圖集的狀態
我們可以通過主鍵來設置實體圖集中每一個實體的狀態。如前邊保存實體時介紹的,如果一個實體的主鍵是CLR數據類型的默認值(如int類型的默認值是0),那么我們認為這個實體是新建的,可以將這個實體標記為Added,在數據庫執行Insert命令;如果主鍵值不是CLR數據類型的默認值,我們認為這個實體是已經存在了,標記為Modified,在數據庫執行Update命令。
一個栗子:
var student = new Student() { //Root entity (沒有主鍵值 Standard = new Standard() //Child entity (有主鍵值) { StandardId = 1, StandardName = "Grade 1" }, Courses = new List<Course>() { new Course(){ CourseName = "Machine Language" }, //Child entity (沒有主鍵值) new Course(){ CourseId = 2 } //Child entity (有主鍵值) } }; using (var context = new SchoolDBEntities()) { //給實體圖集中的所有實體的狀態進行標記 context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified; context.Entry(student.Standard).State = student.Standard.StandardId == 0 ? EntityState.Added : EntityState.Modified; foreach (var course in student.Courses) context.Entry(course).State = course.CourseId == 0 ? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
在上邊的栗子中,Student實體圖集包含Standard和Course實體,context通過每個實體的主鍵對該實體的狀態進行標記。
3.離線場景刪除實體/實體圖集
在離線場景刪除一個實體很簡單,只需通過Entry()方法把實體的狀態標記為Deleted即可,注:我們在將離線實體附加到上下文提過,當父實體的狀態是Deleted時,通過Entry()方法附加實體圖集時,實體圖集的所有子實體都為null,所以在執行SaveChange()進行數據庫刪除時,只刪除父實體的記錄!一個簡單的栗子:
// 待刪除的實體 var student = new Student(){ StudentId = 1 }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = System.Data.Entity.EntityState.Deleted; context.SaveChanges(); }
在上邊的例子中,Student實體的實例只有主鍵值,刪除一個實體也只需要主鍵值就可以了。在數據庫中執行如下代碼:
delete [dbo].[Student] where ([StudentId] = @0)',N'@0 int',@0=1
EF系列目錄鏈接:Entity Franmework系列教程匯總