一. 简介
我们在前面章节介绍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 }