ORM的一个大核心就是实现表间的关系,总结一下EF的实现方式:
1. 一对多的关系:
1.1 隐喻的方式
public class Lodging { …
public Destination Destination { get; set; } [Timestamp] public byte[] RowVersion { get; set; } }
public class Destination { public int DestinationId { get; set; } [Required] public string Name { get; set; } ... public List<Lodging> Lodgings { get; set; }
}
注意此时外键的建立是可为空的
如果设定如下:
[Required]
public Destination Destination { get; set; }
1.2 Fluent API
在Context的OnModelCreating方法中:
modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithOptional(l => l.Destination);
在EntityTypeConfiguration的构造函数中:
HasRequired(l => l.Destination).WithMany(d => d.Lodgings).WillCascadeOnDelete(true);
这里说明一下:Has开头的一系列方法代表着实体主题所拥有的属性的约束,比如在modelBuilder.Entity<Destination>().HasMany(d => d.Lodgings).WithOptional(l => l.Destination);例子中,主题是Destination,里面有属性Lodings,HasMany定义了D里面有List的L。WithOption代表的是Lodging里面对于Destination字段的定义是可为空的,这些映射到数据库字段是否可为空有关。
另外,HasXXX和WithXXX的方法体返回可以理解为是对应Labmbada实体的对象,后面的点定义了对于该对象的操作。
2. 外键关系的声明
2.1 声明属性的方式
public class InternetSpecial { …
[ForeignKey("Accommodation")] public int AccommodationId { get; set; } public Lodging Accommodation { get; set; } }
2.2 命名方式
根据文档的介绍可以通过外键的命名方式来实现设置关联表的外键,但是在我的本地没有成功。
2.3 简洁方式
public class InternetSpecial { …
public Lodging Accommodation { get; set; }
}
简洁方式的一个问题是在赋值过程中可能需要你访问一次数据库来获取关联对象,但是像声明方式那种可以通过赋外键字段的方式来关联起来。
2.4 Fluent API
HasRequired(i => i.Accommodation).WithMany(l => l.InternetSpecials).HasForeignKey(i => i.AccommodationId);
这里Loding类中会有一个List<InternetSpecial>的类。对于1:1的关系,withMany将会被WithOptional和WithRequired替换。
3.级联删除
3.1 两个层次的级联删除
设定的关联关系看来是要声明WillCascadeOnDelete(true)来显示声明级联删除,否则的话默认是不级联删除。
级联删除将有两个层次的应用:
1.是应用层的级联删除,即内存中的级联;
2.数据库层面的级联。这个在DB端的设置是关联关系中的"Insert and Update Specification"中的Delete Rule:
3.2 应用实现
1: public class LodgingConfig : EntityTypeConfiguration<Lodging> {
2: public LodgingConfig() {
3: HasRequired(l => l.Destination).WithMany(d => d.Lodgings).WillCascadeOnDelete(true);
4: }
5: }
注意:级联删除是在WithMany返回的对象中设定的。默认的设定是非级联删除,如果想要设定,必须显示声明级联删除(设定true)。
4. Include的使用
var destination = context.Destinations
.Include("Lodgings")
.Single(d => d.DestinationId == destinationId);
var aLodging = destination.Lodgings.FirstOrDefault();
假如说Include没有声明,destination.Lodgings的值将会是null,var aLodging一行将会报错。
关联属性如果希望被连同一起实例化(勤劳而非懒惰的方式),那么好,使用Include函数。
说道Include其实关联到了级联删除,如果希望在逻辑层也一起实现级联删除,就以为你需要将所有的关联信息取出来,即需要Include,这个对于性能是一个挑战,所以需要考虑是否真的需要实现应用逻辑层的级联删除。