EF Core API会为EntityState为Deleted的实体建立并执行数据库中的DELETE语句。在EF Core中已连接和已断开连接的场景中删除实体没有什么区别。 EF Core使得从上下文中删除实体变得容易,而上下文又将使用以下方法删除数据库中的记录。
DbContext 方法 | DbSet 方法 | 描述 |
---|---|---|
DbContext.Remove | DbSet.Remove | 将指定的实体附加到状态为“已删除”的DbContext并开始对其进行跟踪 |
DbContext.RemoveRang | DbSet.RemoveRange | 将实体的集合或数组附加到具有Deleted状态的DbContext并开始跟踪它们 |
以下示例演示了在断开连接的场景中删除实体的不同方法:
// entity to be deleted
var student = new Student() {
StudentId = 1
};
using (var context = new SchoolContext())
{
context.Remove<Student>(student);
// or the followings are also valid
// context.RemoveRange(student);
//context.Students.Remove(student);
//context.Students.RemoveRange(student);
//context.Attach<Student>(student).State = EntityState.Deleted;
//context.Entry<Student>(student).State = EntityState.Deleted;
context.SaveChanges();
}
在上面的示例中,使用Remove()或RemoveRange()方法从上下文中删除了具有有效StudentId的Studnet实体。数据将从SaveChanges()上的数据库中删除。上面的示例在数据库中执行以下delete命令:
exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Students]
WHERE [StudentId] = @p0;
SELECT @@ROWCOUNT;
',N'@p0 int',@p0=1
go
注意:EF Core中新引入了DbContext.Remove()和DbContext.RemoveRange()方法,以简化删除操作。
如果相应数据库表中不存在Remove()或RemoveRange()方法中指定实体中的Key值,则EF Core将引发异常:以下示例将引发异常。
var student = new Student() {
StudentId = 50
};
using (var context = new SchoolContext()) {
context.Remove<Student>(student);
context.SaveChanges();
}
在上面的示例中,数据库中不存在StudentId = 50的Student。因此,EF Core将引发以下DbUpdateConcurrencyException:
Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.
数据库操作预期会影响1行,但实际上会影响0行。自加载实体以来,数据可能已被修改或删除。
因此,您需要适当地处理以上异常,或者在删除ID之前确保数据库中存在具有ID的对应数据。
var student = new Student() {
StudentId = 50
};
using (var context = new SchoolContext())
{
try
{
context.Remove<Student>(deleteStudent);
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
throw new Exception("数据库中不存在此纪录");
}
catch (Exception ex)
{
throw;
}
}
您可以使用DbContext.RemoveRange()或DbSet.RemoveRange()方法一次性删除多个实体。
IList<Student> students = new List<Student>() {
new Student(){ StudentId = 1 },
new Student(){ StudentId = 2 },
new Student(){ StudentId = 3 },
new Student(){ StudentId = 4 }
};
using (var context = new SchoolContext())
{
context.RemoveRange(students);
// or
// context.Students.RemoveRange(students);
context.SaveChanges();
}
上面的示例将在单个数据库行程中从数据库中删除4条记录。因此,EF Core改善了性能。
exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Students]
WHERE [StudentId] = @p0;
SELECT @@ROWCOUNT;
DELETE FROM [Students]
WHERE [StudentId] = @p1;
SELECT @@ROWCOUNT;
DELETE FROM [Students]
WHERE [StudentId] = @p2;
SELECT @@ROWCOUNT;
DELETE FROM [Students]
WHERE [StudentId] = @p3;
SELECT @@ROWCOUNT;
',N'@p0 int,@p1 int',@p0=1,@p1=2,@p2=3,@p3=4
go
如果一个实体与其他实体有一对一或一对多的关系,则在删除根实体时删除相关数据取决于如何配置该关系。
例如,考虑“学生”和“年级”实体之间存在一对多关系。特定的GradeId将有许多学生记录。如果我们尝试删除数据库中具有相关学生记录的成绩,EF将抛出参考完整性错误。要解决此问题,您可以使用Fluent API定义引用约束操作选项。例如,您可以为该关系配置一个级联删除选项,如下所示。
modelBuilder.Entity<Student>()
.HasOne<Grade>(s => s.Grade)
.WithMany(g => g.Students)
.HasForeignKey(s => s.GradeId)
.OnDelete(DeleteBehavior.Cascade);
现在,如果您删除成绩实体,那么所有相关的学生记录也将在数据库中删除。
EF Core中还有其他引用约束操作选项,例如SetNull,ClientSetNull和Restrict。