- 增加 ISelect.ToTreeList 扩展方法查询数据,加工为树型 List;(注意:实体需要配置父子导航属性)

This commit is contained in:
28810 2020-03-15 18:33:15 +08:00
parent 529be7d9d2
commit 0effad75e4
7 changed files with 232 additions and 86 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<RootNamespace>FreeSql.Tests.VB</RootNamespace> <RootNamespace>FreeSql.Tests.VB</RootNamespace>
@ -15,6 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Extensions\FreeSql.Extensions.BaseEntity\FreeSql.Extensions.BaseEntity.csproj" />
<ProjectReference Include="..\Extensions\FreeSql.Extensions.LazyLoading\FreeSql.Extensions.LazyLoading.csproj" /> <ProjectReference Include="..\Extensions\FreeSql.Extensions.LazyLoading\FreeSql.Extensions.LazyLoading.csproj" />
<ProjectReference Include="..\FreeSql.DbContext\FreeSql.DbContext.csproj" /> <ProjectReference Include="..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
<ProjectReference Include="..\Providers\FreeSql.Provider.SqlServer\FreeSql.Provider.SqlServer.csproj" /> <ProjectReference Include="..\Providers\FreeSql.Provider.SqlServer\FreeSql.Provider.SqlServer.csproj" />

View File

@ -1,4 +1,5 @@
Imports System Imports System
Imports FreeSql.DataAnnotations
Imports Xunit Imports Xunit
Namespace FreeSql.Tests.VB Namespace FreeSql.Tests.VB
@ -38,6 +39,17 @@ Namespace FreeSql.Tests.VB
Dim List9 = g.sqlserver.Select(Of Testvb).IncludeMany(Function(a) a.Testvb2s).ToList() Dim List9 = g.sqlserver.Select(Of Testvb).IncludeMany(Function(a) a.Testvb2s).ToList()
BaseEntity.Initialization(g.sqlserver)
Dim cowR As CowRecord = New CowRecord
cowR.Id = 1
cowR.Lact = 1
cowR.VetCount = 1
cowR.Save()
cowR.VetCount += 1
cowR.Update()
End Sub End Sub
End Class End Class
@ -58,3 +70,52 @@ Class Testvb2
Property Testvb As Testvb Property Testvb As Testvb
Property Context As String Property Context As String
End Class End Class
<Index("uk_Primary", "Id,Lact", True)>
Public Class CowRecord
Inherits BaseEntity(Of CowRecord)
Private _Id As Integer
Private _Lact As Integer
Private _Pen As Integer
Private _BDAT As Date?
Private _FDAT As Date?
Private _DDAT As Date?
Private _EDAT As Date?
Private _ARDAT As Date?
Private _MKDAT As Date?
Private _BFDAT As Date?
Private _USDAT As Date?
Private _RC As Integer
Private _DMLK1 As Integer
Private _VetCount As Integer
<Column(IsPrimary:=True)>
Public Property Id As Integer
Get
Return _Id
End Get
Set(value As Integer)
_Id = value
End Set
End Property
<Column(IsPrimary:=True)>
Public Property Lact As Integer
Get
Return _Lact
End Get
Set(value As Integer)
_Lact = value
End Set
End Property
Public Property VetCount As Integer
Get
Return _VetCount
End Get
Set(value As Integer)
_VetCount = value
End Set
End Property
End Class

View File

