站点图标 江湖人士

Entity Framework Core无连接的场景删除数据

EF Core获取上下文SQL语句

EF Core获取上下文SQL语句

EF Core API在数据库中为EntityState已删除的实体构建并执行DELETE语句。删除EF Core中有连接和无连接的场景中的实体没有区别。EF Core可以轻松地从上下文中删除实体,而后者又将使用以下方法删除数据库中的记录。

DbContext方法DbSet方法描述
DbContext.RemoveDbSet.Remove将指定的实体附加到处于DbContext已删除状态并开始跟踪它。
DbContext.RemoveRangeDbSet.RemoveRange将实体的集合或数组附加到处于DbContext已删除状态并开始跟踪它们。

以下示例演示了在断开连接的方案中删除实体的不同方法。

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();
}

在上面的示例中,使用or 方法从上下文中删除Studnet具有valid 的实体。数据将从数据库中删除。上面的示例在数据库中执行以下delete命令:StudentIdRemove()RemoveRange()SaveChanges()

exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Students]
WHERE [StudentId] = @p0;
SELECT @@ROWCOUNT;
',N'@p0 int',@p0=1
go

备注:DbContext.Remove()DbContext.RemoveRange()方法在EF核心新引入使删除操作简便。

异常:

如果相应数据库表中不存在Remove()RemoveRange()方法中指定实体中的Key值,则EF Core将引发异常:以下示例将引发异常。

var student = new Student() {
    StudentId = 50
};

using (var context = new SchoolContext()) {
    context.Remove<Student>(student);
    context.SaveChanges();
}

在上面的示例中,数据库中不存在Studentwith StudentId = 50。因此,EF Core将抛出以下内容DbUpdateConcurrencyException

数据库操作预计会影响1行,但实际上会影响0行。自加载实体以来,数据可能已被修改或删除。

因此,您需要适当地处理上述异常,或者在删除之前确保数据库中存在具有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("Record does not exist in the database");
    }
    catch (Exception ex)
    {
        throw;
    }
}

删除多个记录

您可以使用DbContext.RemoveRange()or 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);
    // 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

删除相关数据

如果实体与其他实体(例如一对一或一对多)有关系,则删除根实体时删除相关数据取决于关系的配置方式。

例如,考虑到StudentGrade实体之间存在一对多的关系。特定的学生记录会很多GradeId。如果我们尝试删除Students数据库中具有相关记录的成绩,EF将引发参考完整性错误。要解决此问题,可以使用Fluent API定义参照约束操作选项。例如,您可以为关系配置级联删除选项,如下所示。

modelBuilder.Entity<Student>()
    .HasOne<Grade>(s => s.Grade)
    .WithMany(g => g.Students)
    .HasForeignKey(s => s.GradeId)
    .OnDelete(DeleteBehavior.Cascade);

现在,如果删除Grade实体,那么所有相关Student记录也将在数据库中删除。

有在EF核心提供的其他参照约束行动选项,如SetNullClientSetNullRestrict

退出移动版