第一節: 結合EF的本地緩存屬性來介紹【EF增刪改操作】的幾種形式


一. 背景

   說起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 (備注:評論的博客名)
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM