一. 簡介
我們在前面章節介紹EF基本增刪改的時候,曾說過EF的SaveChanges()方法,會一次性的將所有的實體的狀態變化統一提交到數據庫,那么你是否想過EF的實體會有哪些狀態變化呢?什么原因會導致其變化呢?我們怎么來追蹤EF的實體變化呢?本章節以追蹤EF實體狀態變化的三種方式為線索,一一介紹上面疑問。
廢不多說,還是先把整理的結論貼出來,然后配合代碼來介紹。
1. EF的實體狀態總共有5種:Added、Deleted、Modified、Detached、unChanged
①. unChanged:屬性值與數據庫中屬性值相同,沒有發生任何變化,首次查詢出來的實體的狀態值為unChanged
②. Modified:實體中的某些屬性的值或所有屬性值與數據庫中的發生了變化
A:從數據庫中查詢出來的實體,直接改實體屬性的值, 可以將實體狀態改為 Modified。
B:自己創建的實體,必須先Attach一下,直接改實體屬性的值,才可以將實體狀態改為 Modified。
③. Added: 實體將由上下文跟蹤,但是在數據庫中還不存在,
Add()和 AddRange方法可以將實體狀態變為:Added
④. Deleted:實體將由上下文跟蹤並存在於數據庫中,但是已被標記為在下次調用 SaveChanges 時從數據庫中刪除。
A:從數據庫中查詢出來的實體,通過Remove方法, 可以將實體狀態改為 Deleted。
B:自己創建的實體,必須先Attach一下,然后Remove方法,才可以將實體狀態改為 Deleted。
⑤. Detached: 調用AsNoTracking方法,取消實體狀態追蹤
2. 追蹤方法一:DbEntityEntry 追蹤單個實體的狀態
①. db.Entry(XXX).State 來獲取實體的狀態
②. db.Entry(XXX).OriginalValues["txt1"] 可以獲取屬性的原始值
③. db.Entry(XXX).CurrentValues["txt1"] 可以獲取屬性的現在值
3. 追蹤方法二:ChangeTracker 追蹤EF上下文中所有實體的狀態
①. db.ChangeTracker.Entries() 獲取所有的實體狀態變化 獲取的是一個 IEnumerable<DbEntityEntry> 集合,遍歷可以獲取每個實體的狀態變化
4. 追蹤方法三:Local 獲取單個實體狀態發生增加、修改的實體集合(不含刪除)
①. db.XXX.Local 獲取的是XXX實體的集合
5. 刪除狀態和附加狀態: AsNoTracking 和 Attach
二. 代碼測試
1. 追蹤方法一:DbEntityEntry 追蹤單個實體的狀態
1 { 2 Console.WriteLine("-------------------追蹤方法一:DbEntityEntry 追蹤單個實體的狀態--------------------------"); 3 //修改前 4 var data1 = db.TestInfor.FirstOrDefault(); 5 DbEntityEntry stata1 = db.Entry(data1); 6 Console.WriteLine("實體狀態為:" + stata1.State); 7 //修改后 8 data1.txt1 = "fk"; 9 DbEntityEntry stata2 = db.Entry(data1); 10 Console.WriteLine("實體狀態為:" + stata2.State); 11 Console.WriteLine("txt1的原始值為" + stata2.OriginalValues["txt1"]); 12 Console.WriteLine("txt1的現在值為" + stata2.CurrentValues["txt1"]); 13 }
分析:修改后實體狀態由Unchanged→Modified了。
2. 追蹤方法二:ChangeTracker 追蹤EF上下文中所有實體的狀態
1 { 2 Console.WriteLine("-------------------追蹤方法二:ChangeTracker 追蹤EF上下文中所有實體的狀態--------------------------"); 3 //1.增加操作 4 db.TestInfor.Add(new TestInfor() 5 { 6 id = Guid.NewGuid().ToString("N"), 7 txt1 = "1", 8 txt2 = "2" 9 }); 10 11 //2. 刪除操作 12 var data = db.TestInfor.FirstOrDefault(); 13 db.TestInfor.Remove(data); 14 15 //3. 修改操作 16 var data2 = db.TestInfor.Where(u => u.id == "123").FirstOrDefault(); 17 data2.txt2 = "mr123"; 18 19 //4. 另外一個實體的增加操作 20 db.TestInfor2.Add(new TestInfor2() 21 { 22 txt11 = "1", 23 txt22 = "2" 24 }); 25 26 List<DbEntityEntry> entityList = db.ChangeTracker.Entries().ToList(); 27 foreach (var item in entityList) 28 { 29 Console.WriteLine("實體狀態為:" + item.State); 30 } 31 32 }
3. 追蹤方法三:Local 獲取單個實體狀態發生增加、修改的實體集合(不含刪除)
1 { 2 Console.WriteLine("-------------------追蹤方法三:Local 獲取單個實體狀態發生增加、修改的實體集合(不含刪除)--------------------------"); 3 //1.增加操作 4 db.TestInfor.Add(new TestInfor() 5 { 6 id = Guid.NewGuid().ToString("N"), 7 txt1 = "1", 8 txt2 = "2" 9 }); 10 11 //2. 刪除操作 12 var data = db.TestInfor.FirstOrDefault(); 13 db.TestInfor.Remove(data); 14 15 //3. 修改操作 16 var data2 = db.TestInfor.Where(u => u.id == "123").FirstOrDefault(); 17 data2.txt2 = "mr123"; 18 19 //4. 另外一個實體的增加操作 20 db.TestInfor2.Add(new TestInfor2() 21 { 22 txt11 = "1", 23 txt22 = "2" 24 }); 25 26 var EntityList = db.TestInfor.Local; 27 28 foreach (var item in EntityList) 29 { 30 Console.WriteLine("實體的值分別為:{0},{1},{2}", item.id, item.txt1, item.txt2); 31 } 32 33 }
分析:這里的Local獲取的是單個DBSet,發生了增加或修改操作,最終的獲取的是實體集合。
4. 刪除狀態追蹤和附加狀態追蹤(AsNoTracking 和 Attach)
1 { 2 Console.WriteLine("-------------------刪除狀態和附加狀態(AsNoTracking 和 Attach) --------------------------"); 3 4 //以修改為例測試狀態 5 { 6 //1. 帶狀態追蹤 7 var item = db.TestInfor.FirstOrDefault(); 8 item.txt2 = "mr333+" + Guid.NewGuid().ToString("N").Substring(7); 9 Console.WriteLine("實體的狀態為:" + db.Entry(item).State); 10 int n = db.SaveChanges(); 11 if (n > 0) 12 { 13 Console.WriteLine("修改成功"); 14 } 15 else 16 { 17 Console.WriteLine("沒有相應的實體需要修改"); 18 } 19 } 20 { 21 //2. 取消狀態追蹤 22 var item = db.TestInfor.AsNoTracking().FirstOrDefault(); 23 item.txt2 = "mr333+" + Guid.NewGuid().ToString("N").Substring(10); 24 Console.WriteLine("實體的狀態為:" + db.Entry(item).State); 25 int n = db.SaveChanges(); 26 if (n > 0) 27 { 28 Console.WriteLine("修改成功"); 29 } 30 else 31 { 32 Console.WriteLine("沒有相應的實體需要修改"); 33 } 34 } 35 { 36 //3. 自己創建實體進行修改 37 TestInfor item = new TestInfor() 38 { 39 id = "123" 40 }; 41 db.TestInfor.Attach(item); 42 item.txt1 = "fk3456"; 43 Console.WriteLine("實體的狀態為:" + db.Entry(item).State); 44 int n = db.SaveChanges(); 45 if (n > 0) 46 { 47 Console.WriteLine("修改成功"); 48 } 49 else 50 { 51 Console.WriteLine("沒有相應的實體需要修改"); 52 } 53 } 54 }