Entity Framework Core系列教程-16-断开模式下插入数据

在Entity Framework Core的断开模式下插入数据

您了解了如何在连接模式中保存数据。在这里,您将了解有关在断开模式中保存数据的信息。
在断开连接的方案中保存数据与在连接的方案中保存数据有些不同。在断开连接的情况下,
DbContext不知道断开连接的实体,因为在当前DbContext实例的范围之外添加或修改了实体。因此,您需要将断开连接的实体附加到具有适当EntityState的上下文,以便对数据库执行CUD(创建,更新,删除)操作。
下图说明了断开连接情况下的CUD操作:
在这里插入图片描述

根据上图,断开的实体(DbContext不会跟踪的实体)需要使用适当的EntityState附加到DbContext。例如,新实体的已添加状态,已编辑实体的已修改状态和已删除实体的已删除状态,这将在调用SaveChanges()方法时在数据库中产生INSERT,UPDATE或DELETE命令。

为了在断开连接的情况下使用Entity Framework Core将记录插入,更新或删除到DB表中,必须执行以下步骤:

  1. 使用适当的EntityState将实体附加到DbContext,例如添加,修改或删除
  2. 调用SaveChanges()方法

下面的示例演示如何使用上述步骤将新记录插入数据库:

//Disconnected entity
var std = new Student(){ Name = "Bill" };

using (var context = new SchoolContext())
{
    //1. 使用添加的EntityState将实体附加到上下文
    context.Add<Student>(std);
    //或以下内容也有效
    // context.Students.Add(std);
    // context.Entry<Student>(std).State = EntityState.Added;
    // context.Attach<Student>(std);
                  
    //2. 调用SaveChanges将新记录插入Student表
    context.SaveChanges();
}

在上面的示例中,std是Student实体的断开连接的实例。context.Add<Student>()方法将Student实体附加到具有添加状态的上下文。 SaveChanges()方法将生成并执行以下INSERT语句:

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Students] ([Name])
VALUES (@p0);
SELECT [StudentId]
FROM [Students]
WHERE @@ROWCOUNT = 1 AND [StudentId] = scope_identity();',N'@p0 nvarchar(4000), 
@p1 nvarchar(4000) ',@p0=N'Bill'
go

EF Core提供了多种添加状态为“Added”的实体的方法。在上面的示例中,context.Students.Add(std);, context.Entry<Student>(std).State = EntityState.Added; and context.Attach<Student>(std); 将导致与上述相同的INSERT语句。

EF Core提供了以下DbContext和DbSet方法,这些方法将断开连接的实体与添加的EntityState附加在一起,后者又将在数据库中执行INSERT语句。

DbContext 方法 DbSet 方法 描述
DbContext.Attach DbSet.Attach 将实体附加到DbContext。为其Key属性具有值的实体设置“不变”状态,为其Key属性为空或数据类型的默认值的实体设置“添加”状态
DbContext.Add DbSet.Add DbContext.Add DbSet.Add将具有附加状态的实体附加到DbContext
DbContext.AddRange DbSet.AddRange DbContext.AddRange DbSet.AddRange将实体集合附加到具有添加状态的DbContext
DbContext.Entry - DbContext.Entry-获取指定实体的EntityEntry,该实体提供对更改跟踪信息和操作的访问
DbContext.AddAsync DbSet.AddAsync DbContext.AddAsync DbSet.AddAsync异步方法,用于将实体附加到具有“已添加”状态的DbContext上,如果没有,则开始跟踪它。调用SaveChangesAsync()时,数据将插入数据库中
DbContext.AddRangeAsync DbSet.AddRangeAsync DbContext.AddRangeAsync DbSet.AddRangeAsync异步方法,用于一次性将多个实体附加到具有添加状态的DbContext上,如果没有,则开始跟踪它们。调用SaveChangesAsync()时,数据将插入数据库中

注意:上面的DbContext方法是在EF Core中引入的(它们在EF 6或更早版本中不可用)。 DbContext和DbSet方法都执行相同的操作。您使用哪一种取决于您的编码模式和偏好。

插入关系数据

在上一章中,我们学习了在两个实体之间创建一对一,一对多和多对多关系。Entity Framework API会插入相关实体中包含的所有关系数据。
使用DbContext.Add或DbSet.Add方法将相关实体添加到数据库。 Add方法将实体附加到上下文,并将“已添加”状态设置为ID(键)属性为空,空或数据类型默认值的实体图中的所有实体。考虑以下示例。

var stdAddress = new StudentAddress()
{
    City = "SFO",
    State = "CA",
    Country = "USA"
};

var std = new Student()
{
    Name = "Steve",
    Address = stdAddress
};
using (var context = new SchoolContext())
{
    // 将实体附加到具有添加状态的DbContext
    context.Add<Student>(std);

    // 调用SaveChanges将新记录插入Student表
    context.SaveChanges();
}

