EF Core级联删除

前言

最近忙于.NET COERE WEB API的学习,遇到EFCORE中关于级联删除的知识,正好使用postman实践一下。

理论知识

(这块直接贴的官方文档,官方文档很清楚了)

概念

级联删除通常在数据库术语中用来描述一种允许在删除某行时自动触发删除相关行的特性。 EF Core 删除行为还介绍了一个密切相关的概念,即子实体与父实体的关系已断开时自动删除该子实体,这通常称为“删除孤立项”。

EF Core 实现多种不同的删除行为,并允许配置各个关系的删除行为。 EF Core 还实现基于关系的需求为每个关系自动配置有用的默认删除行为的约定。

删除行为

删除行为在 DeleteBehavior 枚举器类型中定义,并且可以传递到 OnDelete Fluent API 来控制是主体/父实体的删除还是依赖实体/子实体关系的断开会对依赖实体/子实体产生副作用。

删除主体/父实体或断开与子实体的关系时有三个 EF 可执行的操作:

  • 可以删除子项/依赖项
  • 子项的外键值可以设置为 null
  • 子项保持不变

 备注

仅当使用 EF Core 删除主体且将依赖实体加载到内存中(即对于跟踪的依赖项)时才应用 EF Core 模型中配置的删除行为。 需要在数据库中设置相应的级联行为以确保未由上下文跟踪的数据已应用必要的操作。 如果使用 EF Core 创建数据库,将为你设置此级联行为。

对于上述第二个操作,如果外键不可以为 null,则将外键值设置为 null 是无效的。 (不可为 null 的外键相当于必需关系。)在这些情况下,EF Core 会跟踪外键属性是否已被标记为 null,直到调用 SaveChanges,此时会引发异常,因为无法将更改永久保存到数据库中。 这类似于从数据库中获取约束冲突。

有四个删除行为,如下表中列出。

可选关系

对于可选关系(可以为 null 的外键),可以保存 null 外键值,从而产生以下影响:

可选关系
行为名称 对内存中的依赖项/子项的影响 对数据库中的依赖项/子项的影响
Cascade 删除实体 删除实体
ClientSetNull(默认) 外键属性设置为 null None
SetNull 外键属性设置为 null 外键属性设置为 null
Restrict None None

必选关系

对于必选关系(不可为 null 的外键),_不可以_保存 null 外键值,从而产生以下影响:

必选关系
行为名称 对内存中的依赖项/子项的影响 对数据库中的依赖项/子项的影响
Cascade(默认) 删除实体 删除实体
ClientSetNull SaveChanges 引发异常 None
SetNull 引发 SaveChanges SaveChanges 引发异常
Restrict None None

在上表中,“无” 可能会造成约束冲突。 例如,如果已删除主体/子实体,但不执行任何操作来更改依赖项/子项的外键,则由于发生外键约束冲突,数据库将可能会引发 SaveChanges。

高级别:

  • 如果实体在没有父项时不能存在,且希望 EF 负责自动删除子项,则使用“Cascade” 。
    • 在没有父项时不能存在的实体通常使用必选关系,其中“Cascade” 是默认值。
  • 如果实体可能有或可能没有父项,且希望 EF 负责为你将外键变为 null,则使用“ClientSetNull”
    • 在没有父项时可以存在的实体通常使用可选关系,其中“ClientSetNull” 是默认值。
    • 如果希望数据库即使在未加载子实体时也尝试将 null 值传播到子外键,则使用“SetNull” 。 但是,请注意,数据库必须支持此操作,并且如此配置数据库可能会导致其他限制,实际上这通常会使此选项不适用。 这就是SetNull不是默认值的原因。
  • 如果不希望 EF Core 始终自动删除实体或自动将外键变为 null,则使用“Restrict” 。 请注意,这要求使用代码手动同步子实体及其外键值,否则将引发约束异常。

 备注

在 EF Core(与 EF6 不同)中,不会立即产生级联影响,而是仅在调用 SaveChanges 时产生。

 备注

EF Core 2.0 中的更改: 在之前的版本中,“Restrict”将导致跟踪的依赖实体中的可选外键属性设置为 null,并且是可选关系的默认删除行为 。 在 EF Core 2.0 中,引入了“ClientSetNull” 以表示该行为,并且它会成为可选关系的默认值。 “Restrict” 的行为已调整为永远不会对依赖实体产生副作用

.NET CORE WEB API 验证示例

公司实体以及雇员实体

 public class Company
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Introduction { get; set; }
        public ICollection employees { get; set; }
    }
public class Employee
    {
        public Guid Id { get; set; }
        public Guid CompanyId { get; set; }

        public string EmployeeNo { get; set; }

        public string FristName { get; set; }
        public string LastName { get; set; }
        public Gender Gender { get; set; }

        public Company Company { get; set; }
    }

DBCONTEXT配置 

 modelBuilder.Entity().
                HasMany(x => x.employees).
                WithOne(x => x.Company).
                HasForeignKey(x => x.CompanyId).
                OnDelete(DeleteBehavior.Cascade);

数据库操作

 public void DeleteCompany(Company company)
        {
            if (company == null)
            {
                throw new ArgumentNullException(nameof(company));
            }
            _dbContext.Companies.Remove(company);
        }

API接口

   [HttpDelete("{companyId}")]
        public async Task DeleteCompany(Guid companyId)
        {
            if (companyId == Guid.Empty)
            {
                throw new ArgumentNullException(nameof(companyId));
            }
            var company = await _repository.GetCompanyAsnc(companyId);
            if (company == null)
            {
                return NotFound();
            }

            _repository.DeleteCompany(company);
            await _repository.SaveChangeAsnc();
            return NoContent();
        }

POSTMAN请求URL示例

https://localhost:5001/api/companies/conmpanyId

 

 

 

你可能感兴趣的:(API,SQL,Server)