开发过程中经常用到导航属性,在这里总结一下导航属性的几种常用方式,总结的不全,只总结简单实用的方式,以下都是一对多关系。
两张表,分别是主表和详情表
第一种方式 反转导航属性
两个实体可以互相为导航属性
两张表的实体结构如下
主表实体类
public class CutterPart : ShareClass
{
/// <summary>
/// 加工部位编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 加工部位名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 工艺卡工序id
/// </summary>
public string CutterCardOperationId { get; set; }
/// <summary>
/// 寿命采集类型,枚举(1-加工件数(件),2-加工时间(min))
/// </summary>
public int GatherGroup { get; set; }
public List<TMSCutterPartDetail> TMSCutterPartDetails { get; set; }
}
详情表实体类
public class TMSCutterPartDetail : Entity<Guid>
{
/// <summary>
/// 加工部位id
/// </summary>
public Guid CutterPartId { get; set; }
public CutterPart CutterPart { get; set; }
/// <summary>
/// 刀具id
/// </summary>
public string ToolId { get; set; }
/// <summary>
/// 数量
/// </summary>
public int Quantity { get; set; }
}
public CutterPart CutterPart { get; set; }
这里要注意一下,注意实体类的命名,详情表的外键是根据实体类型CutterPart后面+Id自动生成的,跟字段名称CutterPart无关。
主表dto实体类
public class TMSCutterPartCreateUpdateDto
{
/// <summary>
/// 加工部位名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 工艺卡工序id
/// </summary>
public string CutterCardOperationId { get; set; }
/// <summary>
/// 寿命采集类型,枚举(1-加工件数(件),2-加工时间(min))
/// </summary>
public int GatherGroup { get; set; }
/// <summary>
/// 加工部位详情
/// </summary>
public List<TMSCutterPartDetailOutDto> TMSCutterPartDetails { get; set; }
}
映射关系中,要忽略详情字段,不然的话新增时就要主表、子表参数一起传了
//加工部位
CreateMap<TMSCutterPartCreateUpdateDto, CutterPart>()
.ForMember(d=>d.TMSCutterPartDetails,opt=>opt.Ignore());
CreateMap<CutterPart, TMSCutterPartOutDto>();
其他地方无需配置,查询的时候使用Include方法即可
var query = _tMSCutterPartRepository
.WhereIf(!string.IsNullOrEmpty(dto.Name), d => d.Name == dto.Name)
.Include(d => d.TMSCutterPartDetails)
;
第二种方式 只包含一个导航属性
第二种方式与第一种方式类似,无反向导航,无外键属性,详情表实体类中没有
public CutterPart CutterPart { get; set; }
public Guid CutterPartId { get; set; } 这个字段有没有都可以,如果有的话,要注意一下命名,这个字段的命名必须是主表实体类名+Id,不然的话会抛异常Unknown column 'b0.xxx' in 'field list'
其余与第一种方式都相同
主表实体类
public class CutterPart : ShareClass
{
/// <summary>
/// 加工部位编码
/// </summary>
public string Code { get; set; }
/// <summary>
/// 加工部位名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 工艺卡工序id
/// </summary>
public string CutterCardOperationId { get; set; }
/// <summary>
/// 寿命采集类型,枚举(1-加工件数(件),2-加工时间(min))
/// </summary>
public int GatherGroup { get; set; }
public List<TMSCutterPartDetail> TMSCutterPartDetails { get; set; }
}
详情表实体类
public class TMSCutterPartDetail : Entity<Guid>
{
/// <summary>
/// 刀具id
/// </summary>
public string ToolId { get; set; }
/// <summary>
/// 数量
/// </summary>
public int Quantity { get; set; }
}
主表dto实体类
public class TMSCutterPartCreateUpdateDto
{
/// <summary>
/// 加工部位名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 工艺卡工序id
/// </summary>
public string CutterCardOperationId { get; set; }
/// <summary>
/// 寿命采集类型,枚举(1-加工件数(件),2-加工时间(min))
/// </summary>
public int GatherGroup { get; set; }
/// <summary>
/// 加工部位详情
/// </summary>
public List<TMSCutterPartDetailOutDto> TMSCutterPartDetails { get; set; }
}
映射关系中,要忽略详情字段,不然的话新增时就要主表、子表参数一起传了
//加工部位
CreateMap<TMSCutterPartCreateUpdateDto, CutterPart>()
.ForMember(d=>d.TMSCutterPartDetails,opt=>opt.Ignore());
CreateMap<CutterPart, TMSCutterPartOutDto>();
其他地方无需配置,查询的时候使用Include方法即可
var query = _tMSCutterPartRepository
.WhereIf(!string.IsNullOrEmpty(dto.Name), d => d.Name == dto.Name)
.Include(d => d.TMSCutterPartDetails)
;
第三种方式
看到网上有ForeignKey,还有Fluent API,指定外键方式,但是我试了没起作用,依然会根据主表类名+Id自动生成外键,这样,如果表中的外键名称跟主表名称不一样,就会抛异常,先记录下这个问题,以后在研究,解决外键与主表名称不一致的问题。