EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关键在于搞清实体类的依赖关系,按此方法配置,快速高效合理。为了方便理解,我们使用简化的实体A和B以及A、B的配置类AMap和BMap,来演示如何正确配置实体类关系的过程。

public class A
{
public int Id { get; set; }
} public class B
{
public int Id { get; set; }
} public class AMap : EntityTypeConfiguration<A>
{
public AMap()
{
this.HasKey(o => o.Id);
}
} public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
}
}

实体类配置

一、确定依赖关系:

假设实体B依赖于实体A(B->A),那么实体B中存在对实体A的引用。

二、实体类配置应该写在哪里?

假设B依赖于A(B->A),很显然,我们希望的是B表中生成外键(A表的主键值)。以下两种方式都可以实现相同的表结构,但毫无疑问我们应该在B的配置文件BMap中进行关系配置。

(1)B依赖于A,A可以对B的存在一无所知。

(2)A可以单独存在,配置写在哪里都不会对A表产生影响。

(3)B对A的依赖是通过在B表中生成外键(A表的主键)。

推荐的写法:

public class BMap : EntityTypeConfiguration<B> { public BMap() { this.HasRequired(o => o.A).WithMany(o=>o.ListB); } }

摒弃的写法:

public class AMap : EntityTypeConfiguration<A> { public AMap() { this.HasMany(o => o.ListB).HasRequired(o => o.A); } }

依赖的方向决定了使用的配置,这在实体类数量和关系复杂时尤其重要,假设有10个实体类依赖A,混合书写配置显然不可取,而在被依赖实体中配置的结果会导致经常修改A的配置文件,你甚至不肯定修改了类A的配置文件是会引起A表的变化。

三、配置依赖关系

配置文件的基类EntityTypeConfiguration包含了一系列Has方法用来配置实体类,其中HasOptional和HasRequired根据实体的引用属性配置实体关系。假设B依赖于A(B->A),HasOptional允许B单独存在,这将在B表中生成可空的外键。HasRequired不允许B单独存在,这将在B表中生成非空的外键。

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasRequired(o => o.A);
}
}

HasRequired

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasOptional(o => o.A);
}
}

HasOptional

四、配置关联类型

HasOptional和HasRequired分别返回OptionalNavigationPropertyConfiguration和RequiredNavigationPropertyConfiguration对象,我们使用其中的WithMany和WithOptional来配置关联的类型。

如果A:B = 1:N,我们使用WithMany。

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasOptional(o => o.A).WithMany();
}
}

1:N(外键可空)

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasRequired(o => o.A).WithMany();
}
}

1:N(外键不可空)

如果A:B= 1:1,我们使用WithOptional。1:1的关联要求外键的非空和唯一,数据库是通过表B的外键作为主键来实现。

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasRequired(o => o.A).WithOptional();
}
}

1:1

四、可选导航属性

导航属性由关联类型决定,但其存在与否不会影响实体的依赖关系和关联类型。

对于B->A,如果A:B = 1:N,我们可以在A中添加ICollection<B>类型的导航属性,同时修改关系配置,将该属性传递给WithMany方法。

public class A
{
public int Id { get; set; } public ICollection<B> BList { get; set; }
} public class B
{
public int Id { get; set; } public A A { get; set; }
} public class AMap : EntityTypeConfiguration<A>
{
public AMap()
{
this.HasKey(o => o.Id);
}
} public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasOptional(o => o.A).WithMany(o => o.BList);
}
}

导航属性

如果A:B = 1:1,我们可以在A中添加B类型的导航属性,同时修改关系配置,将该属性传递给WithOptional方法。

public class A
{
public int Id { get; set; } public B B { get; set; }
} public class B
{
public int Id { get; set; } public A A { get; set; }
} public class AMap : EntityTypeConfiguration<A>
{
public AMap()
{
this.HasKey(o => o.Id);
}
} public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasRequired(o => o.A).WithOptional(o => o.B);
}
}

导航属性

五、显式外键属性

对于B->A,如果A:B = 1:1,外键就是主键。

如果A:B = 1:N,我们可以自定义导航属性对应的外键属性,首先在B中添加显式的用于外键的属性。

public class B
{
public int Id { get; set; } public A A { get; set; } //public int AId { get; set; }
public int? AId { get; set; }
}

显式外键

WithMany返回DependentNavigationPropertyConfiguration对象,我们使用该对象的HasForeignKey方法,如果实体联系配置为HasOptional,则需要使用可空类型匹配。

public class BMap : EntityTypeConfiguration<B>
{
public BMap()
{
this.HasKey(o => o.Id);
this.HasOptional(o => o.A).WithMany().HasForeignKey(o => o.AId);
}
}

