欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Entity Framework Core关联删除

程序员文章站 2022-03-24 10:55:48
目录数据库关联删除行为定义实体fluent api 配置关联实体创建表结构ef core 关联实体删除行为总结deletebehavior.cascadedeletebehavior.setnulld...

关联删除通常是一个数据库术语,用于描述在删除行时允许自动触发删除关联行的特征;即当主表的数据行被删除时,自动将关联表中依赖的数据行进行删除,或者将外键更新为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语句如下:

Entity Framework Core关联删除

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

Entity Framework Core关联删除        

总结

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

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关联删除的资料请关注其它相关文章!