Entity Framework Core系列教程-16-断开模式下插入数据
在Entity Framework Core的断开模式下插入数据
您了解了如何在连接模式中保存数据。在这里,您将了解有关在断开模式中保存数据的信息。
在断开连接的方案中保存数据与在连接的方案中保存数据有些不同。在断开连接的情况下,
DbContext不知道断开连接的实体,因为在当前DbContext实例的范围之外添加或修改了实体。因此,您需要将断开连接的实体附加到具有适当EntityState的上下文,以便对数据库执行CUD(创建,更新,删除)操作。
下图说明了断开连接情况下的CUD操作:
根据上图,断开的实体(DbContext不会跟踪的实体)需要使用适当的EntityState附加到DbContext。例如,新实体的已添加状态,已编辑实体的已修改状态和已删除实体的已删除状态,这将在调用SaveChanges()方法时在数据库中产生INSERT,UPDATE或DELETE命令。
为了在断开连接的情况下使用Entity Framework Core将记录插入,更新或删除到DB表中,必须执行以下步骤:
- 使用适当的EntityState将实体附加到DbContext,例如添加,修改或删除
- 调用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语句。
在下一章中我们将学习如何更新断开连接的实体。