外键配置

六、级联删除配置

HasForeignKey返回CascadableNavigationPropertyConfiguration对象,EF默认开启级联删除,当实体关系复杂导致无法开启级联删除时,我们使用该对象的WillCascadeOnDelete方法配置取消级联删除。

七、关于双向依赖

EF中实体的关联通过表的外键实现,1:N还是1:1都是通过外键实现。我们可以根据1:N配置的方式配置出双向依赖的表,但通常所谓的多对多都不是双向依赖。例如用户和角色、学生和课程、文章和标签等,甚至根本没有依赖,因为二者都可以独立存在,有的只是映射关系对二者的依赖,而这是1:N的问题。

我们使用EntityTypeConfiguration配置实体依赖,该类的ToTable、HasKey等实例方法都用于配置当前实体类映射的Table。HasRequired和HasOptional方法也会在对应的Table中生存外键,而HasMany方法则是其中的异类,偏偏配置的非当前实体类。

HasMany、WithMany除了在配置双向引用时替我们自动生成关系表,带来更多的是配置混乱。而所谓的自动生成关系表更是打破了我们实体类和Table的一一对应。在Microsoft.AspNet.Identity.EntityFramework 1.0中,我们可以看到IdentityUser和IdentityRole并没有通过双向引用自动生成关系表,而是定义了IdentityUserRole实体类用来映射:通过IdentityDbContext<TUser>的OnModelCreating配置我们可以看到虽然使用了HasMany配置TUser的Roles属性,但是完全可以在IdentityUserRole中配置。即使在2.0版本中依旧如此。

八、常见的配置举例

1.用户和角色:

(1)确定依赖关系:User和Role都可以单独存在,但UserRole不可以单独存在,因此存在的依赖是UserRole->User,UserRole->Role。

(2)配置依赖关系:UserRole不能单独存在,因此使用HasRequired。

(3)确定关联类型:User:UserRole==1:*;Role:UserRole=1:*,因此使用WithMany。

(4)显式的外键属性:在UserRole中添加UserId和RoleId作为显式的外键属性。

(5)可选的导航属性:在User和Role中添加ICollection<UserRole>类型的导航属性。

UserRole不应该存在重复的用户角色映射,因此使用外键作为联合主键。

public class User
{
public User()
{
this.UserRoles = new List<UserRole>();
} public int Id { get; set; } public string UserName { get; set; } public ICollection<UserRole> UserRoles { get; set; }
} public class Role
{
public Role()
{
this.UserRoles = new List<UserRole>();
} public int Id { get; set; } public string RoleName { get; set; } public ICollection<UserRole> UserRoles { get; set; }
} public class UserRole
{
public User User { get; set; } public int UserId { get; set; } public Role Role { get; set; } public int RoleId { get; set; }
} public class UserRoleMap : EntityTypeConfiguration<UserRole>
{
public UserRoleMap()
{
this.HasKey(o => new { o.UserId, o.RoleId });
this.HasRequired(o => o.User).WithMany(o => o.UserRoles).HasForeignKey(o => o.RoleId);
this.HasRequired(o => o.Role).WithMany(o => o.UserRoles).HasForeignKey(o => o.UserId);
}
}

UserRole

2.节点树:

(1)确定依赖关系:Category自依赖,Category->Category

(2)配置依赖关系:Category可以单独存在,因此使用HasOptional。

(3)确定关联类型:Category:Category==1:*,因此使用WithMany。

(4)显式的外键属性:在UserRole中添加ParentId,由于Category可以单独存在,ParentId为可空类型。

(5)可选的导航属性:在Category中添加ICollection<Category>类型的导航属性。

public class Category
{
public int Id { get; set; } public string Name { get; set; } public int? ParentId { get; set; } public Category Parent { get; set; } public ICollection<Category> Children { get; set; }
} public class CategoryMap : EntityTypeConfiguration<Category>
{
public CategoryMap()
{
this.HasKey(o => o.Id);
this.HasOptional(o => o.Parent).WithMany(o => o.Children).HasForeignKey(o => o.ParentId);
}
}

Category->Category