@ -1,6 +1,7 @@
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Xunit; using Xunit;
namespace FreeSql.Tests namespace FreeSql.Tests
@ -318,6 +319,7 @@ namespace FreeSql.Tests
cts[1].Goodss.Clear(); cts[1].Goodss.Clear();
cts[1].Goodss.Add(new Goods { Name = "ÉÌÆ·55" }); cts[1].Goodss.Add(new Goods { Name = "ÉÌÆ·55" });
repo.Update(cts); repo.Update(cts);
} }
[Table(Name = "EAUNL_OTM_CT")] [Table(Name = "EAUNL_OTM_CT")]
class Cagetory class Cagetory
@ -390,6 +392,7 @@ namespace FreeSql.Tests
[Fact] [Fact]
public void EnableAddOrUpdateNavigateList_OneToMany_Parent() public void EnableAddOrUpdateNavigateList_OneToMany_Parent()
{ {
g.sqlite.Delete<CagetoryParent>().Where("1=1").ExecuteAffrows();
var repo = g.sqlite.GetRepository<CagetoryParent>(); var repo = g.sqlite.GetRepository<CagetoryParent>();
var cts = new[] { var cts = new[] {
new CagetoryParent new CagetoryParent
@ -412,9 +415,12 @@ namespace FreeSql.Tests
}) })
} }
}; };
repo.DbContextOptions.EnableAddOrUpdateNavigateList = false; //关闭级联保存功能 repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; //打开级联保存功能
repo.Insert(cts); repo.Insert(cts);
repo.SaveMany(cts[0], "Childs"); //指定保存 Childs 一对多属性
var treelist1 = repo.Select.ToTreeList();
//repo.SaveMany(cts[0], "Childs"); //指定保存 Childs 一对多属性
cts[0].Name = "·ÖÀà11"; cts[0].Name = "·ÖÀà11";
cts[0].Childs.Clear(); cts[0].Childs.Clear();
cts[1].Name = "·ÖÀà22"; cts[1].Name = "·ÖÀà22";
@ -427,6 +433,7 @@ namespace FreeSql.Tests
cts[1].Childs.Clear(); cts[1].Childs.Clear();
cts[1].Childs.Add(new CagetoryParent { Name = "·ÖÀà2_22" }); cts[1].Childs.Add(new CagetoryParent { Name = "·ÖÀà2_22" });
repo.Update(cts); repo.Update(cts);
var treelist2 = repo.Select.ToTreeList();
} }
[Table(Name = "EAUNL_OTMP_CT")] [Table(Name = "EAUNL_OTMP_CT")]
class CagetoryParent class CagetoryParent

View File

@ -1,5 +1,6 @@
using FreeSql; using FreeSql;
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
using FreeSql.Internal.CommonProvider;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -246,6 +247,61 @@ public static partial class FreeSqlGlobalExtensions
await select.SetListAsync(list); await select.SetListAsync(list);
return list; return list;
} }
#endif
#endregion
#region ToTreeList()
/// <summary>
/// 查询数据,加工为树型 List 返回<para></para>
/// 注意:实体需要配置父子导航属性
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <param name="that"></param>
/// <returns></returns>
public static List<T1> ToTreeList<T1>(this ISelect<T1> that) where T1 : class
{
var select = that as Select1Provider<T1>;
var tb = select._tables[0].Table;
var navs = tb.Properties.Select(a => tb.GetTableRef(a.Key, false))
.Where(a => a != null &&
a.RefType == FreeSql.Internal.Model.TableRefType.OneToMany &&
a.RefEntityType == tb.Type).ToArray();
if (navs.Length != 1) return select.ToList();
var list = select.ToList();
select._trackToList = null;
select._includeToList.Clear();
var navigateSelectorParamExp = select._tables[0].Parameter ?? Expression.Parameter(typeof(T1), select._tables[0].Alias);
var navigateSelector = Expression.Lambda<Func<T1, IEnumerable<T1>>>(Expression.MakeMemberAccess(navigateSelectorParamExp, navs[0].Property), navigateSelectorParamExp);
select.IncludeMany(navigateSelector);
select._includeManySubListOneToManyTempValue1 = list;
select.SetList(list);
return list.Except(list.SelectMany(a => FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityValueWithPropertyName(select._orm, tb.Type, a, navs[0].Property.Name) as IEnumerable<T1>)).ToList();
}
#if net40
#else
async public static System.Threading.Tasks.Task<List<T1>> ToTreeListAsync<T1>(this ISelect<T1> that) where T1 : class
{
var select = that as Select1Provider<T1>;
var tb = select._tables[0].Table;
var navs = tb.Properties.Select(a => tb.GetTableRef(a.Key, false))
.Where(a => a != null &&
a.RefType == FreeSql.Internal.Model.TableRefType.OneToMany &&
a.RefEntityType == tb.Type).ToArray();
if (navs.Length != 1) return await select.ToListAsync();
var list = await select.ToListAsync();
select._trackToList = null;
select._includeToList.Clear();
var navigateSelectorParamExp = select._tables[0].Parameter ?? Expression.Parameter(typeof(T1), select._tables[0].Alias);
var navigateSelector = Expression.Lambda<Func<T1, IEnumerable<T1>>>(Expression.MakeMemberAccess(navigateSelectorParamExp, navs[0].Property), navigateSelectorParamExp);
select.IncludeMany(navigateSelector);
select._includeManySubListOneToManyTempValue1 = list;
select.SetList(list);
return list.Except(list.SelectMany(a => FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityValueWithPropertyName(select._orm, tb.Type, a, navs[0].Property.Name) as IEnumerable<T1>)).ToList();
}
#endif #endif
#endregion #endregion
} }

