树状结构的部分查询
表结构是
public class RegionTree
{
/// <summary>
/// 自增长id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 自身的编码,不同层级依次添加编码
/// eg.湖北省为42,武汉市为4201,汉阳区为420105
/// </summary>
public string RegionCode { get; set; }
/// <summary>
/// 父级行政区编码(记录的是父级记录的RegionId)
/// </summary>
public string RegionParentCode { get; set; }
/// <summary>
/// 行政区名称
/// </summary>
public string Name {get; set; }
/// <summary>
/// 政区级别
/// </summary>
public AdministrativeLevelEnum AdministrativeLevel { get; set; }
}
业务要求并不是查询所有行政区域的数据结构,而是根据登录用户所属行政区域,返回其树状数据,即返回“部分”树,并且一个用户可能同时属于不同的行政区域
比如,用户属于阜新市
、站前区
、西市区
、东光县
、沧州高新技术产业开发区
,则返回的树状数据应该是
如图所示,同一省的不同市、同一市的不同区,数据要合并到一颗树上,这也是我感觉很绕的地方
返回的结果Dto RegionTreeNode
添加子节点集合
/// <summary>
/// 子节点
/// </summary>
public List<RegionTreeNode> Children { get; set; }
感觉查询做的比较复杂,这里mark一下
public async Task TreeQuery()
{
//根据几个节点id,查询对应部分树
var queryRegionIds = new List<string> { "130923", "210803", "130972", "1", "210802", "2109" };
var resultTree = new List<RegionTreeNode>();
var list = db.Regions.Where(x => queryRegionIds.Contains(x.RegionCode)).ToList();
//向上查询
foreach (var d in list)
{
if (d.AdministrativeLevel != AdministrativeLevelEnum.Province)
{
var tree = await GetTree(d);
var isExistTree = resultTree.Find(x => x.Id == tree.Id);
//目前不存在此树(此省级的数据)
if (isExistTree == null)
{
resultTree.Add(tree);
}
//存在此树,需要将当前数据合并到现有树的数据中
else
{
//查询两棵树的交汇点
var intersectionId = GetIntersection(tree, isExistTree);
var resultTreeIntersection = GetIntersectionChild(isExistTree, intersectionId);
var treeIntersection = GetIntersectionChild(tree, intersectionId);
//将新树的数据从交汇点开始,合并到现有树
foreach (var child in treeIntersection.Children)
{
resultTreeIntersection.Children.Add(child);
}
}
}
else
{
var node = ConvertToTreeNode(d);
resultTree.Add(node);
}
}
//打断点查看结果:resultTree
}
private async Task<RegionTreeNode> GetTree(RegionTree d)
{
RegionTreeNode tree = new RegionTreeNode();
List<RegionTree> list = new List<RegionTree>();
//向上查询树
var parent = await db.Regions.FirstOrDefaultAsync(x => x.RegionCode == d.RegionParentCode);
list.Add(d);
if (parent != null)
{
list.Add(parent);
}
int count = 0; //防止死循环
while (parent != null && count < 5)
{
parent = await db.Regions.FirstOrDefaultAsync(x => x.RegionCode == parent.RegionParentCode);
if (parent != null)
{
list.Add(parent);
}
count++;
}
//处理成树结构
tree = ConvertToTreeNode(list[list.Count - 1]);
tree.Children = new List<RegionTreeNode>();
var node = tree.Children;
for (int i = list.Count - 2; i >= 0; i--)
{
var t = ConvertToTreeNode(list[i]);
node.Add(t);
node[0].Children = new List<RegionTreeNode>();
node = node[0].Children;
}
return tree;
}
private RegionTreeNode ConvertToTreeNode(RegionTree regionTree)
{
//这里可以写成automap,懒得写了~
return new RegionTreeNode()
{
Id = regionTree.Id,
RegionCode = regionTree.RegionCode,
RegionParentCode = regionTree.RegionParentCode,
AdministrativeLevel = regionTree.AdministrativeLevel,
Name = regionTree.Name,
Children=new List<RegionTreeNode>()
};
}
/// <summary>
/// 查询两棵树的交汇点(最低的子节点)
/// </summary>
/// <param name="tree"></param>
/// <param name="isExistTree"></param>
/// <returns></returns>
private long GetIntersection(RegionTreeNode tree, RegionTreeNode isExistTree)
{
var treeCodeList = new List<RegionTreeNode>();
treeCodeList.Add(new RegionTreeNode() { Id = tree.Id, RegionCode = tree.RegionCode });
GetCodeList(tree, treeCodeList);
var isExistTreeCodeList = new List<RegionTreeNode>();
isExistTreeCodeList.Add(new RegionTreeNode() { Id = isExistTree.Id, RegionCode = isExistTree.RegionCode });
GetCodeList(isExistTree, isExistTreeCodeList);
//查询重复的,最大的code
var intersectCodeList = (from t in treeCodeList
from i in isExistTreeCodeList
where t.Id == i.Id
select t).ToList();
var target = new RegionTreeNode() { RegionCode = "" };
foreach (var code in intersectCodeList)
{
//不同层级依次添加编码
//eg.湖北省为42,武汉市为4201,汉阳区为420105
//这里寻找子节点
if (target.RegionCode.Length < code.RegionCode.Length)
{
target = code;
}
}
return target.Id;
}
private void GetCodeList(RegionTreeNode tree, List<RegionTreeNode> codeList)
{
foreach (var node in tree.Children)
{
codeList.Add(new RegionTreeNode() { Id = node.Id, RegionCode = node.RegionCode });
GetCodeList(node, codeList);
}
}
/// <summary>
/// 查询交汇点节点
/// </summary>
/// <param name="intersection"></param>
/// <returns></returns>
private RegionTreeNode GetIntersectionChild(RegionTreeNode tree, long intersection)
{
if (tree.Id == intersection)
{
return tree;
}
//保证节点遍历完
foreach (var node in tree.Children)
{
if (node.Id == intersection)
{
return node;
}
}
foreach (var node in tree.Children)
{
return GetIntersectionChild(node, intersection);
}
return null;
}
示例代码
感觉会有更好的解,如果有思路的话,可以留言给我哦
学习技术最好的文档就是【官方文档】,没有之一。
还有学习资料【Microsoft Learn】、【CSharp Learn】、【My Note】。
如果,你认为阅读这篇博客让你有些收获,不妨点击一下右下角的【推荐】按钮。
如果,你希望更容易地发现我的新博客,不妨点击一下【关注】。