在上面的示例中,context.Add<Student>(std)添加了Student实体的实例。 EF Core API通过Student的引用导航属性到达StudentAddress实例,并将两个实体的EntityState标记为Added,这将在SaveChanges()上构建并执行以下两个INSERT命令。

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Students] ([Name])
VALUES (@p0);
SELECT [StudentId]
FROM [Students]
WHERE @@ROWCOUNT = 1 AND [StudentId] = scope_identity();',N'@p0 nvarchar(4000), 
@p1 nvarchar(4000) ',@p0=N'Steve'
go

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [StudentAddresses] ([Address], [City], [Country], [State], [StudentId])
VALUES (@p5, @p6, @p7, @p8, @p9);
SELECT [StudentAddressId]
FROM [StudentAddresses]
WHERE @@ROWCOUNT = 1 AND [StudentAddressId] = scope_identity();
',N'@p5 nvarchar(4000),@p6 nvarchar(4000),@p7 nvarchar(4000),@p8 nvarchar(4000),
@p9 int',@p5=NULL,@p6=N'SFO',@p7=N'USA',@p8=N'CA',@p9=1
Go

插入多条记录

使用DbContext.AddRange或DbSet.AddRange方法可一次性添加多个实体。您无需多次调用DbContext.Add方法。

AddRange 方法 描述
void AddRange(IEnumerable<Object> entities) void AddRange(IEnumerable<Object> entities)将具有相同或不同类型的实体的列表添加到具有“已添加”状态的DbContext中
void AddRange(param object[] entities) void void AddRange(param object[] entities)将具有相同或不同类型的实体的数组添加到具有“ Added”状态的DbContext中
void AddRangeAsync(IEnumerable<Object>, CancellationToken) void AddRangeAsync(IEnumerable<Object>, CancellationToken)异步方法,用于将相同或不同类型的实体列表添加到具有添加状态的DbContext中

以下示例演示了使用AddRange添加学生实体对象列表。

var studentList = new List<Student>() {
    new Student(){ Name = "Bill" },
    new Student(){ Name = "Steve" }
};

using (var context = new SchoolContext())
{
    context.AddRange(studentList);
    context.SaveChanges();
}

上面的示例将在Student表中插入两个新记录:
您还可以添加不同类型的实体的列表,如下所示:

var std1 = new Student(){ Name = "Bill" };

var std2 = new Student(){ Name = "Steve" };

var computer = new Course() { CourseName = "Computer Science" };

var entityList = new List<Object>() {
    std1,
    std2,
    computer
};

using (var context = new SchoolContext())
{                
    context.AddRange(entityList);

    // or 
    // context.AddRange(std1, std2, computer);

    context.SaveChanges();
}

在上面的示例中,entityList是List<Object>的一种。因此,它可以包含任何类型的实体。 AddRange()方法将所有指定的实体添加到上下文中,SaveChanges()将一次性构建并执行INSERT语句。
EF Core通过在一次数据库往返中对所有上述实体执行INSERT语句来提高性能。上面的示例将在数据库中执行以下语句。

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Courses] ([CourseName], [Description])
VALUES (@p0, @p1);
SELECT [CourseId]
FROM [Courses]
WHERE @@ROWCOUNT = 1 AND [CourseId] = scope_identity();

DECLARE @inserted1 TABLE ([StudentId] int, [_Position] [int]);
MERGE [Students] USING (
VALUES (@p2, 0),
(@p3, 1)) AS i ([Name], _Position) ON 1=0
WHEN NOT MATCHED THEN
INSERT ([Name])
VALUES (i.[Name])
OUTPUT INSERTED.[StudentId], i._Position
INTO @inserted1;

SELECT [t].[StudentId] FROM [Students] t
INNER JOIN @inserted1 i ON ([t].[StudentId] = [i].[StudentId])
ORDER BY [i].[_Position];
',N'@p0 nvarchar(4000),@p1 nvarchar(4000),@p2 nvarchar(4000),@p3 nvarchar(4000)',
@p0=N'Computer Science',@p1=NULL,@p2=N'Steve',@p3=N'Bill'
go

使用DbSet插入数据

如前所述,您可以使用DbSet来保存实体的实例,该实例将以与EF 6.x相同的方式转换为数据库中的INSERT / UPDATE / DELETE命令。
使用DbSet<TEntity>.Add()方法附加具有添加状态的实体,或使用DbSet<TEntity> .AddRange()方法附加具有添加状态的实体的集合,如下所示。

var std = new Student()
{
    Name = "Bill"
};

using (var context = new SchoolContext())
{
    context.Students.Add(std);

    // or
    // context.Students.Attach(std);

    context.SaveChanges();
}

在上面的示例中,context.Students的类型为DbSet<Student>类型。因此,我们只能添加Student实体。 context.Students.Add(std)将Student实体附加到具有Added状态的上下文,当调用SaveChanges()方法时,这将导致INSERT语句。
在下一章中我们将学习如何更新断开连接的实体。

原文地址:https://www.cnblogs.com/AlexanderZhao/p/12878808.html