前言

从表面去看待事物视线总有点被层层薄雾笼罩的感觉,当你静下心来思考并让指尖飞梭于键盘之上,终将会拨开浓雾见青天。这是我切身体验。

在EF关系配置中,我暂且将主体对象称作为父亲,而依赖对象称作为孩子,父亲与孩子关联的关系可能是必须的也可能是可选的,如果是必须的那么意味着孩子不能因没有父亲而独立存在,又如果父亲被删除了(即父亲与孩子的关系被隔离),那么孩子将变成留守儿童(即孤儿),所以当处在这种情况下时,那么孩子应该需要自动被删除。

话题

必须关系和可选关系

我们接下来就父亲与孩子的关联关系来进行删除的话题。

我们建立三个类,一个类是Student(学生类),一个类是Grade(成绩类),最后一个类是Flower(小红花类)。我们假设有如下场景:一个学生对应多门成绩,但一门成绩就属于一个学生,同时可能学生团队合作表现好,一朵小红花对应多个学生,但是这个小红花肯定只会被一个学生拿走也就只对应一个学生,也有可能没得到小红花。鉴于此,类建立如下:

    public class Student /*学生类*/{public int Id { get; set; }public string Name { get; set; }public int? FlowerId { get; set; }public virtual Flower Flower { get; set; }public virtual ICollection<Grade> Grades { get; set; }}public class Grade /*成绩类*/{public int Id { get; set; }public int Fraction { get; set; }  /*学生成绩*/public int StudentId { get; set; }public virtual Student Student { get; set; }}public class Flower  /*小红花类*/{public int Id { get; set; }public string Remark { get; set; } /*小红花描述*/public virtual ICollection<Student> Students { get; set; }}

通过上述描述,我们对应的映射如下:

学生映射:

    public class StudentMap : EntityTypeConfiguration<Student>{public StudentMap(){ToTable("Student");HasKey(key => key.Id);HasOptional(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId);}}

成绩映射:

    public class GradeMap: EntityTypeConfiguration<Grade>{public GradeMap(){ToTable("Grade");HasKey(p => p.Id);HasRequired(p => p.Student).WithMany(p => p.Grades).HasForeignKey(p => p.StudentId);}}

对于EF上下文建立,不再描述,不明白的话可以参见我前两篇文章。

对于我们上面的可选字段FlowerId生成数据库中也是可选的,如下:

我们插入数据如图:

现在我们进行如下操作:删除学生姓名为bob的

            using (var ctx = new EntityDbContext()){ctx.Set<Student>().Remove(ctx.Set<Student>().Single(p => p.Name == "bob"));}

删除后结果如下:

那么问题来了,为什么我删除学生名为bob的而相关成绩也删除了呢?

答案是在学生和成绩之间建立了一个级联删除,所以会自动进行删除,级联删除也就是当父亲被删除时,其孩子也会被删除,EF Code  First为什么会这样做呢?因为学生和成绩之间的关系是必须(Required)的。

EF Code First不仅在实体在进行了配置而且在数据库中进行了配置,因为那是至关重要的,如果级联删除存在于实体中,那么在数据库中也应该必须存在,如果这两者不能同步那么在数据库中会出现约束错误。

接下来我们通过Flower(小花)来简介删除学生姓名为bob的,因为其对应的Remark是so bad(坏学生):

ctx.Set<Flower>().Remove(ctx.Set<Flower>().Include(p => p.Students).Single(p => p.Remark == "so bad"));

结果如下:

那么问题来了,为什么没有删除学生bob呢?

答案就是外键属性FlowerId和导航属性Flower被设置成了空,所以学生bob不会被删除,因为EF Code First不会为可选的关系设置级联删除。

【注意】在此种情况下, 如果你加载学生集合列表到内存中,那么EF Code First会在保存之前将外键属性设置为空。

如果此时你想在可选关系上强制执行删除那就在映射中进行如下操作:

 HasOptional(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId).WillCascadeOnDelete(true);

接下来如果我进行学生与小花之间的映射进行如下修改:

 HasRequired(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId);

此时再来进行上述删除:  ctx.Set<Flower>().Remove(ctx.Set<Flower>().Include(p => p.Students).Single(p => p.Remark == "so bad"));  此时结果如下:

那么问题来了,为什么这样就能进行相应学生的删除了呢?

