第一节: 结合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 (备注:评论的博客名)