一. 背景
說起EF的增刪改操作,相信很多人都會說,有兩種方式:① 通過方法操作 和 ② 通過狀態控制。
相信你在使用EF進行刪除或修改操作的時候,可能會遇到以下錯誤:“ The object cannot be deleted because it was not found in the ObjectStateManager”,通過百度查詢,說是沒有進行上下文的附加,需要attach一下,那么都哪些情況需要附加,哪些是不需要附加的呢?
在本章節,將結合EF的兩種方式,從EF本地緩存的角度來詳細介紹EF的增刪改情況。
二. EF核心結論
經過無數次摸爬滾打,詳細的代碼測試,整理出下面關於EF增刪改操作的結論。
1. 總綱
SaveChangs的時候一次性保存本地屬性狀態的全部變化.(換言之:只有本地緩存屬性的狀態發生變化了,SaveChanges才會實際生效)
補充:這里的屬性狀態的變化是存在於服務器端,一定不要理解為存在於本地,這也是為什么EF上下文不能用單例創建了。
EF的本地緩存屬性的三種形式:
①.通過Attach附加.
②.通過EF的即時查詢,查詢出來的數據,自動就本地緩存了.
③.通過狀態控制. eg:Added、Modified、Deleted. (db.Entry(sl).State = EntityState.Added;)
2. EF的增刪改操作的操作有兩種形式
(一). 通過方法來操控
a. 增加1個實體. Add() 不需要Attach()附加.(當然附加了也不受影響)
b. 增加1個集合. AddRange() 不需要Attach()附加.(當然附加了也不受影響)
c. 刪除. Remove(). 分兩種情況:
特別注意:如果數據為空,會報錯.所以在實際開發過程中,要采用相應的業務邏輯進行處理.
①:自己創建了一個實體(非查詢出來的),必須先Attach,然后Remove.
②:訪問數據庫,即時查詢出來的數據(已經放到EF本地緩存里了),可以省略Attach,直接Remove(當然附加了也不受影響)
d. 修改(如果數據主鍵不存在,執行增加操作). AddOrUpdate(),可以省略Attach,直接AddOrUpdate.
需要引用程序集:using System.Data.Entity.Migrations;
①: 如果是執行增加操作,不需要進行Attach附加,但附加了Attach不受影響
②:如果是執行修改操作,不能進行Attach的附加,附加了Attach將導致修改失效,saveChange為0(無論是自己創建的或即時查詢出來的,都不能進行Attach的附加)
e. 修改. 不需要調用任何方法.
該種方式如果實體為空,SaveChanges時將報錯.
①:自己創建對象→先Attach(根據主鍵來區分對象)→然后修改屬性值→最后saveChange
②: EF即時查詢對象(自動本地緩存)→然后修改屬性值→最后saveChange
(二). 通過修改本地屬性的狀態來操控.
(該種方式本身已經改變了本地緩存屬性了,所以根本不需要Attach附加)
a. 增加. db.Entry(sl).State = EntityState.Added;
b. 刪除. db.Entry(sl).State = EntityState.Deleted;
特別注意:如果數據為空,會報錯.所以在實際開發過程中,要采用相應的業務邏輯進行處理.
①.適用於自己創建對象(根據主鍵來確定對象),然后刪除的情況.
②.適用於即時查詢出來的對象,然后進行刪除的情況.
c. 修改. db.Entry(sl).State = EntityState.Modified;
特別注意:如果數據為空,會報錯.所以在實際開發過程中,要采用相應的業務邏輯進行處理.
①.適用於自己創建對象(根據主鍵來確定對象),然后修改的情況.
②.適用於即時查詢出來的對象,然后修改的情況.
三. 實戰操練
1. 增加方法(Add和AddRange)
1 private static void ADD() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. Add()方法-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = Guid.NewGuid().ToString("N"), 11 txt1 = "t1", 12 txt2 = "t2" 13 }; 14 // db.Set<TestInfor>().Attach(tInfor); //特別注意Add方法前不需要進行Attach狀態的附加,當然附加了也不會出錯. 15 db.Set<TestInfor>().Add(tInfor); 16 int n = db.SaveChanges(); 17 Console.WriteLine("數據作用條數:" + n); 18 } 19 using (DbContext db = new CodeFirstModel()) 20 { 21 Console.WriteLine("---------------------------2. AddRange()方法-------------------------------------"); 22 //監控數據庫SQL情況 23 //db.Database.Log += c => Console.WriteLine(c); 24 List<TestInfor> tList = new List<TestInfor>() 25 { 26 new TestInfor() 27 { 28 id = Guid.NewGuid().ToString("N"), 29 txt1 = "t11", 30 txt2 = "t22" 31 }, 32 new TestInfor() 33 { 34 id = Guid.NewGuid().ToString("N"), 35 txt1 = "t11", 36 txt2 = "t22" 37 }, 38 new TestInfor() 39 { 40 id = Guid.NewGuid().ToString("N"), 41 txt1 = "t11", 42 txt2 = "t22" 43 } 44 }; 45 //特別注意AddRange方法前不需要進行Attach狀態的附加,當然附加也不錯. 46 foreach (var item in tList) 47 { 48 db.Set<TestInfor>().Attach(item); 49 } 50 db.Set<TestInfor>().AddRange(tList); 51 int n = db.SaveChanges(); 52 Console.WriteLine("數據作用條數:" + n); 53 } 54 }
2. 刪除方法(先Attach-后Remove)
1 private static void Delete1() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. Remove()方法 (調用Attach狀態附加)-------------------------------------"); 6 //監控數據庫SQL情況 7 //db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = "11", //實際測試的時候要有這條id的數據才能去測試哦 11 }; 12 /* 13 * 特別注意1:Remove方法刪除必須調用Attach進行狀態的附加,如果不附加將報下面的錯誤。 14 * The object cannot be deleted because it was not found in the ObjectStateManager. 15 * 特別注意2:直接使用狀態的方式進行刪除,db.Entry(tInfor).State = EntityState.Deleted; 是不需要進行attach附加的 16 * 該種方式在后面進行測試講解 17 * 特別注意3:無論是Remove凡是還是直接狀態的方式,如果傳入的刪除的數據為空,會報錯拋異常 18 */ 19 20 db.Set<TestInfor>().Attach(tInfor); //如果注釋掉該句話,則報錯 21 db.Set<TestInfor>().Remove(tInfor); 22 23 int n = db.SaveChanges(); 24 Console.WriteLine("數據作用條數:" + n); 25 } 26 }
3. 刪除方法(先查詢→后Remove刪除)
1 private static void Delete2() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------3. Remove()方法 (調用Attach狀態附加)-------------------------------------"); 6 int n; 7 //監控數據庫SQL情況 8 //db.Database.Log += c => Console.WriteLine(c); 9 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault(); 10 /* 11 * 特別注意1:對於先查詢(即時查詢,查出來放到了EF的本地緩存里),后刪除,這種情況可以省略Attach狀態的附加。 12 * 因為查出來的數據已經放在EF的本地緩存里了,相當於已經附加了,無須再次附加(當然附加也不報錯) 13 */ 14 if (tInfor == null) 15 { 16 n = 0; 17 } 18 else 19 { 20 //db.Set<TestInfor>().Attach(tInfor); //對於先查詢(即時查詢,查出來放到了EF的本地緩存里),后刪除,這種情況省略該句話,仍然有效 21 db.Set<TestInfor>().Remove(tInfor); 22 n = db.SaveChanges(); 23 } 24 Console.WriteLine("數據作用條數:" + n); 25 } 26 }
4. 修改(AddOrUpdate)
1 private static void Update1() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. AddOrUpdate()方法-------------------------------------"); 6 Console.WriteLine("-------------------------測試增加和自己創建數據的修改情況-----------------------------"); 7 //監控數據庫SQL情況 8 // db.Database.Log += c => Console.WriteLine(c); 9 TestInfor tInfor = new TestInfor() 10 { 11 id = "123", 12 txt1 = "馬茹", 13 txt2 = "馬茹2" 14 }; 15 /* 16 特別注意AddOrUpdate方法前不需要進行Attach狀態的附加 17 * 如果是執行增加操作,不需要附加Attach,附加了Attach不受影響 18 * 如果是執行修改操作,不能附加Attach,附加了Attach將導致修改失效,saveChange為0 19 */ 20 //db.Set<TestInfor>().Attach(tInfor); 21 db.Set<TestInfor>().AddOrUpdate(tInfor); 22 int n = db.SaveChanges(); 23 Console.WriteLine("數據作用條數:" + n); 24 } 25 using (DbContext db = new CodeFirstModel()) 26 { 27 Console.WriteLine("-------------------------測試即時查詢出來的數據的修改情況-----------------------------"); 28 //監控數據庫SQL情況 29 // db.Database.Log += c => Console.WriteLine(c); 30 TestInfor tInfor =db.Set<TestInfor>().Where(u=>u.id=="123").FirstOrDefault(); 31 tInfor.txt1="ypf11"; 32 /* 33 即時查詢出來的數據,調用AddorUpdate方法執行修改操作 34 35 * 如果是執行修改操作,不需要進行Attach的附加,附加了Attach將導致修改失效,saveChange為0 36 */ 37 db.Set<TestInfor>().Attach(tInfor); 38 db.Set<TestInfor>().AddOrUpdate(tInfor); 39 int n = db.SaveChanges(); 40 Console.WriteLine("數據作用條數:" + n); 41 } 42 }
5. 修改(自己創建對象,然后attach附加→修改屬性值→SaveChanges)
1 private static void Update2() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. attach附加→修改屬性值→SaveChanges-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = "123" 11 }; 12 13 /* 14 特別注意1:該方式為自己創建對象(對象中必須要有主鍵值),然后通過attach附加,然后修改屬性值,最后保存SaveChange。可以實現修改操作. 15 特別注意2:該種方式如果實體為空,SaveChanges時將報錯. 16 */ 17 db.Set<TestInfor>().Attach(tInfor); 18 tInfor.txt1 = "ypf1"; 19 20 int n = db.SaveChanges(); 21 Console.WriteLine("數據作用條數:" + n); 22 } 23 }
6. 修改(即時查詢→修改屬性值→SaveChanges)
1 private static void Update3() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. 即時查詢→修改屬性值→SaveChangess-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "123").FirstOrDefault(); 9 10 /* 11 特別注意1:EF即時查詢出來一個對象(自動保存到本地緩存了),然后修改屬性值,最后保存SaveChange。可以實現修改操作. 12 特別注意2:該種方式如果實體為空,SaveChanges時將報錯. 13 */ 14 tInfor.txt1 = "ypf333"; 15 16 int n = db.SaveChanges(); 17 Console.WriteLine("數據作用條數:" + n); 18 } 19 }
7. 增加方法(EntityState.Added)
1 private static void ADD2() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. EntityState.Added-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = Guid.NewGuid().ToString("N"), 11 txt1 = "t1", 12 txt2 = "t2" 13 }; 14 db.Entry(tInfor).State = EntityState.Added; 15 int n = db.SaveChanges(); 16 Console.WriteLine("數據作用條數:" + n); 17 } 18 }
8. 刪除方法(EntityState.Deleted-自己創建的對象)
1 private static void Delete3() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------EntityState.Deleted-自己創建的對象-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = "122", 11 }; 12 db.Entry(tInfor).State = EntityState.Deleted; 13 int n = db.SaveChanges(); 14 Console.WriteLine("數據作用條數:" + n); 15 } 16 17 }
9. 刪除方法(EntityState.Deleted-即時查詢的對象)
1 private static void Delete4() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------EntityState.Deleted-即時查詢的對象-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "123").FirstOrDefault(); 9 db.Entry(tInfor).State = EntityState.Deleted; 10 int n = db.SaveChanges(); 11 Console.WriteLine("數據作用條數:" + n); 12 } 13 14 }
10. 修改(自己創建對象,然后Modified→SaveChanges)
1 private static void Update4() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. 自己創建對象,然后Modified→SaveChanges-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = new TestInfor() 9 { 10 id = "1", 11 txt1 = "ypf1", 12 txt2="ypf1" 13 }; 14 db.Entry(tInfor).State = EntityState.Modified; 15 int n = db.SaveChanges(); 16 Console.WriteLine("數據作用條數:" + n); 17 } 18 }
11. 修改(即時查詢→修改屬性值→然后Modified→SaveChanges)
1 private static void Update5() 2 { 3 using (DbContext db = new CodeFirstModel()) 4 { 5 Console.WriteLine("---------------------------1. 即時查詢→修改屬性值→SaveChanges-------------------------------------"); 6 //監控數據庫SQL情況 7 // db.Database.Log += c => Console.WriteLine(c); 8 TestInfor tInfor = db.Set<TestInfor>().Where(u => u.id == "2").FirstOrDefault(); 9 10 tInfor.txt1 = "ypf2"; 11 tInfor.txt2 = "ypf2"; 12 13 db.Entry(tInfor).State = EntityState.Modified; 14 int n = db.SaveChanges(); 15 Console.WriteLine("數據作用條數:" + n); 16 } 17 }
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,如需代碼請留下你的評論,加我QQ:604649488 (備注:評論的博客名)