引用关系
Entity Framework 中配置了对象之间的引用关系后,在查询数据的时候会非常方便。
但是很多老系统中都是没有显示指定外键的,大多是靠在子对象中加一个主对象的 ID 来解决问题。
这时候如果不配置一下的话,是根本无法实现两个对象之间的引用关系的。因为 Entity Framework 并不知道那一列是用来确定引用关系的。
所以,这章将会介绍它默认的规则,也会介绍如何自由地配置引用关系。
默认规则
当你不配置任何东西的时候,Entity Framework 默认会认为你的数据库是这样的:
- 如果你的实体中有另一个实体的集合类型导航属性,Code First 默认会认为它们是一对多的关系;
- 如果你的一个实体上有另一个实体的导航属性(不能两者都有对方的导航属性),Code First 也会默认认为它们是一对多的关系;
- 如果你的两个实体上都有对方的集合类型导航属性,Code First 默认会认为它们是多对多的关系;
- 如果你的两个实体上都有对方的导航属性,Code First 默认会认为它们是一对一的关系;
- 默认情况下,Code First 对外键和表名(多对多的关联表)都有要求。
综上:如果你是先建立实体再创建数据库,可以不用配置而使用默认规则;但是如果你是现有数据库,一般很难完全匹配,总会需要配置一些东西。
利用 Fluent API 来配置引用关系
Attribute 配置法(Data Annotations)无法 实现所有功能,建议使用 Fluent API 来实现,具体配置遵循一下标准:
Entity.HasMultiplicity.WithMultiplicity.Map(Option)
其中,Has[Multiplicity] 包含以下三种方法:
- HasOptional
- HasRequired
- HasMany
另外,With[Multiplicity] 同样也包含一下三种方法:
- WithOptional
- WithRequired
- WithMany
但是这到底怎么用呢?后面将会用一个实例来演示一下。
配置一对多与一对一关系
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual IList<Article> Articles { get; set; }
}
public class Article
{
public int ID { get; set; }
public string Name { get; set; }
public virtual User Owner { get; set; }
}
上述代码中有两个实体,它们是一对多的关系。
完整的配置代码如下:
public class TestContext : DbContext
{
public DbSet<User> UserSet { get { return Set<User>(); } }
public DbSet<Article> ArticleSet { get { return Set<Article>(); } }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder
.Configurations
.Add(new UserTypeConfiguration())
.Add(new ArticleTypeConfiguration());
base.OnModelCreating(modelBuilder);
}
}
public class UserTypeConfiguration : EntityTypeConfiguration<User>
{
public UserTypeConfiguration()
{
HasKey(u => u.ID);
Property(u => u.ID)
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(u => u.Articles)
.WithRequired(a => a.Owner)
.Map(x => x.MapKey("UserID"));
ToTable("User");
}
}
public class ArticleTypeConfiguration : EntityTypeConfiguration<Article>
{
public ArticleTypeConfiguration()
{
HasKey(a => a.ID);
Property(a => a.ID)
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
ToTable("Article");
}
}
别的都是基本配置,这里最关键的一段代码是:
HasMany(u => u.Articles)
.WithRequired(a => a.Owner)
.Map(x => x.MapKey("UserID"));
其实这里的语义很清晰,如果我把这段英文直接翻译成中文,我觉得可以是这样子的。
“我有许多的 Article,它有一个 Owner 且是必须的,外键被映射成了 UserID”
是不是很清晰的关系?另外,两个实体间的关系只要在任意一个实体上配置一次就行了,但是配置方法不同。
上面是配置在主对象 User 上的,如果配置在 Article 上,语句应该是这样写的:
HasRequired(a => a.Owner)
.WithMany(u => u.Articles)
.Map(x => x.MapKey("UserID"));
“我有一个 Owner 切是必须的,它有很多的 Article,外键被映射成了 UserID”
那一对一的关系怎么配置呢?
其实一对一不就是这样的吗:我有一个 XXX,它有一个 XXX,外键被映射成了 XXX。
HasRequired(a => a.Owner)
.WithOptional(u => u.Article)
.Map(x => x.MapKey("UserID"));
另外,HasOptional 和 HasRequired 有什么区别呢?区别就在于,这个外键(或非显示外键)是否允许为 Null。
配置多对多关系
多对多关系的语义非常简单:我有很多 XXX,它有很多XXX……
但是,最关键的就是这个 Map。
因为多对多的话,必须要配置一张映射表,具体的配置方法如下:
HasMany(a => a.Categories)
.WithMany(c => c.Articles)
.Map(x => x.ToTable("ArticleCategory")
.MapLeftKey("ArticleID")
.MapRightKey("CategoryID"));
这里在 Map 的时候,必须要配置关联左表的外键(或非显示外键)和关联右表的外键,还要指定关联表的名字。
总体而言,配置起来也非常简单!