Entity Framework Core关联删除

 更新时间:2021年12月29日 17:05:00   作者:Sweet Tang  
关联删除通常是一个数据库术语,用于描述在删除行时允许自动触发删除关联行的特征;即当主表的数据行被删除时,自动将关联表中依赖的数据行进行删除,或者将外键更新为NULL或默认值。本文将为大家具体介绍一下Entity Framework Core关联删除,需要的可以参考一下

关联删除通常是一个数据库术语,用于描述在删除行时允许自动触发删除关联行的特征;即当主表的数据行被删除时,自动将关联表中依赖的数据行进行删除,或者将外键更新为NULL或默认值。

数据库关联删除行为

我们先来看一看SQL Server中支持的行为。在创建外键约束时,可以指定关联表在主表删除行时,对依赖的数据如何执行操作。例如下面的SQL语句,[Order Details]表中[OrderID]字段 是外键,依赖于[Orders]表中的主键[OrderID]。

CREATE TABLE [Orders] (
    [OrderID] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    [OrderDate] datetime2 NULL,
    CONSTRAINT [PK_Orders] PRIMARY KEY ([OrderID])
);

GO

CREATE TABLE [Order Details] (
    [DetailId] int NOT NULL IDENTITY,
    [OrderID] int NULL,
    [ProductID] int NOT NULL,
    CONSTRAINT [PK_Order Details] PRIMARY KEY ([DetailId]),
    CONSTRAINT [FK_Order Details_Orders_OrderID] FOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) ON DELETE SET NULL
);

外键约束[FK_Order Details_Orders_OrderID]末尾的语句是ON DELETE SET NULL,表示当主表的数据行删除时,自动将关联表数据行的外键更新为NULL。

在SQL Server中支持如下四种行为:

1.ON DELETE NO ACTION

默认行为,删除主表数据行时,依赖表中的数据不会执行任何操作,此时会产生错误,并回滚DELETE语句。例如会产生下面的错误:

DELETE 语句与 REFERENCE 约束"FK_Order Details_Orders_OrderID"冲突。该冲突发生于数据库"Northwind_Test",表"dbo.Order Details", column 'OrderID'。

语句已终止。

2.ON DELETE CASCADE

删除主表数据行时,依赖表的中数据行也会同步删除。

3.ON DELETE SET NULL

删除主表数据行时,将依赖表中数据行的外键更新为NULL。为了满足此约束,目标表的外键列必须可为空值。

4.ON DELETE SET DEFAULT

删除主表数据行时,将依赖表的中数据行的外键更新为默认值。为了满足此约束,目标表的所有外键列必须具有默认值定义;如果外键可为空值,并且未显式设置默认值,则将使用NULL作为该列的隐式默认值。

简单介绍了数据库中行为后,我们来着重介绍 EF Core 中的关联实体的行为。

定义实体

我们先定义两个实体Order、OrderDetail分别表示订单和订单明细;其中Order与OrderDetail的关系是一对多,在OrderDetail实体中OrderID表示外键,依赖于Order实体中的主键OrderID。

    public class Order
    {
        public int OrderID { get; set; }

        public string Name { get; set; }

        public DateTime? OrderDate { get; set; }

        public ICollection<OrderDetail> OrderDetails { get; set; }
    }

    public class OrderDetail
    {
        public int DetailId { get; set; }

        public int? OrderID { get; set; }
        
        public int ProductID { get; set; }

        public Order Order { get; set; }
    }

Fluent API 配置关联实体

在DbContext中OnModelCreating方法中,我们使用 Fluent API 配置实体中之间的关系。

    public class NorthwindContext : DbContext
    {

        public virtual DbSet<Order> Orders { get; set; }
        public virtual DbSet<OrderDetail> OrderDetails { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Order>(
                builder =>
                {
                    builder.HasMany<OrderDetail>(e => e.OrderDetails).WithOne(e => e.Order).HasForeignKey(e => e.OrderID).OnDelete(DeleteBehavior.ClientSetNull);
                });
        }
    }

在OnDelete方法中,需要传递参数DeleteBehavior枚举,分别有如下四个值:

    public enum DeleteBehavior
    {
        Cascade,

        SetNull,

        ClientSetNull,

        Restrict
    }    

这四个枚举值的分别表示不同的行为,这也是我们今天的重点。

创建表结构