View File

@ -3080,6 +3080,15 @@
<param name="then">即能 ThenInclude还可以二次过滤这个 EFCore 做不到?)</param> <param name="then">即能 ThenInclude还可以二次过滤这个 EFCore 做不到?)</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:FreeSqlGlobalExtensions.ToTreeList``1(FreeSql.ISelect{``0})">
<summary>
查询数据,加工为树型 List 返回<para></para>
注意:实体需要配置父子导航属性
</summary>
<typeparam name="T1"></typeparam>
<param name="that"></param>
<returns></returns>
</member>
<member name="M:System.Linq.Expressions.LambadaExpressionExtensions.And``1(System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}},System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}})"> <member name="M:System.Linq.Expressions.LambadaExpressionExtensions.And``1(System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}},System.Linq.Expressions.Expression{System.Func{``0,System.Boolean}})">
<summary> <summary>
使用 and 拼接两个 lambda 表达式 使用 and 拼接两个 lambda 表达式

View File

@ -23,18 +23,18 @@ namespace FreeSql.Internal.CommonProvider
protected string _select = "SELECT ", _orderby, _groupby, _having; protected string _select = "SELECT ", _orderby, _groupby, _having;
protected StringBuilder _where = new StringBuilder(); protected StringBuilder _where = new StringBuilder();
protected List<DbParameter> _params = new List<DbParameter>(); protected List<DbParameter> _params = new List<DbParameter>();
protected List<SelectTableInfo> _tables = new List<SelectTableInfo>(); internal protected List<SelectTableInfo> _tables = new List<SelectTableInfo>();
protected List<Func<Type, string, string>> _tableRules = new List<Func<Type, string, string>>(); protected List<Func<Type, string, string>> _tableRules = new List<Func<Type, string, string>>();
protected Func<Type, string, string> _aliasRule; protected Func<Type, string, string> _aliasRule;
protected string _tosqlAppendContent; protected string _tosqlAppendContent;
protected StringBuilder _join = new StringBuilder(); protected StringBuilder _join = new StringBuilder();
protected IFreeSql _orm; internal protected IFreeSql _orm;
protected CommonUtils _commonUtils; protected CommonUtils _commonUtils;
protected CommonExpression _commonExpression; protected CommonExpression _commonExpression;
protected DbTransaction _transaction; protected DbTransaction _transaction;
protected DbConnection _connection; protected DbConnection _connection;
protected Action<object> _trackToList; internal protected Action<object> _trackToList;
protected List<Action<object>> _includeToList = new List<Action<object>>(); internal protected List<Action<object>> _includeToList = new List<Action<object>>();
#if net40 #if net40
#else #else
protected List<Func<object, Task>> _includeToListAsync = new List<Func<object, Task>>(); protected List<Func<object, Task>> _includeToListAsync = new List<Func<object, Task>>();

