如何从闭包重建树层次结构 table (Entity Framework Core 5)
How to rebuild a tree hierarchy from a closure table (Entity Framework Core 5)
我很难为 dto 构建我的树层次结构。树层次结构被捕获在具有深度的闭包 table 中。闭包 table 是使用数据库中的触发器构建的,并没有关系映射到 EF 中的 Teams table。
这是我的基本 Class 结构:
public class Team
{
public Guid Id {get; set;}
public string Title {get; set;}
public Guid? ParentTeamId {get; set;}
public Team ParentTeam {get; set;}
}
public class TeamDto
{
public Guid Id {get; set;}
public string Title {get; set;}
public Guid? ParentTeamId {get; set;}
public ICollection<TeamDto> SubTeams {get; set;} = new List<TeamDto>();
}
闭包 table 的结构如下:
public class Closure
{
public Guid ParentId {get; set;}
public Guid ChildId {get; set;}
public int Depth {get; set;}
}
EF 定义如下:
public void Configure(EntityTypeBuilder<Team> builder)
{
builder.HasOne(p => p.ParentTeam)
.WithMany()
.HasForeignKey(k => k.ParentTeamId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
}
public void Configure(EntityTypeBuilder<Closure> builder)
{
builder.HasKey(aa => new {aa.ChildId, aa.ParentId});
}
在对此进行查询时,我能做的最好的事情就是在第一级填充 subTeams。我正在使用 AutoMapper,但我并没有执着于 AutoMapper 解决方案。
我获取与用户相关的团队列表的查询(已在 teamMemberships 列表中捕获,_mapper 是 AutoMapper 注入,_context 是我的数据库上下文注入)如下所示:
var teamList = await _context.Closures
.Where(x => teamMemberships.Contains(x.ChildId))
.Select(x => x.ParentId)
.ToListAsync();
var query = _context.Teams
.Where(d => teamList.Contains(d.Id) && x.ParentTeamId == null)
.Select(x => new TeamDto
{
Id = x.Id,
ParentTeamId = x.ParentTeamId,
Title = x.Title,
SubTeams = _mapper.Map<List<TeamDto>>(_context.Teams.Where(y => y.ParentTeamId == x.Id))
})
.AsQueryable();
我需要以某种方式递归地拉出子团队,以便在 Dto 中建立完整的树。我在想可能有一个引用闭包 table 的查询可以做到这一点。作为 json 发送的 dto 应如下所示:
[
{
"id": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"parentId": null,
"title": "Team 1",
"subTeams": [
{
"id": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"parentId": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"title": "Team 1.1",
"subTeams": [
{
"id": "251358d9-3c5a-43b5-91f1-c2e82778c50b",
"parentId": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"title": "Team 1.1.1",
"subTeams": []
},
{
"id": "4e27be0b-3a7b-4c8c-8039-2a18e1afcbd0",
"parentId": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"title": "Team 1.1.2",
"subTeams": []
}
]
},
{
"id": "2a4d56fc-00f0-4418-80a2-7d870c0ddee5",
"parentId": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"title": "Team 1.2",
"subTeams": []
}
]
},
...
]
非常感谢任何见解。
感谢 Gert Arnold 和其他人的回答。
我的解决方案确实涉及递归函数。我使用团队闭包 table 拉取了所有相关团队,然后从该拉取中在内存中构建了树。
// teamMemberships is a list of teamIds that are relevant to the user
// I pull all related teams to that list (because a user, for example is a member
// of Team 1 implicitly if they are a member of 1.1, but not explicitly) because
// I need all ancestors and descendants of the selected teams
var allRelatedTeams = await _context.TeamClosures
.Where(x => teamMemberships.Contains(x.ChildId) || teamMemberships.Contains(x.ParentId))
.Select(x => x.ParentId)
.ToListAsync();
var teams = _context.Teams
.Where(x => allRelatedTeams.Contains(x.Id))
.ProjectTo<TeamDto>(_mapper.ConfigurationProvider)
.AsQueryable();
var query = teams
.Where(x => x.ParentTeamId == null)
.AsQueryable();
foreach (var team in query)
{
ParseSubTeams(team, await teams.ToListAsync());
}
递归函数:
private void ParseSubTeams(TeamDto team, List<TeamDto> teamList)
{
var childTeams = teamList
.Where(x => _context.TeamClosures
.Where(y => y.ParentId == team.Id)
.Select(z => z.ChildId)
.ToList().Contains(x.Id))
.ToList();
foreach (var subTeam in childTeams)
{
if (subTeam.ParentTeamId == team.Id)
{
team.SubTeams.Add(_mapper.Map<TeamDto>(subTeam));
ParseSubTeams(subTeam, teamList);
}
}
}
我很难为 dto 构建我的树层次结构。树层次结构被捕获在具有深度的闭包 table 中。闭包 table 是使用数据库中的触发器构建的,并没有关系映射到 EF 中的 Teams table。
这是我的基本 Class 结构:
public class Team
{
public Guid Id {get; set;}
public string Title {get; set;}
public Guid? ParentTeamId {get; set;}
public Team ParentTeam {get; set;}
}
public class TeamDto
{
public Guid Id {get; set;}
public string Title {get; set;}
public Guid? ParentTeamId {get; set;}
public ICollection<TeamDto> SubTeams {get; set;} = new List<TeamDto>();
}
闭包 table 的结构如下:
public class Closure
{
public Guid ParentId {get; set;}
public Guid ChildId {get; set;}
public int Depth {get; set;}
}
EF 定义如下:
public void Configure(EntityTypeBuilder<Team> builder)
{
builder.HasOne(p => p.ParentTeam)
.WithMany()
.HasForeignKey(k => k.ParentTeamId)
.IsRequired(false)
.OnDelete(DeleteBehavior.Cascade);
}
public void Configure(EntityTypeBuilder<Closure> builder)
{
builder.HasKey(aa => new {aa.ChildId, aa.ParentId});
}
在对此进行查询时,我能做的最好的事情就是在第一级填充 subTeams。我正在使用 AutoMapper,但我并没有执着于 AutoMapper 解决方案。
我获取与用户相关的团队列表的查询(已在 teamMemberships 列表中捕获,_mapper 是 AutoMapper 注入,_context 是我的数据库上下文注入)如下所示:
var teamList = await _context.Closures
.Where(x => teamMemberships.Contains(x.ChildId))
.Select(x => x.ParentId)
.ToListAsync();
var query = _context.Teams
.Where(d => teamList.Contains(d.Id) && x.ParentTeamId == null)
.Select(x => new TeamDto
{
Id = x.Id,
ParentTeamId = x.ParentTeamId,
Title = x.Title,
SubTeams = _mapper.Map<List<TeamDto>>(_context.Teams.Where(y => y.ParentTeamId == x.Id))
})
.AsQueryable();
我需要以某种方式递归地拉出子团队,以便在 Dto 中建立完整的树。我在想可能有一个引用闭包 table 的查询可以做到这一点。作为 json 发送的 dto 应如下所示:
[
{
"id": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"parentId": null,
"title": "Team 1",
"subTeams": [
{
"id": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"parentId": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"title": "Team 1.1",
"subTeams": [
{
"id": "251358d9-3c5a-43b5-91f1-c2e82778c50b",
"parentId": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"title": "Team 1.1.1",
"subTeams": []
},
{
"id": "4e27be0b-3a7b-4c8c-8039-2a18e1afcbd0",
"parentId": "05a8d423-feb1-4ea6-9596-d26b18feaa9e",
"title": "Team 1.1.2",
"subTeams": []
}
]
},
{
"id": "2a4d56fc-00f0-4418-80a2-7d870c0ddee5",
"parentId": "e5500e84-21fb-4b5e-aa9b-a95e51d3dd05",
"title": "Team 1.2",
"subTeams": []
}
]
},
...
]
非常感谢任何见解。
感谢 Gert Arnold 和其他人的回答。
我的解决方案确实涉及递归函数。我使用团队闭包 table 拉取了所有相关团队,然后从该拉取中在内存中构建了树。
// teamMemberships is a list of teamIds that are relevant to the user
// I pull all related teams to that list (because a user, for example is a member
// of Team 1 implicitly if they are a member of 1.1, but not explicitly) because
// I need all ancestors and descendants of the selected teams
var allRelatedTeams = await _context.TeamClosures
.Where(x => teamMemberships.Contains(x.ChildId) || teamMemberships.Contains(x.ParentId))
.Select(x => x.ParentId)
.ToListAsync();
var teams = _context.Teams
.Where(x => allRelatedTeams.Contains(x.Id))
.ProjectTo<TeamDto>(_mapper.ConfigurationProvider)
.AsQueryable();
var query = teams
.Where(x => x.ParentTeamId == null)
.AsQueryable();
foreach (var team in query)
{
ParseSubTeams(team, await teams.ToListAsync());
}
递归函数:
private void ParseSubTeams(TeamDto team, List<TeamDto> teamList)
{
var childTeams = teamList
.Where(x => _context.TeamClosures
.Where(y => y.ParentId == team.Id)
.Select(z => z.ChildId)
.ToList().Contains(x.Id))
.ToList();
foreach (var subTeam in childTeams)
{
if (subTeam.ParentTeamId == team.Id)
{
team.SubTeams.Add(_mapper.Map<TeamDto>(subTeam));
ParseSubTeams(subTeam, teamList);
}
}
}