我们分别使用使用这这个枚举值,来创建数据表结构。

        [InlineData(DeleteBehavior.Cascade)]
        [InlineData(DeleteBehavior.SetNull)] 
        [InlineData(DeleteBehavior.ClientSetNull)]
        [InlineData(DeleteBehavior.Restrict)]
        [Theory]
        public void Create_Database(DeleteBehavior behavior)
        {
            using (var northwindContext = new NorthwindContext(behavior))
            {
                northwindContext.Database.EnsureDeleted();
                northwindContext.Database.EnsureCreated();
            }
        }
        

四个枚举值创建表的SQL语句类似如下,唯一区别在于创建外键约束[FK_Order Details_Orders_OrderID]中ON DELETE {}后面的语句。

CREATE TABLE [Orders] (
    [OrderID] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NULL,
    [OrderDate] datetime2 NULL,
    CONSTRAINT [PK_Orders] PRIMARY KEY ([OrderID])
);

GO

CREATE TABLE [Order Details] (
    [DetailId] int NOT NULL IDENTITY,
    [OrderID] int NOT NULL,
    [ProductID] int NOT NULL,
    CONSTRAINT [PK_Order Details] PRIMARY KEY ([DetailId]),
    CONSTRAINT [FK_Order Details_Orders_OrderID] FOREIGN KEY ([OrderID]) REFERENCES [Orders] ([OrderID]) ON DELETE CASCADE
);

四个枚举值分别对应的SQL语句如下:

EF Core 关联实体删除行为

我们分别通过枚举值与是否跟踪关联实体,进行代码测试,测试代码如下:

        [InlineData(DeleteBehavior.Cascade, true)]
        [InlineData(DeleteBehavior.Cascade, false)]
        [InlineData(DeleteBehavior.SetNull, true)]
        [InlineData(DeleteBehavior.SetNull, false)]
        [InlineData(DeleteBehavior.ClientSetNull, true)]
        [InlineData(DeleteBehavior.ClientSetNull, false)]
        [InlineData(DeleteBehavior.Restrict, true)]
        [InlineData(DeleteBehavior.Restrict, false)]

        [Theory]
        public void Execute(DeleteBehavior behavior, bool includeDetail)
        {
            using (var northwindContext = new NorthwindContext(behavior))
            {
                northwindContext.Database.EnsureDeleted();
                northwindContext.Database.EnsureCreated();
            }

            int orderId;
            int detailId;
            using (var northwindContext = new NorthwindContext(behavior))
            {
                var order = new Order {
                    Name = "Order1"
                };

                var orderDetail = new OrderDetail {
                    ProductID = 11
                };
                order.OrderDetails = new List<OrderDetail> {
                    orderDetail
                };


                northwindContext.Set<Order>().Add(order);
                                northwindContext.SaveChanges();

                orderId = order.OrderID;
                detailId = orderDetail.DetailId;
            }

            using (var northwindContext = new NorthwindContext(behavior))
            {
                var queryable = northwindContext.Set<Order>().Where(e => e.OrderID == orderId);
                if (includeDetail){
                    queryable = queryable.Include(e => e.OrderDetails);
                }

                var order = queryable.Single(); 
                northwindContext.Set<Order>().Remove(order);

                try
                {
                    northwindContext.SaveChanges();
                    DumpSql();
                }
                catch (Exception)
                {
                    DumpSql();
                    throw;
                }

            }

            using (var northwindContext = new NorthwindContext(behavior))
            {
                var orderDetail = northwindContext.Set<OrderDetail>().Find(detailId);
                if (behavior == DeleteBehavior.Cascade)
                {
                    Assert.Null(orderDetail);
                }
                else
                {
                    Assert.NotNull(orderDetail);
                }
            }
        }
         

        

总结

根据上面的测试结果,我们可以出得如下结论:

DeleteBehavior.Cascade

  • 如果关联实体未被跟踪,主实体的状态标记为删除,执行SaveChage时,在删除主表的数据的同时,通过数据库的行为删除关联表的数据行;
  • 如果关联实体已经被跟踪,将主实体的状态标记为删除时,关联实体的状态也会标记为删除,执行SaveChange时,先删除关联表的数据行,然后再删除主表的数据行;
  • 外键可以设置非空值、也可以设置为可为空值;
  • 关联实体可以不被跟踪。

