领域驱动设计之model的关系及ef建模
本文的主要讲的是领域驱动设计中,领域模型(实体,聚合,值对象)间的关系,实际上更为准确的应该是面向对象中的类间的关系。
一、对象间的关系:
1、依赖:
唯物辨证法对事物间的联系做了如下论述:联系是指一切事物之间和事物内部各个要素之间的相互影响、相互制约和相互作用。联系是事物本身所固有的客观现象。世界上没有独立存在的事物,每一种事物都是和其他事物相联系而存在的,这是一切事物的客观本性。
同样的道理,软件系统中的每个类,都要和其他的类发生某种联系,才得以存在。我们用依赖来描述2个类之间的联系。
在系统中,如果一个类发生变化会引起另一个类发生变化则称这两个类之间存在(广义的)依赖关系。类间的依赖关系可以是结构性的(类之间的继承和关联),这种关系是长期的,也可以是行为性的(类的参数变化以及类之间消息传递机制等引起),这种关系是比较松散的,短期的。
2、关联:
关联是类之间建立的更持久的关系,或者说是必然性的关系需求。体现的是一种“拥有”的关系。表现在代码上,就是一个类包含另一个类的实例,通常表现为被关联类以类属性的形式出现在关联类的类定义中,也可以表现为关联类引用了一个类型为被关联类的全局变量。关联可以使单向的,也可以使双向的。关联是一种长期的行为。如:公司和员工的关系,主题和回帖
3、聚合:
聚合关系是关联关系的一种,是强的关联关系。聚合是整体和部分之间的关系。整体和部分没有生命周期的关联,整体的消亡不会影响部分,部分可以脱离整体存在。
4、组合:
组合关系是关联关系的一种,是比聚合关系强的关系,一种更强烈的整体和部分的关系(一般关联<聚合<组合)。整体和部分是不可分的。整体和部分有生命周期的关联,整体维护部分的生命周期,部分不能单独存在
二、关系数据库的关系
1、一对一
2、一对多
3、多对一 和一对多是一样的,只是分析的视角不一样
4、多对多
三、entity framework code first 建模策略
1、继承:
code first 对于继承的处理有3种方式
每个层次结构一张表 (TPH) 继承:
TPH 是指将继承层次结构的数据存储在一个表中,并使用鉴别器列来标识每行的类型。如果没有特殊设置 codefirst 默认以TPH方式处理。
每个类型一张表 (TPT) 继承:
TPT 将基本类型的所有属性都存储到一个表中。 派生类型的所有其他属性都将存储到独立的表中,这些独立的表具备指回基本表的外键。 TPT 映射使用 Map 调用指定基本表的名称,然后使用 Map<TEntityType> 为每个派生类型配置表。
每个具体类型一张表 (TPC) 继承:
TPC 将每个类型的数据存储到完全独立的表中,这些表之间不存在外键约束。 这种配置与 TPT 映射类似,区别是配置每种派生类型时,要用到“MapInheritedProperties”调用。
详细信息请参考:ADO.NET Entity Framework 4.1 中的代码优先
2、一对一
两个实体分别包含一个引用属性,Code First默认约定它们为一对一关系
在Code First中,一对一关系总是需要配置,因为两个实体都包含有一个引用属性,无法确定它们的主从关系。
配置一对一关系常用的方法:
HasRequired ,HasOptional ,WithOptional ,WithRequiredPrincipal,WithRequiredDependent
1: public class Person
2: {
3: public int PersonId { get; set; }
4: public int SocialSecurityNumber { get; set; }
5: public string FirstName { get; set; }
6: public string LastName { get; set; }
7: public byte[] RowVersion { get; set; }
8: public PersonPhoto Photo { get; set; }
9: }
10:
11: public class PersonPhoto
12: {
13: public int PersonId { get; set; }
14: public byte[] Photo { get; set; }
15: public string Caption { get; set; }
16: public Person PhotoOf { get; set; }
17: }
因为Photo是具体人的,所以PersonPhoto使用PersonId作为主键。
一对一关系配置的几种情况:
- 1、PersonPhoto必须属于一个Person,但是Person不一定有PersonPhoto,这种关系是1:0..1,此种情况下Person是一定存在的,所以它是主从关系主的一方。
HasRequired(t => t.PhotoOf).WithOptional(t => t.Photo);或HasOptional(t => t.Photo).WithRequired(t => t.PhotoOf);
- 2、PersonPhoto必须属于一个Person,Person也必须有PersonPhoto,这种关系式1:1,此种情况下,两个都一定存在,要确定主从关系,需要使用WithRequiredPrincipal或WithRequiredDependent。
HasRequired(t => t.PhotoOf).WithRequiredDependent(t => t.Photo);或HasRequired(t => t.Photo).WithRequiredPrincipal(t => t.PhotoOf);
具体配置:
1: public class Person
2: {
3: public int PersonId { get; set; }
4: public int SocialSecurityNumber { get; set; }
5: public string FirstName { get; set; }
6: public string LastName { get; set; }
7: public byte[] RowVersion { get; set; }
8: public PersonPhoto Photo { get; set; }
9: }
10:
11: public class PersonPhoto
12: {
13: public int PersonId { get; set; }
14: public byte[] Photo { get; set; }
15: public string Caption { get; set; }
16: public Person PhotoOf { get; set; }
17: }
18:
19: //配置Person
20: public class PersonConfiguration : EntityTypeConfiguration<Person>
21: {
22: public PersonConfiguration()
23: {
24: //主键
25: HasKey(t => t.PersonId);
26: //并发检查
27: Property(t => t.SocialSecurityNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).IsConcurrencyToken();
28: //长度50 不为空
29: Property(t => t.FirstName).IsRequired().HasMaxLength(50);
30: //长度50 不为空
31: Property(t => t.LastName).IsRequired().HasMaxLength(50);
32: //并发检查
33: Property(t => t.RowVersion).IsRowVersion();
34: //HasRequired(t => t.Photo).WithRequiredPrincipal(t => t.PhotoOf);
35: //HasOptional(t => t.Photo).WithRequired(t => t.PhotoOf);
36: }
37: }
38:
39: //配置PersonPhoto
40: public class PersonPhotoConfiguration : EntityTypeConfiguration<PersonPhoto>
41: {
42: public PersonPhotoConfiguration()
43: {
44: //主键
45: HasKey(t => t.PersonId);
46: //长度50
47: Property(t => t.Caption).HasMaxLength(50);
48: //必须从属于Person
49: HasRequired(t => t.PhotoOf).WithRequiredDependent(t => t.Photo);
50: }
51: }
52:
53: public class BreakAwayContext : DbContext
54: {
55: public DbSet<Person> People { get; set; }
56: public DbSet<PersonPhoto> Photos { get; set; }
57:
58: protected override void OnModelCreating(DbModelBuilder modelBuilder)
59: {
60: modelBuilder.Configurations.Add(new PersonConfiguration());
61: modelBuilder.Configurations.Add(new PersonPhotoConfiguration());
62: base.OnModelCreating(modelBuilder);
63: }
64: }
65:
66: public class Initializer : DropCreateDatabaseAlways<BreakAwayContext>
67: {
68: public Initializer()
69: {
70: }
71:
72: //创建数据库时 Seed数据
73: protected override void Seed(BreakAwayContext context)
74: {
75: context.People.Add(new Person()
76: {
77: FirstName = "E",
78: LastName = "F",
79: SocialSecurityNumber = 123456,
80: Photo = new PersonPhoto()
81: {
82: Caption = "这是照片",
83: Photo = new byte[] { }
84: }
85: });
86: context.SaveChanges();
87: }
88: }
以上例子来源于:【配置关系】—Entity Framework实例详解
3、一对多(多对一)
两个实体,如果一个实体包含一个引用属性,另一个实体包含一个集合属性,Code First默认约定它们为一对多关系。
一对多 多对多的 例子请参考【配置关系】—Entity Framework实例详解
4、多对多