答案就是当你用上必须的关系(即Required)之后即使你设置外键属性可为空,但是当映射到数据库之后,它会将其映射为非空的外键字段(可以理解为关系映射比POCO实体手动设置优先级高)!不信看如下图:

小结

(1)当关系为可选(Optional)时此时外键属性和导航属性为空,不会进行级联删除,但是可以用 WillCascadeOnDelete 进行强制删除。

(2)当关系为必须(Required)时此时会内置进行级联删除即使外键属性为可空的类型,也就是说无需多此一举加上WillCascadeOnDelete来进行级联删除。

你是不是觉得关于删除就这么简单呢?那你就大错特错了,请继续看下文。

隔离关系

依然以上述为例,我们现在想象有这样一场景,bob的成绩太差每次都没及格,并且虽给了小红花但是评语写着so bad,这样放学回家如何向爸妈交代呢,至少将成绩考好点吧,于是它要求老师删除他不良的成绩并给其100分的好成绩。在此场景下,我们代码如下:

            using (var ctx = new EntityDbContext()){var stu = ctx.Set<Student>().Single(p => p.Name == "bob");stu.Grades.Remove(stu.Grades.OrderBy(p => p.Id).First(p => p.Student.Name == "bob"));stu.Grades.Add(new Grade() { Fraction = 100 });}

但结果是老师也是有心无力啊,出错了,如下:

因为成绩从导航属性集合中移出后,它变成孤立对象(外键为NULL),提交时,是因为外键约束而失败,异常提示,也显示外键不能为空!

所以此时我们能想到的办法就是直接将孩子进行删除或者通过重写SaveChanges找到并删除。

于是在保存之前我添加如下代码:

ctx.Set<Grade>().Local.Where(p => p.Student == null).ToList().ForEach(r => ctx.Set<Grade>().Remove(r));

重写SaveChanges

        public override int SaveChanges(){ctx.Set<Grade>().Local.Where(p => p.Student == null).ToList().ForEach(r => ctx.Set<Grade>().Remove(r));return base.SaveChanges();}

最后通过,数据成功进行添加,如图:

上述代码有如下四点意思

(1)使用DbSet.Local来访问当前通过上下文追踪的没有运行任何数据库查询并且未被删除的成绩实体

(2)过滤列表中每一个没有引用学生实体的数据

(3)通过一个过滤列表的副本,来避免枚举时修改一个Collection

(4)标记每个孤儿(成绩)为已删除

小结

(1)默认情况下,EF Code First认为空的外键属性其关系是可选的,而对于非空的外键属性其关系是必须的。必须关系同时配置了级联删除,以至于如果父亲被删除则其所有的孩子也将被删除。

(2)必须和可选的关系自然能通过Fluent API来进行改变或者Data Anotaions和级联删除能够用Fluent API来进行配置

(3)如果父亲已经被隔离,那么通过级联删除不会删除孩子。

EF 那些琐事儿

上述异常信息被EF团队称作为“概念上可空消息”,因为当一个关系被隔离,则其关系中的外键将被设置为空。然而,如果属性为非空,那么EF在概念上将其设置为空,但是实际上没这么做,所以“概念上可空消息”没有被保存到数据库中而是在异常中。

转载于:https://www.cnblogs.com/CreateMyself/p/4751994.html

EntityFramework之孩子删除(四)(你以为你真的懂了?)相关推荐

  1. “三次握手,四次挥手”你真的懂吗?

    来源:码农桃花源 解读:"拼多多"被薅的问题出在哪儿?损失将如何买单? 之前有推过一篇不错的干货<TCP之三次握手四次挥手>,前几天有兄弟投稿,开始还以为是同一篇,后经 ...

  2. 【原创】“三次握手,四次挥手”你真的懂吗?

    记得刚毕业找工作面试的时候,经常会被问到:你知道"3次握手,4次挥手"吗?这时候我会"胸有成竹"地"背诵"前期准备好的"答案&qu ...

  3. 第三次握手为什么没有序列号_“三次握手,四次挥手”你真的懂吗?

    记得刚毕业找工作面试的时候,经常会被问到:你知道"3次握手,4次挥手"吗?这时候我会"胸有成竹"地"背诵"前期准备好的"答案&qu ...

  4. 为什么envi镶嵌老是出错_孩子为什么不专注,家长真的了解吗?

    孩子为什么不专注,家长真的了解吗? 72变亲子学堂|一起来育儿 一.孩子真的是不够专注吗? 关于孩子注意力不集中.老是坐不住等问题,其中一部分是孩子无法集中注意力,还有一部分,则是因为家长不了解孩子的 ...

  5. 金三银四,你真的懂软件测试吗?

    所谓金山银四,又是一波求职月,不安的因素在悸动.测试行业也是如此,测试员都寻求更好的职业机会,软件测试岗同时也在做筛选,所谓优胜劣汰. 那么面临跳槽季,想在测试行业大展身手的你,真的懂软件测试嘛?小黑 ...

  6. 我上了一节小学AI课,发现四年级的小朋友不仅懂AI,还学会了写代码运行

    郭一璞 发自 崂山 量子位 报道 | 公众号 QbitAI "用最快的速度,从文件夹中所有的照片里找到这个人" 接到这个任务后,周涵迅速的启动了代码编辑器,熟练地输入了几行Pyth ...

  7. 【redis 四】一文搞懂redis持久化之RDB

    前言: redis持久化分为RDB和AOF,此篇博文着重讲解RDB方式的持久化.演示系统 centos7. 1.官网说明 地址:https://redis.io/topics/persistence ...

  8. 某程序员哀叹工资低:二本计算机毕业,四年前端开发,年包才四十万!薪资真的和学历挂钩吗?...

    ‍ ‍程序员的薪资和学历有关系吗?是不是学历越高薪资越高? 一个程序员发帖问大家:为什么大家的工资都那么高,毕业三五年就能达到年包六七十万?自己二本计算机专业,做了四年前端开发,如今年包四十多万就到顶 ...

  9. css实战手册第四版 pdf_你真的了解CSS继承吗?看完必跪

    也许你瞧不起以前的 css ,但是你不该再轻视眼下的 css .近年来 css 的变量系统已逐步得到各大浏览器厂商支持,自定义选择器等强势袭来,嵌套系统/模块系统也在路上...为了更好的掌握 css ...

最新文章

  1. linux 根目录下的子目录的意义
  2. java cache缓存_涨姿势:为什么Java中“1000==1000”为false,而”100==100“为true?
  3. 4 [面向对象]-三大特性:多态
  4. python调用java的jar包_python调用java的jar包报错127
  5. 每天一个JavaScript实例-动态省份选择城市
  6. SpringMVC项目 使用IDEA快速构建
  7. 编程语言在中国哪种职位最多_您使用最多的是哪种“古老”编程语言?
  8. redis 客户端_你在使用什么 Redis 客户端工具?
  9. 循序渐进学.Net Core Web Api开发系列【7】:项目发布到CentOS7
  10. 应广单片机mini-c中用指针实现数组数功能
  11. springboot使用xxl-job
  12. scrapy实战--爬取腾讯的招聘信息
  13. SEO文章优化,内容SEO优化,SEO文章内容优化
  14. 小米4调整系统分区刷入opengapps stock
  15. php微信摇一摇,怎样使用JS+H5实现微信摇一摇
  16. 服务器添加html扩展,html5 – 服务器可扩展性 – HTML 5 websockets和彗星
  17. I2C协议研读(三):仲裁和时钟同步
  18. 数字化门店| 旧衣回收店系统 | 智慧门店小程序开发教程
  19. Linux软硬链接和软硬限制
  20. 斗智斗勇之redis

热门文章

  1. 全民奇迹修复云服务器跨服,千万玩家抢线路 全民奇迹跨服那些事儿
  2. 多个数组间元素排列组合问题求解(Java实现)
  3. 《Linux系统调用:raise,abort》
  4. 一、初识 Packet Tracer
  5. 2022车工(技师)考试题库模拟考试平台操作
  6. 已解决SettingWithCopyWarning df.rename(columns={‘旧列名1’: ‘新列名1’, ‘旧列名2’: ‘新列名2’}, inplace=True)
  7. HBase优化之Apache Phoenix二级索引
  8. Java打印杨辉三角形/帕斯卡三角形(含注释)
  9. php7 502 bad gateway,502 bad gateway怎么解决
  10. java中如何gzip_Java如何压缩Gzip格式的文件?