EntityFramework 系列:实体类配置-根据依赖配置关系和关联的更多相关文章

  1. Entity Framework 6 Code First 实践系列(1):实体类配置-根据依赖配置关系和关联

    EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关键在于搞清实体类的依赖关系,按此方法配置,快速高效合理.为了方便理解,我们使用简化的实体A和B以及A.B ...

  2. 【转】Entity Framework 6 Code First 实践系列(1):实体类配置-根据依赖配置关系和关联

    本文转自:http://www.cnblogs.com/easygame/p/3622893.html EF实体类的配置可以使用数据注释或Fluent API两种方式配置,Fluent API配置的关 ...

  3. Hibernate 由实体类与配置文件的配置关系生成数据库中的表

    import org.hibernate.cfg.Configuration; import org.hibernate.tool.hbm2ddl.SchemaExport; public class ...

  4. EntityFramework中实体类到表名的批量映射

    在使用EntityFramework做CodeFirst开发时,经常需要将实体类映射到数据库表,但是C#实体类和数据库表中的命名遵循的是不同的规范,这就需要为每个实体类做一个到数据库表名的映射.大多情 ...

  5. 【AutoMapper】实体类间自动实现映射关系,及其转换。

    官方项目下载: http://automapper.codeplex.com/ 博文 http://www.iteye.com/blogs/tag/AutoMapper 图解: 第一步:创建映射Map ...

  6. Mybatis系列(二):优化MyBatis配置文件中的配置和解决字段名与实体类属性名不相同的冲突

    原文链接:http://www.cnblogs.com/xdp-gacl/p/4264301.html     http://www.cnblogs.com/xdp-gacl/p/4264425.ht ...

  7. [开源] FreeSql.AdminLTE.Tools 根据实体类生成后台管理代码

    前言 FreeSql 发布至今已经有9个月,功能渐渐完善,自身的生态也逐步形成,早在几个月前写过一篇文章<ORM 开发环境之利器:MVC 中间件 FreeSql.AdminLTE>,您可以 ...

  8. 实体类和数据表的映射异常(XXX is not mapping[ ])

    在使用SSH框架开发过程,使用hibernate框架提供的工具类实现与数据库数据交互,在执行cmd操作时,如果出现以下异常: org.hibernate.hql.ast.QuerySyntaxExce ...

  9. Mybatis 使用Mybatis时实体类属性名和表中的字段名不一致

    开发中,实体类中的属性名和对应的表中的字段名不一定都是完全相同的,这样可能会导致用实体类接收返回的结果时导致查询到的结果无法映射到实体类的属性中,那么该如何解决这种字段名和实体类属性名不相同的冲突呢? ...

随机推荐

  1. COCI 2018/2019 CONTEST #2 Solution

    Problem1 Preokret 第一题一定不是什么难题. 第一个问题在读入的时候判断当前时间是不是在1440及以前就行 第二个问题考虑离线处理,由于每个时刻只能最多发生1个事件那么就弄个桶记录每一 ...

  2. 洛谷 P5108 仰望半月的夜空 解题报告

    P5108 仰望半月的夜空 题目描述 半月的夜空中,寄托了多少人与人之间的思念啊 曦月知道,这些思念会汇集成一个字符串\(S(n = |S|)\) 由于思念汇集的过于复杂,因此曦月希望提炼出所有的思念 ...

  3. 广二模拟赛 Problem A: 青春野狼不做理性小魔女的梦 解题报告

    Problem A: 青春野狼不做理性小魔女的梦 题意 给一个长为\(k\)的序列\(A\)和一个数\(n\),给出一部分\(A_i\)的值,另一部分为\(-1\),代表不知道这个\(A_i\)是多少 ...

  4. 【bzoj4516】 Sdoi2016—生成魔咒

    http://www.lydsy.com/JudgeOnline/problem.php?id=4516 (题目链接) 题意 依次向字符串末尾加上一个字符,每次求不同子串个数. Solution 如果 ...

  5. gdb调试遇到的问题

    解决方法:http://stackoverflow.com/questions/31062010/ubuntu-14-04-gcc-4-8-4-gdb-pretty-printing-doesnt-w ...

  6. 小贾漫谈——Java反射

    一.Class的API 二.测试使用的JavaBean class Admin{ //字段 public String userName; public String pwd; private int ...

  7. css原生变量var()

    了解css/css3原生变量var 阮一峰css变量教程 深入学习css自定义属性(css变量)

  8. bzoj千题计划228:bzoj2095: [Poi2010]Bridges

    http://www.lydsy.com/JudgeOnline/problem.php?id=2095 二分答案,判断是否存在混合图的欧拉回路 如果只有一个方向的风力<=mid,这条边就是单向 ...

  9. koa1.x获取原始body内容

    Node版本比较老,koa1.x配合koa-body-parser,默认koa-body-parser会把请求数据转成json对象, 然而有的时候需要获取原始的内容,不要转换,看波koa-body-p ...

  10. 《Linux命令行与shell脚本编程大全》23章24章

    第二十三章 使用其他shell bash shell是linux发行版中最广泛使用的shell.但是它并不是唯一的选择,还有其他的shell可以供你选择. 23.1 什么是dash shell 百度百 ...