View File

@ -659,6 +659,93 @@ namespace FreeSql.Internal.CommonProvider
foreach (var item in list) foreach (var item in list)
setListValue(item, null); setListValue(item, null);
Action<List<TNavigate>, TableInfo> fillOneToManyData = (subList, tbref2) =>
{
if (subList.Any() == false)
{
foreach (var item in list)
setListValue(item, new List<TNavigate>());
return;
}
Dictionary<string, List<Tuple<T1, List<TNavigate>>>> dicList = new Dictionary<string, List<Tuple<T1, List<TNavigate>>>>();
foreach (var item in list)
{
if (tbref.Columns.Count == 1)
{
var dicListKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString();
if (dicListKey == null) continue;
var dicListVal = Tuple.Create(item, new List<TNavigate>());
if (dicList.TryGetValue(dicListKey, out var items) == false)
dicList.Add(dicListKey, items = new List<Tuple<T1, List<TNavigate>>>());
items.Add(dicListVal);
}
else
{
var sb = new StringBuilder();
for (var z = 0; z < tbref.Columns.Count; z++)
{
if (z > 0) sb.Append("*$*");
sb.Append(getListValue(item, tbref.Columns[z].CsName, z));
}
var dicListKey = sb.ToString();
var dicListVal = Tuple.Create(item, new List<TNavigate>());
if (dicList.TryGetValue(dicListKey, out var items) == false)
dicList.Add(dicListKey, items = new List<Tuple<T1, List<TNavigate>>>());
items.Add(dicListVal);
sb.Clear();
}
}
var parentNavs = new List<string>();
foreach (var navProp in tbref2.Properties)
{
if (tbref2.ColumnsByCs.ContainsKey(navProp.Key)) continue;
if (tbref2.ColumnsByCsIgnore.ContainsKey(navProp.Key)) continue;
var tr2ref = tbref2.GetTableRef(navProp.Key, false);
if (tr2ref == null) continue;
if (tr2ref.RefType != TableRefType.ManyToOne) continue;
if (tr2ref.RefEntityType != tb.Type) continue;
parentNavs.Add(navProp.Key);
}
foreach (var nav in subList)
{
string key = null;
if (tbref.RefColumns.Count == 1)
{
key = _orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[0].CsName).ToString();
}
else
{
var sb = new StringBuilder();
for (var z = 0; z < tbref.RefColumns.Count; z++)
{
if (z > 0) sb.Append("*$*");
sb.Append(_orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[z].CsName));
}
key = sb.ToString();
sb.Clear();
}
if (dicList.TryGetValue(key, out var t1items) == false) continue;
foreach (var t1item in t1items)
t1item.Item2.Add(nav);
//将子集合的,多对一,对象设置为当前对象
foreach (var parentNav in parentNavs)
foreach (var t1item in t1items)
_orm.SetEntityValueWithPropertyName(tbref.RefMiddleEntityType, nav, parentNav, t1item.Item1);
}
foreach (var t1items in dicList.Values)
foreach (var t1item in t1items)
setListValue(t1item.Item1, t1item.Item2);
dicList.Clear();
};
if (tbref.RefType == TableRefType.OneToMany && _includeManySubListOneToManyTempValue1 != null && _includeManySubListOneToManyTempValue1 is List<TNavigate>)
{
fillOneToManyData(_includeManySubListOneToManyTempValue1 as List<TNavigate>, _commonUtils.GetTableByEntity(tbref.RefEntityType));
return;
}
var subSelect = _orm.Select<TNavigate>() var subSelect = _orm.Select<TNavigate>()
.DisableGlobalFilter() .DisableGlobalFilter()
.WithConnection(_connection) .WithConnection(_connection)
@ -794,83 +881,7 @@ namespace FreeSql.Internal.CommonProvider
} }
} }
if (subList.Any() == false) fillOneToManyData(subList, tbref2);
{
foreach (var item in list)
setListValue(item, new List<TNavigate>());
return;
}
Dictionary<string, List<Tuple<T1, List<TNavigate>>>> dicList = new Dictionary<string, List<Tuple<T1, List<TNavigate>>>>();
foreach (var item in list)
{
if (tbref.Columns.Count == 1)
{
var dicListKey = getListValue(item, tbref.Columns[0].CsName, 0)?.ToString();
if (dicListKey == null) continue;
var dicListVal = Tuple.Create(item, new List<TNavigate>());
if (dicList.TryGetValue(dicListKey, out var items) == false)
dicList.Add(dicListKey, items = new List<Tuple<T1, List<TNavigate>>>());
items.Add(dicListVal);
}
else
{
var sb = new StringBuilder();
for (var z = 0; z < tbref.Columns.Count; z++)
{
if (z > 0) sb.Append("*$*");
sb.Append(getListValue(item, tbref.Columns[z].CsName, z));
}
var dicListKey = sb.ToString();
var dicListVal = Tuple.Create(item, new List<TNavigate>());
if (dicList.TryGetValue(dicListKey, out var items) == false)
dicList.Add(dicListKey, items = new List<Tuple<T1, List<TNavigate>>>());
items.Add(dicListVal);
sb.Clear();
}
}
var parentNavs = new List<string>();
foreach (var navProp in tbref2.Properties)
{
if (tbref2.ColumnsByCs.ContainsKey(navProp.Key)) continue;
if (tbref2.ColumnsByCsIgnore.ContainsKey(navProp.Key)) continue;
var tr2ref = tbref2.GetTableRef(navProp.Key, false);
if (tr2ref == null) continue;
if (tr2ref.RefType != TableRefType.ManyToOne) continue;
if (tr2ref.RefEntityType != tb.Type) continue;
parentNavs.Add(navProp.Key);
}
foreach (var nav in subList)
{
string key = null;
if (tbref.RefColumns.Count == 1)
{
key = _orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[0].CsName).ToString();
}
else
{
var sb = new StringBuilder();
for (var z = 0; z < tbref.RefColumns.Count; z++)
{
if (z > 0) sb.Append("*$*");
sb.Append(_orm.GetEntityValueWithPropertyName(tbref.RefEntityType, nav, tbref.RefColumns[z].CsName));
}
key = sb.ToString();
sb.Clear();
}
if (dicList.TryGetValue(key, out var t1items) == false) return;
foreach (var t1item in t1items)
t1item.Item2.Add(nav);
//将子集合的,多对一,对象设置为当前对象
foreach (var parentNav in parentNavs)
foreach (var t1item in t1items)
_orm.SetEntityValueWithPropertyName(tbref.RefMiddleEntityType, nav, parentNav, t1item.Item1);
}
foreach (var t1items in dicList.Values)
foreach (var t1item in t1items)
setListValue(t1item.Item1, t1item.Item2);
dicList.Clear();
} }
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
@ -1044,7 +1055,7 @@ namespace FreeSql.Internal.CommonProvider
key = sb.ToString(); key = sb.ToString();
sb.Clear(); sb.Clear();
} }
if (dicList.TryGetValue(key, out var t1items) == false) return; if (dicList.TryGetValue(key, out var t1items) == false) continue;
foreach (var t1item in t1items) foreach (var t1item in t1items)
t1item.Item2.Add(subList[a]); t1item.Item2.Add(subList[a]);
} }
@ -1065,6 +1076,7 @@ namespace FreeSql.Internal.CommonProvider
return this; return this;
} }
internal object _includeManySubListOneToManyTempValue1 = null;
internal void SetList(IEnumerable<T1> list) internal void SetList(IEnumerable<T1> list)
{ {
foreach (var include in _includeToList) include?.Invoke(list); foreach (var include in _includeToList) include?.Invoke(list);