mirror of
https://github.com/dotnetcore/FreeSql.git
synced 2026-02-04 07:20:51 +08:00
update
1
Home.md
1
Home.md
@@ -31,6 +31,7 @@ FreeSql是一个功能强大的 .NET ORM 功能库,支持 .NetFramework 4.0+
|
||||
* [延时加载](https://github.com/2881099/FreeSql/wiki/%e5%bb%b6%e6%97%b6%e5%8a%a0%e8%bd%bd)
|
||||
* [贪婪加载!!](https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd)
|
||||
* [LinqToSql](https://github.com/2881099/FreeSql/wiki/LinqToSql)
|
||||
* [查询父子关系]](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2%e7%88%b6%e5%ad%90%e5%85%b3%e7%b3%bb)
|
||||
* [仓储层Repository](https://github.com/2881099/FreeSql/wiki/Repository)
|
||||
* [UnitOfWork](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)
|
||||
* [联级保存](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* [延时加载](https://github.com/2881099/FreeSql/wiki/%e5%bb%b6%e6%97%b6%e5%8a%a0%e8%bd%bd)
|
||||
* [贪婪加载!!](https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd)
|
||||
* [LinqToSql](https://github.com/2881099/FreeSql/wiki/LinqToSql)
|
||||
* [查询父子关系]](https://github.com/2881099/FreeSql/wiki/%e6%9f%a5%e8%af%a2%e7%88%b6%e5%ad%90%e5%85%b3%e7%b3%bb)
|
||||
* [仓储层Repository](https://github.com/2881099/FreeSql/wiki/Repository)
|
||||
* [UnitOfWork](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)
|
||||
* [联级保存](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
|
||||
|
||||
199
查询父子关系.md
Normal file
199
查询父子关系.md
Normal file
@@ -0,0 +1,199 @@
|
||||
无限级分类(父子)是一种比较常用的表设计,每种设计方式突出优势的同时也带来缺陷,如:
|
||||
|
||||
- 方法1:表设计中只有 parent_id 字段,困扰:查询麻烦(本文可解决);
|
||||
- 方法2:表设计中冗余子级id便于查询,困扰:添加/更新/删除的时候需要重新计算;
|
||||
- 方法3:表设计中存储左右值编码,困扰:同上;
|
||||
|
||||
方法1设计最简单,本文解决它的递归查询问题,让使用透明化。
|
||||
|
||||
## 父子导航属性
|
||||
|
||||
FreeSql 支持的导航属性的中,有针对父子关系的设置方式,如下:
|
||||
|
||||
```csharp
|
||||
public class Area
|
||||
{
|
||||
[Column(IsPrimary = true)]
|
||||
public string Code { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public virtual string ParentCode { get; set; }
|
||||
|
||||
[Navigate(nameof(ParentCode))]
|
||||
public Area Parent { get; set; }
|
||||
[Navigate(nameof(ParentCode))]
|
||||
public List<Area> Childs { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
定义 Parent 属性,在表达式中可以这样:
|
||||
```csharp
|
||||
fsql.Select<Area>().Where(a => a.Parent.Parent.Parent.Name == "中国").First();
|
||||
```
|
||||
|
||||
定义 Childs 属性,在表达式中可以这样(子查询):
|
||||
```csharp
|
||||
fsql.Select<Area>().Where(a => a.Childs.AsSelect().Any(c => c.Name == "北京")).First();
|
||||
```
|
||||
|
||||
定义 Childs 属性,还可以使用[【级联保存】](https://github.com/dotnetcore/FreeSql/wiki/%E8%81%94%E7%BA%A7%E4%BF%9D%E5%AD%98)、[【贪婪加载】](https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd) 等等操作。
|
||||
|
||||
|
||||
添加测试数据:
|
||||
|
||||
```csharp
|
||||
fsql.Delete<Area>().Where("1=1").ExecuteAffrows();
|
||||
var repo = fsql.GetRepository<Area>();
|
||||
repo.DbContextOptions.EnableAddOrUpdateNavigateList = true;
|
||||
repo.DbContextOptions.NoneParameter = true;
|
||||
repo.Insert(new Area
|
||||
{
|
||||
Code = "100000",
|
||||
Name = "中国",
|
||||
Childs = new List<Area>(new[] {
|
||||
new Area
|
||||
{
|
||||
Code = "110000",
|
||||
Name = "北京",
|
||||
Childs = new List<Area>(new[] {
|
||||
new Area{ Code="110100", Name = "北京市" },
|
||||
new Area{ Code="110101", Name = "东城区" },
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
## 1、ToTreeList
|
||||
|
||||
配置好父子属性之后,就可以这样用了:
|
||||
|
||||
```csharp
|
||||
var t1 = fsql.Select<Area>().ToTreeList();
|
||||
Assert.Single(t1);
|
||||
Assert.Equal("100000", t1[0].Code);
|
||||
Assert.Single(t1[0].Childs);
|
||||
Assert.Equal("110000", t1[0].Childs[0].Code);
|
||||
Assert.Equal(2, t1[0].Childs[0].Childs.Count);
|
||||
Assert.Equal("110100", t1[0].Childs[0].Childs[0].Code);
|
||||
Assert.Equal("110101", t1[0].Childs[0].Childs[1].Code);
|
||||
```
|
||||
|
||||
查询数据本来是平面的,ToTreeList 方法将返回的平面数据加工为树型 List 返回。
|
||||
|
||||
## 2、AsTreeCte 递归查询
|
||||
|
||||
若不做数据冗余的无限级分类表设计,递归查询少不了,AsTreeCte 正是解决递归查询的封装,方法参数说明:
|
||||
|
||||
| 参数 | 描述 |
|
||||
| -- | -- |
|
||||
| (可选) pathSelector | 路径内容选择,可以设置查询返回:中国 -> 北京 -> 东城区 |
|
||||
| (可选) up | false(默认):由父级向子级的递归查询,true:由子级向父级的递归查询 |
|
||||
| (可选) pathSeparator | 设置 pathSelector 的连接内容,默认:-> |
|
||||
| (可选) level | 设置递归层级 |
|
||||
|
||||
|
||||
姿势一:AsTreeCte() + ToTreeList
|
||||
|
||||
```csharp
|
||||
var t2 = fsql.Select<Area>()
|
||||
.Where(a => a.Name == "中国")
|
||||
.AsTreeCte() //查询 中国 下的所有记录
|
||||
.OrderBy(a => a.Code)
|
||||
.ToTreeList(); //非必须,也可以使用 ToList(见姿势二)
|
||||
Assert.Single(t2);
|
||||
Assert.Equal("100000", t2[0].Code);
|
||||
Assert.Single(t2[0].Childs);
|
||||
Assert.Equal("110000", t2[0].Childs[0].Code);
|
||||
Assert.Equal(2, t2[0].Childs[0].Childs.Count);
|
||||
Assert.Equal("110100", t2[0].Childs[0].Childs[0].Code);
|
||||
Assert.Equal("110101", t2[0].Childs[0].Childs[1].Code);
|
||||
// WITH "as_tree_cte"
|
||||
// as
|
||||
// (
|
||||
// SELECT 0 as cte_level, a."Code", a."Name", a."ParentCode"
|
||||
// FROM "Area" a
|
||||
// WHERE (a."Name" = '中国')
|
||||
|
||||
// union all
|
||||
|
||||
// SELECT wct1.cte_level + 1 as cte_level, wct2."Code", wct2."Name", wct2."ParentCode"
|
||||
// FROM "as_tree_cte" wct1
|
||||
// INNER JOIN "Area" wct2 ON wct2."ParentCode" = wct1."Code"
|
||||
// )
|
||||
// SELECT a."Code", a."Name", a."ParentCode"
|
||||
// FROM "as_tree_cte" a
|
||||
// ORDER BY a."Code"
|
||||
```
|
||||
|
||||
姿势二:AsTreeCte() + ToList
|
||||
```csharp
|
||||
t3 = fsql.Select<Area>()
|
||||
.Where(a => a.Name == "中国")
|
||||
.AsTreeCte()
|
||||
.OrderBy(a => a.Code)
|
||||
.ToList();
|
||||
Assert.Equal(4, t3.Count);
|
||||
Assert.Equal("100000", t3[0].Code);
|
||||
Assert.Equal("110000", t3[1].Code);
|
||||
Assert.Equal("110100", t3[2].Code);
|
||||
Assert.Equal("110101", t3[3].Code);
|
||||
//执行的 SQL 与姿势一相同
|
||||
```
|
||||
|
||||
姿势三:AsTreeCte(pathSelector) + ToList
|
||||
|
||||
设置 pathSelector 参数后,如何返回隐藏字段?
|
||||
|
||||
```csharp
|
||||
t4 = fsql.Select<Area>()
|
||||
.Where(a => a.Name == "中国")
|
||||
.AsTreeCte(a => a.Name + "[" + a.Code + "]")
|
||||
.OrderBy(a => a.Code)
|
||||
.ToList(a => new {
|
||||
item = a,
|
||||
level = Convert.ToInt32("a.cte_level"),
|
||||
path = "a.cte_path"
|
||||
});
|
||||
Assert.Equal(4, t4.Count);
|
||||
Assert.Equal("100000", t4[0].item.Code);
|
||||
Assert.Equal("110000", t4[1].item.Code);
|
||||
Assert.Equal("110100", t4[2].item.Code);
|
||||
Assert.Equal("110101", t4[3].item.Code);
|
||||
Assert.Equal("中国[100000]", t4[0].path);
|
||||
Assert.Equal("中国[100000] -> 北京[110000]", t4[1].path);
|
||||
Assert.Equal("中国[100000] -> 北京[110000] -> 北京市[110100]", t4[2].path);
|
||||
Assert.Equal("中国[100000] -> 北京[110000] -> 东城区[110101]", t4[3].path);
|
||||
// WITH "as_tree_cte"
|
||||
// as
|
||||
// (
|
||||
// SELECT 0 as cte_level, a."Name" || '[' || a."Code" || ']' as cte_path, a."Code", a."Name", a."ParentCode"
|
||||
// FROM "Area" a
|
||||
// WHERE (a."Name" = '中国')
|
||||
|
||||
// union all
|
||||
|
||||
// SELECT wct1.cte_level + 1 as cte_level, wct1.cte_path || ' -> ' || wct2."Name" || '[' || wct2."Code" || ']' as cte_path, wct2."Code", wct2."Name", wct2."ParentCode"
|
||||
// FROM "as_tree_cte" wct1
|
||||
// INNER JOIN "Area" wct2 ON wct2."ParentCode" = wct1."Code"
|
||||
// )
|
||||
// SELECT a."Code" as1, a."Name" as2, a."ParentCode" as5, a.cte_level as6, a.cte_path as7
|
||||
// FROM "as_tree_cte" a
|
||||
// ORDER BY a."Code"
|
||||
```
|
||||
|
||||
> 更多姿势...请根据代码注释进行尝试
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [《多表查询》](https://github.com/2881099/FreeSql/wiki/%e5%a4%9a%e8%a1%a8%e6%9f%a5%e8%af%a2)
|
||||
- [《返回数据》](https://github.com/2881099/FreeSql/wiki/%e8%bf%94%e5%9b%9e%e6%95%b0%e6%8d%ae)
|
||||
- [《LinqToSql》](https://github.com/2881099/FreeSql/wiki/LinqToSql)
|
||||
- [《仓储层Repository》](https://github.com/2881099/FreeSql/wiki/Repository)
|
||||
- [《过滤器、全局过滤器》](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)
|
||||
- [《优化之:延时加载》](https://github.com/2881099/FreeSql/wiki/%e5%bb%b6%e6%97%b6%e5%8a%a0%e8%bd%bd)
|
||||
- [《优化之:贪婪加载》](https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd)
|
||||
- [《Expression 表达式函数》](https://github.com/2881099/FreeSql/wiki/%e8%a1%a8%e8%be%be%e5%bc%8f%e5%87%bd%e6%95%b0)
|
||||
- [《性能》](https://github.com/2881099/FreeSql/wiki/%e6%80%a7%e8%83%bd)
|
||||
- [《分区、分表、分库》](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e5%8c%ba%e5%88%86%e8%a1%a8)
|
||||
- [《租户》](https://github.com/2881099/FreeSql/wiki/%e7%a7%9f%e6%88%b7)
|
||||
Reference in New Issue
Block a user