在偶然开发中,导入的多条数据中,可能都存在同一个字段生成主外键关联子级数据的逻辑,此时循环去生成子级数据,会导致重复添加子级数据,有点绕吧,那就那实例说吧
如下:实现导入两条论文数据,论文中有起草人和所在单位,而起草人和所在单位是多对多关系,
这里讲下额外题,多表之间的关系:
- 一对一:外键+主键,主键和主键匹配(一对一关系的表会直接安排成一张表)
- 一对多,多对一:主键+外键
- 多对多:通过中间表连接两张或多张表直接的联系
所以在论文循环时,会遇到多个起草人关联的是同一个企业,企业名称一样,本该是一条数据,但是却关联了多次,且id也会不一样,就会导致在数据库插入起草人数据时,会关联插入多次同一个企业信息,这就导致了重复的企业数据
input.ForEach(s =>
{
s.DataSources = SystemConstants.StandardSource;
var enterprises = MapToEnterprises(s.Affiliations, companys, enterpriseList);
companys.AddRange(enterprises);
var talents = MapToTalents(s.Talents, enterprises, newTalents, exsitTalents);
newTalents.AddRange(talents);
var domain = new Standard().SetId(GuidGenerator.Create());
SetStandard(s, domain, talents, true);
domains.Add(domain);
//增加企业或创新平台成果关系
var platFormList = s.Affiliations.Where(m => m.EntityType == SystemConstants.innovationplatform).ToList();
var plats = MapToPlatforms(platFormList, platForms, innovationPlatforms);
innovationPlatforms.AddRange(plats);
platMappings.AddRange(ScientficResultAddEInnoList(domain.Id, enterprises, plats));
});
此处是之前的企业数据,由于这两条论文中的起草人都存在同一个人和所属企业,如上面的excel模板总起草人都有"河南大学-王清北"以及 所在单位”河南大学“,则就导致会在循环生成河南大学企业时会生成两遍
/// <summary>
/// 转化企业实体
/// </summary>
/// <param name="dtos"></param>
/// <param name="enterprises"></param>
/// <returns></returns>
private List<Enterprise> MapToEnterprises(List<SRInnoPlatEpriseDo> dtos, List<Enterprise> newEnterprises, List<Enterprise> enterprises)
{
var es = new List<Enterprise>();
var existEs = enterprises.Where(s => dtos.Select(d => d.Name).Contains(s.EnterpriseName)).ToList();
es.AddRange(existEs);
var newEs = dtos.Where(s => s.EntityType == SystemConstants.Enterprise && !enterprises.Select(e => e.EnterpriseName).Contains(s.Name))
.Select(s => new Enterprise(GuidGenerator.Create(), s.Name, false, "-", "-", SystemConstants.ImportSource)).ToList();
es.AddRange(newEs);
return es.Distinct().ToList();
}
/// <summary>
/// 转化人才实体
/// </summary>
/// <param name="dtos"></param>
/// <param name="enterprises"></param>
/// <param name="enterpriseList"></param>
/// <returns></returns>
private List<Talent> MapToTalents(List<TalentBaseDo> dtos, List<Enterprise> enterprises,
List<Talent> newExistTalents, List<Talent> existTalent = null)
{
List<Talent> talents = new List<Talent>();
dtos.ForEach(s =>
{
var items = s.Name.Split('-');
var enterprise = enterprises.FirstOrDefault(e => e.EnterpriseName == items[0]);
var talent = existTalent.FirstOrDefault(t => t.Name == items[1] && t.EnterpriseName == items[0]);
var newTalent = newExistTalents.FirstOrDefault(t => t.Name == items[1] && t.EnterpriseName == items[0]);
if (talent != null)
{
talents.Add(talent);
}
else if (newTalent != null)
{
talents.Add(newTalent);
}
else
{
var newTelent = new Talent().SetId(GuidGenerator.Create())
.SetName(items[1])
.SetEnterpriseName(items[0])
.SetJobTitle(string.Empty)
.SetArea(string.Empty)
.SetIsGreen(false);
newTelent.Enterprise = new List<Enterprise>();
newTelent.Enterprise.Add(enterprise);
talents.Add(newTelent);
}
});
return talents.Distinct().ToList();
}
最简单的解决方式就是每次循环记录下生成的企业信息插入到全局对象中,然后再在每次处理转化企业实体时,过滤下上次已生成的企业信息,直接用上次生成的企业信息,不再重新生成,
代码修改如下:
/// <summary>
/// 转化企业实体
/// </summary>
/// <param name="dtos"></param>
/// <param name="enterprises"></param>
/// <returns></returns>
private List<Enterprise> MapToEnterprises(List<SRInnoPlatEpriseDo> dtos, List<Enterprise> newEnterprises, List<Enterprise> enterprises)
{
var es = new List<Enterprise>();
var existEs = enterprises.Where(s => dtos.Select(d => d.Name).Contains(s.EnterpriseName)).ToList();
var newExistEs = newEnterprises.Where(s => dtos.Select(d => d.Name).Contains(s.EnterpriseName)).ToList();
es.AddRange(existEs);
es.AddRange(newExistEs);
var newEs = dtos.Where(s => s.EntityType == SystemConstants.Enterprise && !enterprises.Select(e => e.EnterpriseName).Contains(s.Name)
&& !newEnterprises.Select(e => e.EnterpriseName).Contains(s.Name))
.Select(s => new Enterprise(GuidGenerator.Create(), s.Name, false, "-", "-", SystemConstants.ImportSource)).ToList();
es.AddRange(newEs);
return es.Distinct().ToList();
}
经测试
虽然每个起草人都关联了新创建的id=1的这条还未真实插入到数据库的企业,abp中ef会自动判断只插入一条id=1的企业,第二条及之后的起草人都会关联这一条id=1的企业,而不会每次都重新插入,之前的方式因为同一个企业生成了多个id,导致每次插入起草人时,关联的子级企业也会重新再插入