EF6基础系列(十)---离线场景保存实体和实体图集

离线场景保存和删除实体/实体图集

这一节的内容是在离线场景中保存实体和实体图集

在离线场景中,当我们保存一个离线的实体图集或一个单独的离线实体时,我们需要做两件事。首先,我们要把实体附加到新的上下文中,让上下文了知道存在这些实体。其次,我们需要手动设置每个实体的EntityState,因为新的上下文不知道这些离线实体都经过了些什么操作,所以新的上下文不能自动地给实体添加EntityState。上一节我们清楚了附加离线图集的方法,附加离线图集时默认添加的EntityState不一定合适,所以我们就要执行第二步:给实体或实体图集添加合适的EntityState。

1.离线场景中保存实体

在离线场景中我们保存一个实体时,最核心的问题:我们需要知道一个实体是新建的还是本来就存在的。只有知道了这个问题的答案,我们才能给实体设置EntityState。如果实体主键值是0(主键是Int类型,默认值为0)我们认为这个实体是新建的,给它的状态标记为Added;如果主键值大于0,那么我们就认为这个实体是已经存在的,将它的状态标记为Modified。如下图所示:

下边是一个栗子:

// 新建的离线实体
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,在数据库中执行:

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的记录时,会报异常

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

2.离线场景中保存实体图集

上边部分我们学习了通过主键值来设置实体的状态,这里我们将学习怎么去保存一个实体图集。

在离线场景中保存一个实体图集是一件比较复杂的事,我们需要进行认真的设计。离线场景中保存实体图集最需要解决的问题是给实体图集中的每一个实体添加标记适当的状态。但是怎么去设计呢?在下图中我们可以看到新的Context根本不知道每个实体的状态。

我们必须在执行SaveChange()方法前确定每一个实体的状态,下边介绍通过主键设置实体图集状态的方法

通过主键设置主题图集的状态

我们可以通过主键来设置实体图集中每一个实体的状态。如前边保存实体时介绍的,如果一个实体的主键是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通过每个实体的主键对该实体的状态进行标记。

3.离线场景删除实体/实体图集

在离线场景删除一个实体很简单,只需通过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

原文地址:https://www.cnblogs.com/wyy1234/p/9636023.html

时间: 2024-07-29 19:15:24

EF6基础系列(十)---离线场景保存实体和实体图集的相关文章

EF6基础系列(九)--- 附加离线实体图集到上下文

附加离线实体图集到上下文 这节主要内容是通过不同的方法将离线实体附加到上下文中. 在离线场景中,保存一个实体要略微困难一些.当我们保存一个离线的实体图集或一个单独的离线实体时,我们需要做两件事.首先,我们要把实体附加到新的上下文中,让上下文了知道存在这些实体.其次,我们需要手动设置每个实体的EntityState,因为新的上下文不知道这些离线实体都经过了些什么操作,所以新的上下文不能自动地给实体添加EntityState. 下图说明了此过程. 为了将离线实体附加到上下文,并为实体图中的每个实体设

EF6基础系列(十一)---EF6中的异步查询和异步保存

EF6中的异步查询和异步保存 在.NET4.5中介绍了异步操作,异步操作在EF中也很有用,在EF6中我们可以使用DbContext的实例进行异步查询和异步保存. 1.异步查询 下边是一个通过L2E语法实现异步查询的栗子: private static async Task<Student> GetStudent() { Student student = null; using (var context = new SchoolDBEntities()) { Console.WriteLine

EF6基础系列(五)---EF中的实体关系

这一节将总结EF是怎么管理实体之间的关系.EF与数据库一样支持三种关系类型:①一对一 ,②一对多,③多对多. 下边是一个SchoolDB数据库的实体数据模型,图中包含所有的实体和各个实体间的关系.通过设计器我们很容易看出实体间的对应关系 1.一对一 如上图,Student和StudentAddress具有一对一的关系(零或一).一个学生只能有一个或零个地址.实体框架将Student实体导航属性添加到StudentAddress实体中,将StudentAddress实体导航属性添加到Student

EF6基础系列(一)---什么是Entity Framework

什么是Entity Framework 1.EF的概念 在.NET3.5之前,我们经常编写ADO.NET代码或企业数据访问块来保存或检索底层数据库中的数据.做法是:打开过一个数据库的连接,创建一个DataSet来获取或提交数据到数据库,通过将DataSet中的数据和.NET对象相互转换来满足业务需求.这是一个麻烦且容易出错的过程.Microsoft提供了“Entity Framework”框架,用于自动地执行所有上述与数据库相关的活动.EF是一个适用于.NET开发的开源ORM框架.它使开发人员能

EF6基础系列(12)--- EF进行批量添加/删除

EF6添加了批量添加/删除实体集合的方法,我们可以使用DbSet.AddRange()方法将实体集合添加到上下文,同时实体集合中的每一个实体的状态都标记为Added,在执行SaveChange()方法时为每个实体执行Insert操作:同样的我们使用DbSet.RemoveRange()方法将集合中的所有实体都标记为deleted状态,在执行SaveChange()方法时为每一条数据执行delete操作. 通过AddRange()和RemoveRange()方法可以有效提升性能,所以建议在进行不批

EF6基础系列(九)---预先加载、延迟加载、显示加载

1.预先加载: 预先加载:在对一种类型的实体进行查询时,将相关的实体作为查询的一部分一起加载.预先加载可以使用Include()方法实现. 1.加载一个相关实体类型 栗子:使用Include()方法从数据库中获取所有学生及成绩级别.导航属性实现预先加载: using (var ctx = new SchoolDBEntities()) { var stud1 = ctx.Students .Include("Standard") .Where(s => s.StudentName

实体生命周期(EF基础系列8)

在我们进行增删查改操作之前,相当重要去理解实体的生命周期,它是怎么被EF操作的. 在实体的生命周期过程中,每个实体基于上下文的操作都会有一个实体状态,实体状态是一个枚举类型的值:System.Data.Entity.EntityState,包含下面的值: 1.Added 2.Deleted 3.Modified 4.Unchanged 5.Detached 数据上下文不仅包含所有从数据库中检索的对象的引用,并且它有这个实体对象的实体状态,维护修改实体的属性,这个特性叫做更改跟踪. 实体状态从“U

Bootstrap &lt;基础二十五&gt;警告(Alerts)

警告(Alerts)以及 Bootstrap 所提供的用于警告的 class.警告(Alerts)向用户提供了一种定义消息样式的方式.它们为典型的用户操作提供了上下文信息反馈. 您可以为警告框添加一个可选的关闭按钮.为了创建一个内联的可取消的警告框,请使用 警告(Alerts) jQuery 插件. 您可以通过创建一个 <div>,并向其添加一个 .alert class 和四个上下文 class(即 .alert-success..alert-info..alert-warning..ale

C#基础系列——Attribute特性使用

前言:上篇 C#基础系列--反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术--特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属性.方法等具有某些统一的特征,从而达到某些特殊的需要.比如:方法的异常捕捉,你是否还在某些可能出现异常的地方(例如数据库的操作.文件的操作等)经常使用try...catch.这个时候如果使用特性,就可以大大减少方法里面的try...catch的使用.你只需要定义一个专门捕捉异常的特性类Excepti