Entity Framework Core系列教程-18-断开模式下删除数据

唐啸
2023-12-01

Entity Framework Core 断开模式下删除数据

EF Core API会为EntityState为Deleted的实体建立并执行数据库中的DELETE语句。在EF Core中已连接和已断开连接的场景中删除实体没有什么区别。 EF Core使得从上下文中删除实体变得容易,而上下文又将使用以下方法删除数据库中的记录。

DbContext 方法DbSet 方法描述
DbContext.RemoveDbSet.Remove将指定的实体附加到状态为“已删除”的DbContext并开始对其进行跟踪
DbContext.RemoveRangDbSet.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。

 类似资料: