EntityFramework:再谈 “如何映射聚合?”

背景

在之前的文章中《DDD:使用EntityFramework的话,如果只为聚合根设计仓储,其它实体如何处理?》,我介绍了如何映射聚合以保证其语义,当时的结论是:聚合内除了聚合根之外的实体必须使用多主键,否则删除操作(Order.OrderItems.Remove(1))只会将外键更新为 Null,最开始学习如何使用 EntityFramework 来映射聚合的时候,就纠结这个问题,当时汤雪华大哥就告诉了更新为 Null 就算删除了,当时感觉是接受了,不过没有内化,这篇文章也是为了内化这种思想。

使用多主键映射三级聚合

模型

 1     public class Level1
 2     {
 3         public virtual int Level1Id { get; set; }
 4 
 5         public virtual ICollection<Level2> Level2s { get; set; }
 6     }
 7 
 8     public class Level2
 9     {
10         public virtual int Level2Id { get; set; }
11         public virtual int Level1Id { get; set; }
12 
13         public virtual ICollection<Level3> Level3s { get; set; }
14     }
15 
16     public class Level3
17     {
18         public virtual int Level3Id { get; set; }
19         public virtual int Level2Id { get; set; }
20         public virtual int Level1Id { get; set; }
21     }

映射

 1             modelBuilder.Entity<Level1>()
 2                 .HasKey(x => x.Level1Id)
 3                 .Property(x => x.Level1Id)
 4                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
 5             modelBuilder.Entity<Level1>()
 6                 .HasMany(x => x.Level2s)
 7                 .WithRequired()
 8                 .HasForeignKey(x => x.Level1Id)
 9                 .WillCascadeOnDelete();
10 
11             modelBuilder.Entity<Level2>()
12                 .HasKey(x => new { x.Level2Id, x.Level1Id })
13                 .Property(x => x.Level2Id)
14                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
15             modelBuilder.Entity<Level2>()
16                 .HasMany(x => x.Level3s)
17                 .WithRequired()
18                 .HasForeignKey(x => new { x.Level2Id, x.Level1Id })
19                 .WillCascadeOnDelete();
20 
21             modelBuilder.Entity<Level3>()
22                 .HasKey(x => new { x.Level3Id, x.Level2Id, x.Level1Id })
23                 .Property(x => x.Level3Id)
24                 .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

测试添加

 1             for (var i = 1; i <= 2; i++)
 2             {
 3                 var l1 = new Level1
 4                 {
 5                     Level2s = new List<Level2>
 6                     { 
 7                         new Level2
 8                         {
 9                             Level3s = new List<Level3>
10                             {
11                                 new Level3()
12                             }
13                         }
14                     }
15                 };
16                 context.Level1s.Add(l1);
17                 context.SaveChanges();
18             }

测试删除

1             using (var context = new StudyContext())
2             {
3                 context.Level1s.First().Level2s.First().Level3s.Clear();
4 
5                 context.SaveChanges();
6             }

说明

因为采用了多主键,所以 context.Level1s.First().Level2s.First().Level3s.Clear() 会真正的生成 delete 语句,否则只会把外键更新为 null,但是多主键感觉真是不爽,两级还可以接受。

多主键映射这种风格可以导致“物理删除”,单主键这种风格可以导致“逻辑删除”。

如果是逻辑删除,需要注意哪些事项?

  1. 确定何时物理删除?如何物理删除?可以后台自动根据配置的元数据定时物理删除。
  2. 报表查询的时候注意查询语句。

为啥不用级联删除?

级联删除是解决:rep.delete(order),双主键是解决:order.OrderItems.Clear()。

 

备注

前几天我还暗自高兴找到了“物理删除”的映射方式,今天又更喜欢“逻辑删除”了,害苦了我一位兄弟,他都是用的 “双主键”。

 

posted on 2013-10-18 09:27  幸福框架  阅读(1688)  评论(3编辑  收藏  举报

导航

我要啦免费统计