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

Posted wyy1234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 NINSERT [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 NUPDATE [dbo].[Student]
SET [StudentName] = @0
WHERE @@ROWCOUNT > 0 AND [StudentID] = @1N@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

以上是关于EF6基础系列(十)---离线场景保存实体和实体图集的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

《Entity Framework 6 Recipes》中文翻译系列 -----第二章 实体数据建模基础之拆分实体到多表以及拆分表到多实体

如何仅保存/更新父实体而不将其子实体保存在 asp.net mvc 的 EF6 中?

EF框架学习---EF中的在线和离线场景