DeleteBehavior.SetNull

  • 如果关联实体未被跟踪,主实体的状态标记为删除,执行SaveChage时,在删除主表的数据时,通过数据库的行为将关联表数据行的外键更新为NULL,;
  • 如果关联实体已经被跟踪,将主实体的状态标记为删除时,关联实体的外键会被设置为null,同时将关联实体的状态标记为修改,执行SaveChange时,先更新关联表的数据行 ,然后删除主表的数据行;
  • 因为要将外键更新为NULL,所以外键必须设置为可空字段;
  • 关联实体可以不被跟踪。

DeleteBehavior.ClientSetNull

  • 数据库不会执行任何行为;
  • 关联实体必须被跟踪,将主实体的状态标记为删除时,关联实体的外键被设置为null,同时将关联实体的状态标记为修改,执行SaveChange时,先更新关联表的数据行,然后删除主表的数据行(此时的行为与DeleteBehavior.SetNull一致);
  • 因为要将外键更新为NULL,所以外键必须设置为可空字段;
  • 关联实体必须被跟踪,否则保存数据时会抛出异常。

DeleteBehavior.Restrict

  • 框架不执行任何操作,由开发人员决定关联实体的行为,可以将关联实体的状态设置为删除,也可以将关联实体的外键设置为null;
  • 因为要修改关联实体的状态或外键的值,所以关联实体必须被跟踪。 

以上就是Entity Framework Core关联删除的详细内容,更多关于Entity Framework关联删除的资料请关注脚本之家其它相关文章!

相关文章

  • 判断WebBrowser浏览器网页加载完成的处理方法

    判断WebBrowser浏览器网页加载完成的处理方法

    .net 中通过 SqlConnection 连接 sql server,我们会发现第一次连接时总是很耗时,但后面连接就很快,这个其实和SqlConnection 的连接池机制有关,正确的理解这个连接池机制,有助于我们编写高效的数据库应用程序。
    2013-04-04
  • .NET或.NET Core Web APi基于tus协议实现断点续传的示例

    .NET或.NET Core Web APi基于tus协议实现断点续传的示例

    这篇文章主要介绍了.NET或.NET Core Web APi基于tus协议实现断点续传的示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • asp.net中Table生成Excel表格的方法

    asp.net中Table生成Excel表格的方法

    这篇文章主要介绍了asp.net中Table生成Excel表格的方法,可实现将html的table表格生成excel的功能,需要的朋友可以参考下
    2015-01-01
  • ASP.NET数据库编程之处理文件访问许可

    ASP.NET数据库编程之处理文件访问许可

    ASP.NET数据库编程之处理文件访问许可...
    2006-09-09
  • asp.net中将js的返回值赋给asp.net控件的小例子

    asp.net中将js的返回值赋给asp.net控件的小例子

    要做一个显示用户在线停留时间的功能,拖了一个label控件用于显示时间,而时间是通过js来实现的,现在要把js的返回值赋给label,方法如下:
    2013-03-03
  • .Net Core使用Coravel实现任务调度的完整步骤

    .Net Core使用Coravel实现任务调度的完整步骤

    最近在使用调度程序创建简单的服务,该服务将执行一些重复的IO操作,使用的是Coravel调度库,下面这篇文章主要给大家介绍了关于.Net Core使用Coravel实现任务调度的完整步骤,需要的朋友可以参考下
    2022-08-08
  • ASP.NET批量下载文件的方法

    ASP.NET批量下载文件的方法

    这篇文章主要介绍了ASP.NET批量下载文件的方法,实例汇总了常见的asp.net实现批量下载的方法,具有一定的实用价值,需要的朋友可以参考下
    2014-11-11
  • ASP.NET MVC处理文件上传的小例子

    ASP.NET MVC处理文件上传的小例子

    这篇文章介绍了ASP.NET MVC处理文件上传的小例子,有需要的朋友可以参考一下
    2013-10-10
  • WPF创建Prism应用程序

    WPF创建Prism应用程序

    这篇文章介绍了WPF创建Prism应用程序的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • .net core如何在网络高并发下提高JSON的处理效率详解

    .net core如何在网络高并发下提高JSON的处理效率详解

    这篇文章主要给大家介绍了关于.net core如何在网络高并发下提高JSON的处理效率的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用.net core具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-04-04

最新评论