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关联删除的资料请关注其它相关文章!
推荐阅读
-
详解如何在ASP.NET Core中应用Entity Framework
-
【ASP.NET Core学习】Entity Framework Core
-
Entity Framework Core 生成跟踪列-阴影属性
-
ASP.NET CORE系列【六】Entity Framework Core 之数据库迁移
-
ASP.NET CORE系列【三】使用Entity Framework Core进行增删改查
-
ASP.NET CORE系列【二】使用Entity Framework Core进行增删改查
-
.NET Core开发日志——Entity Framework与PostgreSQL
-
(摘)Entity Framework Core 2.1带来更好的SQL语句生成方案
-
[Abp 源码分析]七、仓储与 Entity Framework Core
-
Entity Framework (EF) Core工具创建一对多和多对多的关系