From 0effad75e448fa605e324e70033e9bdfe0a08d27 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Sun, 15 Mar 2020 18:33:15 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20ISelect.ToTreeList=20?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95=E6=9F=A5=E8=AF=A2=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=EF=BC=8C=E5=8A=A0=E5=B7=A5=E4=B8=BA=E6=A0=91=E5=9E=8B?= =?UTF-8?q?=20List=EF=BC=9B(=E6=B3=A8=E6=84=8F=EF=BC=9A=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E9=9C=80=E8=A6=81=E9=85=8D=E7=BD=AE=E7=88=B6=E5=AD=90=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E5=B1=9E=E6=80=A7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Tests.VB/FreeSql.Tests.VB.vbproj | 3 +- FreeSql.Tests.VB/UnitTest1.vb | 63 ++++++- .../RepositoryTests.cs | 11 +- FreeSql/Extensions/FreeSqlGlobalExtensions.cs | 56 ++++++ FreeSql/FreeSql.xml | 9 + .../SelectProvider/Select0Provider.cs | 8 +- .../SelectProvider/Select1Provider.cs | 168 ++++++++++-------- 7 files changed, 232 insertions(+), 86 deletions(-) diff --git a/FreeSql.Tests.VB/FreeSql.Tests.VB.vbproj b/FreeSql.Tests.VB/FreeSql.Tests.VB.vbproj index a8e7b84e..ef69f571 100644 --- a/FreeSql.Tests.VB/FreeSql.Tests.VB.vbproj +++ b/FreeSql.Tests.VB/FreeSql.Tests.VB.vbproj @@ -1,4 +1,4 @@ - + FreeSql.Tests.VB @@ -15,6 +15,7 @@ + diff --git a/FreeSql.Tests.VB/UnitTest1.vb b/FreeSql.Tests.VB/UnitTest1.vb index 84ab482a..da79ebdf 100644 --- a/FreeSql.Tests.VB/UnitTest1.vb +++ b/FreeSql.Tests.VB/UnitTest1.vb @@ -1,4 +1,5 @@ Imports System +Imports FreeSql.DataAnnotations Imports Xunit 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() + 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 Class @@ -57,4 +69,53 @@ Class Testvb2 Property TestvbId As Integer Property Testvb As Testvb Property Context As String -End Class \ No newline at end of file +End Class + + +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 + + + Public Property Id As Integer + Get + Return _Id + End Get + Set(value As Integer) + _Id = value + End Set + End Property + + + + 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 diff --git a/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs b/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs index 7d0a479e..d6dca944 100644 --- a/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs +++ b/FreeSql.Tests/FreeSql.Tests.DbContext/RepositoryTests.cs @@ -1,6 +1,7 @@ using FreeSql.DataAnnotations; using System; using System.Collections.Generic; +using System.Linq; using Xunit; namespace FreeSql.Tests @@ -318,6 +319,7 @@ namespace FreeSql.Tests cts[1].Goodss.Clear(); cts[1].Goodss.Add(new Goods { Name = "Ʒ55" }); repo.Update(cts); + } [Table(Name = "EAUNL_OTM_CT")] class Cagetory @@ -390,6 +392,7 @@ namespace FreeSql.Tests [Fact] public void EnableAddOrUpdateNavigateList_OneToMany_Parent() { + g.sqlite.Delete().Where("1=1").ExecuteAffrows(); var repo = g.sqlite.GetRepository(); var cts = new[] { new CagetoryParent @@ -412,9 +415,12 @@ namespace FreeSql.Tests }) } }; - repo.DbContextOptions.EnableAddOrUpdateNavigateList = false; //رռ湦 + repo.DbContextOptions.EnableAddOrUpdateNavigateList = true; //򿪼湦 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].Childs.Clear(); cts[1].Name = "22"; @@ -427,6 +433,7 @@ namespace FreeSql.Tests cts[1].Childs.Clear(); cts[1].Childs.Add(new CagetoryParent { Name = "2_22" }); repo.Update(cts); + var treelist2 = repo.Select.ToTreeList(); } [Table(Name = "EAUNL_OTMP_CT")] class CagetoryParent diff --git a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs index 99debfaf..fa3116cd 100644 --- a/FreeSql/Extensions/FreeSqlGlobalExtensions.cs +++ b/FreeSql/Extensions/FreeSqlGlobalExtensions.cs @@ -1,5 +1,6 @@ using FreeSql; using FreeSql.DataAnnotations; +using FreeSql.Internal.CommonProvider; using System; using System.Collections; using System.Collections.Concurrent; @@ -246,6 +247,61 @@ public static partial class FreeSqlGlobalExtensions await select.SetListAsync(list); return list; } +#endif + #endregion + + #region ToTreeList() 父子分类 + /// + /// 查询数据,加工为树型 List 返回 + /// 注意:实体需要配置父子导航属性 + /// + /// + /// + /// + public static List ToTreeList(this ISelect that) where T1 : class + { + var select = that as Select1Provider; + 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>>(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)).ToList(); + } +#if net40 +#else + async public static System.Threading.Tasks.Task> ToTreeListAsync(this ISelect that) where T1 : class + { + var select = that as Select1Provider; + 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>>(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)).ToList(); + } #endif #endregion } \ No newline at end of file diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 1efa0813..4958b871 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -3080,6 +3080,15 @@ 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + + + 查询数据,加工为树型 List 返回 + 注意:实体需要配置父子导航属性 + + + + + 使用 and 拼接两个 lambda 表达式 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index d0c9807a..5f843cf7 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -23,18 +23,18 @@ namespace FreeSql.Internal.CommonProvider protected string _select = "SELECT ", _orderby, _groupby, _having; protected StringBuilder _where = new StringBuilder(); protected List _params = new List(); - protected List _tables = new List(); + internal protected List _tables = new List(); protected List> _tableRules = new List>(); protected Func _aliasRule; protected string _tosqlAppendContent; protected StringBuilder _join = new StringBuilder(); - protected IFreeSql _orm; + internal protected IFreeSql _orm; protected CommonUtils _commonUtils; protected CommonExpression _commonExpression; protected DbTransaction _transaction; protected DbConnection _connection; - protected Action _trackToList; - protected List> _includeToList = new List>(); + internal protected Action _trackToList; + internal protected List> _includeToList = new List>(); #if net40 #else protected List> _includeToListAsync = new List>(); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 2d3b877f..2629c519 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -659,6 +659,93 @@ namespace FreeSql.Internal.CommonProvider foreach (var item in list) setListValue(item, null); + Action, TableInfo> fillOneToManyData = (subList, tbref2) => + { + if (subList.Any() == false) + { + foreach (var item in list) + setListValue(item, new List()); + return; + } + + Dictionary>>> dicList = new Dictionary>>>(); + 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()); + if (dicList.TryGetValue(dicListKey, out var items) == false) + dicList.Add(dicListKey, items = new List>>()); + 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()); + if (dicList.TryGetValue(dicListKey, out var items) == false) + dicList.Add(dicListKey, items = new List>>()); + items.Add(dicListVal); + sb.Clear(); + } + } + var parentNavs = new List(); + 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) + { + fillOneToManyData(_includeManySubListOneToManyTempValue1 as List, _commonUtils.GetTableByEntity(tbref.RefEntityType)); + return; + } + var subSelect = _orm.Select() .DisableGlobalFilter() .WithConnection(_connection) @@ -794,83 +881,7 @@ namespace FreeSql.Internal.CommonProvider } } - if (subList.Any() == false) - { - foreach (var item in list) - setListValue(item, new List()); - return; - } - - Dictionary>>> dicList = new Dictionary>>>(); - 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()); - if (dicList.TryGetValue(dicListKey, out var items) == false) - dicList.Add(dicListKey, items = new List>>()); - 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()); - if (dicList.TryGetValue(dicListKey, out var items) == false) - dicList.Add(dicListKey, items = new List>>()); - items.Add(dicListVal); - sb.Clear(); - } - } - var parentNavs = new List(); - 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(); + fillOneToManyData(subList, tbref2); } break; case TableRefType.ManyToMany: @@ -1044,7 +1055,7 @@ namespace FreeSql.Internal.CommonProvider key = sb.ToString(); 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) t1item.Item2.Add(subList[a]); } @@ -1065,6 +1076,7 @@ namespace FreeSql.Internal.CommonProvider return this; } + internal object _includeManySubListOneToManyTempValue1 = null; internal void SetList(IEnumerable list) { foreach (var include in _includeToList) include?.Invoke(list);