首页 > 代码库 > EntityFramework Fluent Api

EntityFramework Fluent Api

引用关系

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
最后的 Map 是用来映射外键(非显示外键也行)的。
 

但是这到底怎么用呢?后面将会用一个实例来演示一下。

 

配置一对多与一对一关系

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 的时候,必须要配置关联左表的外键(或非显示外键)和关联右表的外键,还要指定关联表的名字。

总体而言,配置起来也非常简单!

 

EntityFramework Fluent Api