模型
描述
问题
解决
概述
在关系数据库中,表之间的关系(也称为关联)是通过外键定义的。外键 (FK) 是用于在两个表的数据之间建立并强制链接的一列或列组合。有三种关系类型:一对一、一对多和多对多。在一对多关系中,外键是在表示关系多端的表上定义的。多对多关系涉及定义第三个表(也称为接合或联接表),主键由来自两个相关表的外键组成。在一对一关系中,主键还用作外键,两个表都没有单独的外键列。
在实体框架中,实体可以通过关联(关系)与其他实体相关。每个关系都包含两端,它们描述关系中两个实体的实体类型以及类型的多重性(一、零或一、多)。关系可由引用约束控制,该引用约束描述了关系中的哪端为 Role 以及哪端为 Dependent,哪端为 Role 以及哪端为 Function
导航属性为在两个实体类型间导航关联提供了一种方式。针对对象参与到其中的每个关系,各对象均可以具有导航属性。使用导航属性,可以在两个方向上导航和管理关系,返回引用对象(如果多重性为一或者零或一)或集合(如果多重性为多)。也可以选择使用单向导航,在这种情况下,只对参与关系的一种而不是两种类型定义导航属性。
模型
public partial class SystemRole : BaseEntity { public override int Id { get; set; } public string RoleName { get; set; } /// <summary> /// 所属的部门ID /// </summary> public int? DepartmentID { get; set; } [NotMapped] public Department Department { get; set; } public string DepartmentName { get { if (Department != null) return Department.DepartmentName; return ""; } } public bool IsEnabled { get; set; } /// <summary> /// 该角色具备的功能操作集合。 /// </summary> public IList<SystemFunction> Functions { get; set; } } /// <summary> /// SystemRole和SystemFunction的关联表 /// </summary> public class SystemRoleFunction : BaseEntity { public override int Id { get; set; } public int RoleID { get; set; } public SystemRole Role { get; set; } public int FunctionID { get; set; } public SystemFunction Function { get; set; } } public class SystemFunction { public int Id { get; set; } public string FunctionCode { get; set; } public string FunctionName { get; set; } /// <summary> /// 定义的分组名称 /// </summary> public string GroupName { get; set; } public int DisplayOrder { get; set; } } public IList<SystemRole> GetAllRoles(bool enabledOnly = true, bool loadingFunctions = false) { var queryable = this._roleRepository.TableNoTracking.Include(t => t.Department); if (enabledOnly) queryable = queryable.Where(t => t.IsEnabled == true); var list = queryable.ToList(); if (loadingFunctions) { list.ForEach(r => { var roleFuncDbSet = this._dbContext.Set<SystemRoleFunction>(); var funcDbSet = this._dbContext.Set<SystemFunction>(); var funcs = from re in roleFuncDbSet join f in funcDbSet on re.FunctionID equals f.Id where re.RoleID == r.Id select f; r.Functions = funcs.ToList(); }); } return list; }
描述
模型关系:SystemRole 和SystemFuction多对多关系,通过SystemRoleFunction关联,关联字段RoleID、FunctionID;SystemRole与Department一对多关系,关联字段DepartmentID。
前提:在Web开发框架中我们并没有采用EF Fluent API使用EntityTypeConfiguration分文件配置Model映射关系,我们只在DBContext中为模型添加了DbSet。
问题
我们跑下代码,看下是否配置正确。发现以下两个错误:


解决
我们可以看到,都是两个外键惹的祸, 都是需要加载外键相关表的数据,但是都没有加载出来。我们看第一个的现象“SystemRoledId”无效, 从linq语句我们可以看出两个关联模型:SystemRoleFunction和SystemFunction , SystemFunction与SystemRole通过SystemRoleFunctiond的RoleID、FunctionID关联,这个错误说明模型中某个导航属性加载时没有找到相关的外键, 我们来瞧瞧是哪个模型,看哪个数据模型用了导航属性但是没有相关外键的,我们在上面的三个模型里找找看,哦,原来是SystemRole的这段public IList<SystemFunction> Functions { get; set; },Oh,My God,原来在这儿, 明白了,那么我们怎么解决呢,我们的外键是设在SystemRoleFunction这个关联表中,看来在这而添加外键不现实,看来我们只能取消这个导航属性了,把他作为一个数据集,由代码来给这个数据集赋值,在该代码上加上[NotMapped]的特性,运行后结果正常

好了,来看下我们的第二个错误现象,我也有红色字体注明了加上NotMapped后出现的错误,加上后我们就找不到外键关联的导航属性,故而报错,更深层次大家可以看EF相关源码是最直观的思考方式。我们去掉该特性后可以看到结果已经正常。