这一节的内容是在离线场景中保存实体和实体图集html
在离线场景中,当咱们保存一个离线的实体图集或一个单独的离线实体时,咱们须要作两件事。首先,咱们要把实体附加到新的上下文中,让上下文了知道存在这些实体。其次,咱们须要手动设置每一个实体的EntityState,由于新的上下文不知道这些离线实体都通过了些什么操做,因此新的上下文不能自动地给实体添加EntityState。上一节咱们清楚了附加离线图集的方法,附加离线图集时默认添加的EntityState不必定合适,因此咱们就要执行第二步:给实体或实体图集添加合适的EntityState。sql
在离线场景中咱们保存一个实体时,最核心的问题:咱们须要知道一个实体是新建的仍是原本就存在的。只有知道了这个问题的答案,咱们才能给实体设置EntityState。若是实体主键值是0(主键是Int类型,默认值为0)咱们认为这个实体是新建的,给它的状态标记为Added;若是主键值大于0,那么咱们就认为这个实体是已经存在的,将它的状态标记为Modified。以下图所示:数据库
下边是一个栗子:ide
// 新建的离线实体 var student = new Student(){ StudentName = "Bill" }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = student.StudentId == 0? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
由于Student是新建的,Id默认为0,因此Student被标记为Added,在数据库中执行:post
exec sp_executesql N'INSERT [dbo].[Student]([StudentName], [StandardId]) VALUES (@0, NULL) SELECT [StudentID] FROM [dbo].[Student] WHERE @@ROWCOUNT > 0 AND [StudentID] = scope_identity(),@0='Bill'
一样的,若是一个实体的主键不是0,那么将它的状态被标记为Modified,一个栗子:学习
// 虽然是新建的,可是由于Id不是0,因此EF认为是已存在的 var student = new Student(){ StudentId = 1, StudentName = "Steve" }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = student.StudentId == 0? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
在数据库中执行以下代码,注意:若是数据库中没有StudentId 为1的记录时,会报异常spa
exec sp_executesql N'UPDATE [dbo].[Student] SET [StudentName] = @0 WHERE @@ROWCOUNT > 0 AND [StudentID] = @1'N'@0 varchar(50),@1 int',@0='Steve',@1=1
上边部分咱们学习了经过主键值来设置实体的状态,这里咱们将学习怎么去保存一个实体图集。设计
在离线场景中保存一个实体图集是一件比较复杂的事,咱们须要进行认真的设计。离线场景中保存实体图集最须要解决的问题是给实体图集中的每个实体添加标记适当的状态。可是怎么去设计呢?在下图中咱们能够看到新的Context根本不知道每一个实体的状态。code
咱们必须在执行SaveChange()方法前肯定每个实体的状态,下边介绍经过主键设置实体图集状态的方法htm
咱们能够经过主键来设置实体图集中每个实体的状态。如前边保存实体时介绍的,若是一个实体的主键是CLR数据类型的默认值(如int类型的默认值是0),那么咱们认为这个实体是新建的,能够将这个实体标记为Added,在数据库执行Insert命令;若是主键值不是CLR数据类型的默认值,咱们认为这个实体是已经存在了,标记为Modified,在数据库执行Update命令。
一个栗子:
var student = new Student() { //Root entity (没有主键值 Standard = new Standard() //Child entity (有主键值) { StandardId = 1, StandardName = "Grade 1" }, Courses = new List<Course>() { new Course(){ CourseName = "Machine Language" }, //Child entity (没有主键值) new Course(){ CourseId = 2 } //Child entity (有主键值) } }; using (var context = new SchoolDBEntities()) { //给实体图集中的全部实体的状态进行标记 context.Entry(student).State = student.StudentId == 0 ? EntityState.Added : EntityState.Modified; context.Entry(student.Standard).State = student.Standard.StandardId == 0 ? EntityState.Added : EntityState.Modified; foreach (var course in student.Courses) context.Entry(course).State = course.CourseId == 0 ? EntityState.Added : EntityState.Modified; context.SaveChanges(); }
在上边的栗子中,Student实体图集包含Standard和Course实体,context经过每一个实体的主键对该实体的状态进行标记。
在离线场景删除一个实体很简单,只需经过Entry()方法把实体的状态标记为Deleted便可,注:咱们在将离线实体附加到上下文提过,当父实体的状态是Deleted时,经过Entry()方法附加实体图集时,实体图集的全部子实体都为null,因此在执行SaveChange()进行数据库删除时,只删除父实体的记录!一个简单的栗子:
// 待删除的实体 var student = new Student(){ StudentId = 1 }; using (var context = new SchoolDBEntities()) { context.Entry(student).State = System.Data.Entity.EntityState.Deleted; context.SaveChanges(); }
在上边的例子中,Student实体的实例只有主键值,删除一个实体也只须要主键值就能够了。在数据库中执行以下代码:
delete [dbo].[Student] where ([StudentId] = @0)',N'@0 int',@0=1
EF系列目录连接:Entity Franmework系列教程汇总