From 74d1de08a24333ba528649d68a09c0b4f143373c Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Sun, 22 May 2022 16:14:57 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20DTO=20=E9=9D=9E?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E5=B1=9E=E6=80=A7=E7=9B=B4=E6=8E=A5=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E5=AD=90=E6=9F=A5=E8=AF=A2=20ToList=EF=BC=8C=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=BC=82=E6=AD=A5=E6=89=A7=E8=A1=8C=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/base_entity/Program.cs | 20 ++- FreeSql.DbContext/FreeSql.DbContext.xml | 9 - FreeSql/Interface/Curd/ISelect/ISelect0.cs | 3 +- .../SelectProvider/Select0Provider.cs | 1 + .../SelectProvider/Select0ProviderReader.cs | 159 +++++++++++++++++- .../SelectProvider/Select1Provider.cs | 2 +- 6 files changed, 175 insertions(+), 19 deletions(-) diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index d2b4ccb5..f28e9085 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -307,8 +307,26 @@ namespace base_entity }) //users3 = fsql.Ado.Query("select * from user1 where groupid = @id", new { id = a.Id }) }); + var typs = fsql.Select(); + var typs2 = typeof(ISelect).GetInterfaces().SelectMany(a => a.GetMethods().Where(b => b.Name == "ToListAsync")).ToArray(); - + var tsub02 = fsql.Select() + .ToListAsync(a => new + { + users1 = fsql.Select().Where(b => b.GroupId == a.Id).ToList(), + users2 = fsql.Select().Where(b => b.GroupId == a.Id).ToList(b => new + { + userid = b.Id, + username = b.Username + }), + users3 = fsql.Select().Limit(10).ToList(), + users4 = fsql.Select().Limit(10).ToList(b => new + { + userid = b.Id, + username = b.Username + }) + //users3 = fsql.Ado.Query("select * from user1 where groupid = @id", new { id = a.Id }) + }).Result; diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index d34b34ef..4335acb5 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -786,14 +786,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql/Interface/Curd/ISelect/ISelect0.cs b/FreeSql/Interface/Curd/ISelect/ISelect0.cs index 24970727..b845bf3c 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect0.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect0.cs @@ -21,7 +21,8 @@ namespace FreeSql Task ToDataTableByPropertyNameAsync(string[] properties, CancellationToken cancellationToken = default); Task> ToDictionaryAsync(Func keySelector, CancellationToken cancellationToken = default); Task> ToDictionaryAsync(Func keySelector, Func elementSelector, CancellationToken cancellationToken = default); - Task> ToListAsync(bool includeNestedMembers = false, CancellationToken cancellationToken = default); + Task> ToListAsync(CancellationToken cancellationToken = default); + Task> ToListAsync(bool includeNestedMembers, CancellationToken cancellationToken = default); Task> ToListAsync(string field, CancellationToken cancellationToken = default); Task ToOneAsync(CancellationToken cancellationToken = default); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 20fba909..e47c22e3 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -946,6 +946,7 @@ namespace FreeSql.Internal.CommonProvider _distinct = tmpDistinct; } } + public Task> ToListAsync(CancellationToken cancellationToken = default) => ToListAsync(false, cancellationToken); public virtual Task> ToListAsync(bool includeNestedMembers = false, CancellationToken cancellationToken = default) { if (_selectExpression != null) return this.InternalToListAsync(_selectExpression, cancellationToken); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs index 9d410ba7..287e995f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs @@ -944,9 +944,11 @@ namespace FreeSql.Internal.CommonProvider af.fillSubSelectMany[b].Item2.Add(otherListItem); continue; } + var threadId = Thread.CurrentThread.ManagedThreadId; try { - _SameSelectPendingOnlySync.TryAdd(Thread.CurrentThread.ManagedThreadId, new List>()); + _SameSelectPendingOnlySync.TryAdd(threadId, new List>()); + var cssps = CurrentSameSelectPendingOnlySync; var newexp = findSubSelectMany[a]; var newexpParms = otherAfmanys[a].Select(d => { @@ -960,12 +962,10 @@ namespace FreeSql.Internal.CommonProvider for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) { var vals = newexpParamVals.Select(d => d[c]).ToArray(); - if (c == ret.Count - 1) CurrentSameSelectPendingOnlySync.Add(null); //flush flag + if (c == ret.Count - 1) cssps.Add(null); //flush flag var diret = newexpFunc.DynamicInvoke(vals); - if (c < ret.Count - 1) continue; var otherList = diret as IEnumerable; - var cssps = CurrentSameSelectPendingOnlySync; var retlistidx = 0; foreach (var otherListItem in otherList) { @@ -980,11 +980,11 @@ namespace FreeSql.Internal.CommonProvider af.fillSubSelectMany[tryrowidx * otherAfmanys.Count + a].Item2.Add(otherListItem); } } - CurrentSameSelectPendingOnlySync.Clear(); + cssps.Clear(); } finally { - _SameSelectPendingOnlySync.TryRemove(Thread.CurrentThread.ManagedThreadId, out var oldssps); + _SameSelectPendingOnlySync.TryRemove(threadId, out var oldssps); } } return ret; @@ -1225,6 +1225,16 @@ namespace FreeSql.Internal.CommonProvider internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { + var cssps = CurrentSameSelectPendingOnlySync; + ReadAnonymousTypeOtherInfo csspsod = null; + if (cssps != null) + { + var ods = new List(); + if (otherData?.Any() == true) ods.AddRange(otherData); + ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(cssps.Any() && cssps.Last() == null ? cssps.Count - 1 : cssps.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); + otherData = ods.ToArray(); + } + string sql = null; if (otherData?.Length > 0) { @@ -1236,6 +1246,7 @@ namespace FreeSql.Internal.CommonProvider else sql = this.ToSql(af.Field); + if (ProcessSameSelectPendingOnlySync(cssps, ref sql, csspsod)) return Task.FromResult(new List()); return ToListAfPrivateAsync(sql, af, otherData, cancellationToken); } @@ -1316,6 +1327,16 @@ namespace FreeSql.Internal.CommonProvider } internal Task> ToListMapReaderPrivateAsync(ReadAnonymousTypeAfInfo af, ReadAnonymousTypeOtherInfo[] otherData, CancellationToken cancellationToken) { + var cssps = CurrentSameSelectPendingOnlySync; + ReadAnonymousTypeOtherInfo csspsod = null; + if (cssps != null) + { + var ods = new List(); + if (otherData?.Any() == true) ods.AddRange(otherData); + ods.Add(csspsod = new ReadAnonymousTypeOtherInfo($", {(cssps.Any() && cssps.Last() == null ? cssps.Count - 1 : cssps.Count)}{_commonUtils.FieldAsAlias("fsql_subsel_rowidx")}", new ReadAnonymousTypeInfo { CsType = typeof(int) }, new List())); + otherData = ods.ToArray(); + } + string sql = null; if (otherData?.Length > 0) { @@ -1327,6 +1348,7 @@ namespace FreeSql.Internal.CommonProvider else sql = this.ToSql(af.field); + if (ProcessSameSelectPendingOnlySync(cssps, ref sql, csspsod)) return Task.FromResult(new List()); return ToListMrPrivateAsync(sql, af, otherData, cancellationToken); } protected Task> ToListMapReaderAsync(ReadAnonymousTypeAfInfo af, CancellationToken cancellationToken) => ToListMapReaderPrivateAsync(af, null, cancellationToken); @@ -1340,7 +1362,130 @@ namespace FreeSql.Internal.CommonProvider async protected Task InternalMinAsync(Expression exp, CancellationToken cancellationToken) => (await this.ToListAsync($"min({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Min(); async protected Task InternalSumAsync(Expression exp, CancellationToken cancellationToken) => (await this.ToListAsync($"sum({_commonExpression.ExpressionSelectColumn_MemberAccess(_tables, null, SelectTableInfoType.From, exp, true, null)}){_commonUtils.FieldAsAlias("as1")}", cancellationToken)).Sum(); - protected Task> InternalToListAsync(Expression select, CancellationToken cancellationToken) => this.ToListMapReaderAsync(this.GetExpressionField(select), cancellationToken); + static ConcurrentDictionary _dicGetMethodsByName = new ConcurrentDictionary(); + async protected Task> InternalToListAsync(Expression select, CancellationToken cancellationToken) + { + //【注意】:此异步有特别逻辑,因为要处理子查询集合 ToList -> ToListAsync,原因是 LambdaExpression 表达式树内不支持 await Async + var map = new ReadAnonymousTypeInfo(); + var field = new StringBuilder(); + var index = 0; + var findSubSelectMany = new List(); + + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, this, null, _whereGlobalFilter, null, findSubSelectMany, true); + var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); + if (findSubSelectMany.Any() == false) return await this.ToListMapReaderPrivateAsync(af, new ReadAnonymousTypeOtherInfo[0], cancellationToken); + + af.fillSubSelectMany = new List>(); + //查询 SubSelectMany + var otherAfmanys = findSubSelectMany.Select(a => + { + var vst = new FindAllMemberExpressionVisitor(this); + vst.Visit(a); + var finds = vst.Result; + + var afs = new List>(); + foreach (var find in finds) + { + var otherMap = new ReadAnonymousTypeInfo(); + field.Clear(); + _commonExpression.ReadAnonymousField(_tables, field, otherMap, ref index, find.Item1, this, null, _whereGlobalFilter, null, null, true); + var otherRet = new List(); + var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); + afs.Add(NativeTuple.Create(find.Item1, find.Item2, otherAf)); + } + return afs; + }).ToList(); + var otherAfdic = otherAfmanys.SelectMany(a => a).GroupBy(a => a.Item1.ToString()).ToDictionary(a => a.Key, a => a.ToList()); + var otherAfs = otherAfdic.Select(a => a.Value.First().Item3).ToArray(); + var ret = await this.ToListMapReaderPrivateAsync(af, otherAfs, cancellationToken); + if (ret.Any() == false || otherAfmanys.Any() == false) return ret; + + var rmev = new ReplaceMemberExpressionVisitor(); + + for (var a = 0; a < otherAfmanys.Count; a++) + { + if (otherAfmanys[a].Any() == false) + { + var otherList = Expression.Lambda(findSubSelectMany[a]).Compile().DynamicInvoke() as IEnumerable; + foreach (var otherListItem in otherList) + for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) + af.fillSubSelectMany[b].Item2.Add(otherListItem); + continue; + } + var threadId = Thread.CurrentThread.ManagedThreadId; //一定要【注意】 await 会影响该值,以下以容将 ToList 替换成 ToListAsync 后再执行 + try + { + _SameSelectPendingOnlySync.TryAdd(threadId, new List>()); + var cssps = CurrentSameSelectPendingOnlySync; + var newexp = findSubSelectMany[a]; + var newexpParms = otherAfmanys[a].Select(d => + { + var newexpParm = Expression.Parameter(d.Item1.Type); + newexp = rmev.Replace(newexp, d.Item1, newexpParm); + return newexpParm; + }).ToArray(); + var newexpCallExp = (newexp as MethodCallExpression); + if (newexpCallExp?.Object != null) { + var asyncMethods = _dicGetMethodsByName.GetOrAdd(newexpCallExp.Object.Type, dgmbn => dgmbn.GetMethods().Where(c => c.Name == $"{newexpCallExp.Method.Name}Async") + .Concat(dgmbn.GetInterfaces().SelectMany(b => b.GetMethods().Where(c => c.Name == $"{newexpCallExp.Method.Name}Async"))).ToArray()); + var asyncMethod = asyncMethods.Length == 1 ? asyncMethods.First() : null; + var newexpMethodGenericArgs = newexpCallExp.Method.GetGenericArguments(); + var newexpMethodParmArgs = newexpCallExp.Method.GetParameters(); + if (asyncMethods.Length > 1) + { + asyncMethods = asyncMethods + .Where(b => + { + var bGenericArgs = b.GetGenericArguments(); + return bGenericArgs.Length == newexpMethodGenericArgs.Length; + }) + .Select(b => newexpMethodGenericArgs.Length == 0 ? b : b.MakeGenericMethod(newexpMethodGenericArgs)) + .Where(b => + { + var bParmArgs = b.GetParameters(); + return bParmArgs.Length - 1 == newexpMethodParmArgs.Length && newexpMethodParmArgs.Where((c, d) => c.ParameterType == bParmArgs[d].ParameterType).Count() == newexpMethodParmArgs.Length; + }).ToArray(); + if (asyncMethods.Length == 1) asyncMethod = asyncMethods.First(); + } + if (asyncMethod != null) + newexp = Expression.Call(newexpCallExp.Object, asyncMethod, newexpCallExp.Arguments.Concat(new[] { Expression.Constant(cancellationToken, typeof(CancellationToken)) }).ToArray()); + } + var newexpFunc = Expression.Lambda(newexp, newexpParms).Compile(); + + var newexpParamVals = otherAfmanys[a].Select(d => otherAfdic[d.Item1.ToString()].First().Item3.retlist).ToArray(); + for (int b = a, c = 0; b < af.fillSubSelectMany?.Count; b += otherAfmanys.Count, c++) + { + var vals = newexpParamVals.Select(d => d[c]).ToArray(); + if (c == ret.Count - 1) cssps.Add(null); //flush flag + var diretTask = newexpFunc.DynamicInvoke(vals) as Task; + + if (c < ret.Count - 1) continue; + await diretTask; + var diret = diretTask.GetType().GetProperty("Result").GetValue(diretTask, new object[0]); + var otherList = diret as IEnumerable; + var retlistidx = 0; + foreach (var otherListItem in otherList) + { + var retlist = cssps[0].Item3.retlist; + while (retlistidx >= retlist.Count) + { + cssps.RemoveAt(0); + retlist = cssps[0].Item3.retlist; + retlistidx = 0; + } + int.TryParse(retlist[retlistidx++]?.ToString(), out var tryrowidx); + af.fillSubSelectMany[tryrowidx * otherAfmanys.Count + a].Item2.Add(otherListItem); + } + } + cssps.Clear(); + } + finally + { + _SameSelectPendingOnlySync.TryRemove(threadId, out var oldssps); + } + } + return ret; + } async public Task InternalInsertIntoAsync(string tableName, Expression select, CancellationToken cancellationToken) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 722b44c6..09240313 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -1323,7 +1323,7 @@ namespace FreeSql.Internal.CommonProvider async public Task ToOneAsync(CancellationToken cancellationToken = default) => (await this.Limit(1).ToListAsync(cancellationToken)).FirstOrDefault(); public Task FirstAsync(Expression> select, CancellationToken cancellationToken = default) => this.ToOneAsync(select, cancellationToken); public Task FirstAsync(CancellationToken cancellationToken = default) => this.ToOneAsync(cancellationToken); - public override Task> ToListAsync(bool includeNestedMembers = false, CancellationToken cancellationToken = default) => base.ToListAsync(_isIncluded || includeNestedMembers, cancellationToken); + public override Task> ToListAsync(bool includeNestedMembers, CancellationToken cancellationToken = default) => base.ToListAsync(_isIncluded || includeNestedMembers, cancellationToken); #endif } } \ No newline at end of file