diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 743835e4..9c6cd88b 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -532,14 +532,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs b/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs index a26bfbd5..9216cbcc 100644 --- a/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs +++ b/FreeSql.Tests/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs @@ -1114,33 +1114,89 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title"" .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) .Where(a => a.model2id <= model1.id) .ToList(); + var t001 = g.sqlite.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(a => new + { + a.model1.id, + a.childs, + childs2 = a.childs + }); var t1 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) .ToList(); + var t111 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, + a.model2.childs, + childs2 = a.model2.childs + }); var t2 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id), then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + var t222 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, + a.model2.childs, + childs2 = a.model2.childs + }); var t00 = g.sqlite.Select() .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) .Where(a => a.model2id <= model1.id) .ToList(); + var t0001 = g.sqlite.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id)) + .Where(a => a.model2id <= model1.id) + .ToList(a => new + { + a.model1.id, + a.childs, + childs2 = a.childs + }); var t11 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) .Where(a => a.id <= model1.id) .ToList(); + var t1111 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id)) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, + a.model2.childs, + childs2 = a.model2.childs + }); var t22 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) .Where(a => a.id <= model1.id) .ToList(); + var t2222 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id))) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, + a.model2.childs, + childs2 = a.model2.childs + }); //---- Select ---- @@ -1148,33 +1204,77 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title"" .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) .Where(a => a.model2id <= model1.id) .ToList(); + var at001 = g.sqlite.Select() + .IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.model2id <= model1.id) + .ToList(a => new + { + a.model2id, a.childs, childs2 = a.childs + }); var at1 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) .Where(a => a.id <= model1.id) - .ToList(); + .ToList(); + var at111 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, a.model2.childs, childs2 = a.model2.childs + }); var at2 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) .Where(a => a.id <= model1.id) .ToList(); + var at2222 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), + then => then.IncludeMany(m3 => m3.childs2.Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, a.model2.childs, childs2 = a.model2.childs + }); var at00 = g.sqlite.Select() .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) .Where(a => a.model2id <= model1.id) .ToList(); + var at011 = g.sqlite.Select() + .IncludeMany(a => a.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.model2id <= model1.id) + .ToList(a => new + { + a.model2id, a.childs, childs2 = a.childs + }); var at11 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) .Where(a => a.id <= model1.id) .ToList(); + var at1112 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id })) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, a.model2.childs, childs2 = a.model2.childs + }); var at22 = g.sqlite.Select() .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) .Where(a => a.id <= model1.id) .ToList(); + var at2223 = g.sqlite.Select() + .IncludeMany(a => a.model2.childs.Take(1).Where(m3 => m3.model2111Idaaa == a.model2.model2id).Select(m3 => new TestInclude_OneToManyModel3 { id = m3.id }), + then => then.IncludeMany(m3 => m3.childs2.Take(2).Where(m4 => m4.model3333Id333 == m3.id).Select(m4 => new TestInclude_OneToManyModel4 { id = m4.id }))) + .Where(a => a.id <= model1.id) + .ToList(a => new + { + a.id, a.model2.childs, childs2 = a.model2.childs + }); } public class TestInclude_OneToManyModel11 @@ -1557,6 +1657,15 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title"" .IncludeMany(a => a.Tag.Songs.Take(1).Select(b => new Song { Id = b.Id, Title = b.Title })) .Where(a => a.Tag.Id == tag1.Id || a.Tag.Id == tag2.Id) .ToList(true); + + var asongs2222 = g.sqlite.Select() + .IncludeMany(a => a.Tags.Select(b => new Tag { Id = b.Id, Name = b.Name }), + then => then.IncludeMany(t => t.Songs.Select(b => new Song { Id = b.Id, Title = b.Title }))) + .Where(a => a.Id == song1.Id || a.Id == song2.Id || a.Id == song3.Id) + .ToList(a => new + { + a.Id, a.Is_deleted, a.Tags + }); } [Fact] diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 705a1aa2..d1177708 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -28,13 +28,13 @@ namespace FreeSql.Internal } internal const int ReadAnonymousFieldAsCsName = -53129; - public bool ReadAnonymousField(List _tables, StringBuilder field, ReadAnonymousTypeInfo parent, ref int index, Expression exp, SelectGroupingProvider grouping, List whereCascadeExpression, bool isAllDtoMap) + public bool ReadAnonymousField(List _tables, StringBuilder field, ReadAnonymousTypeInfo parent, ref int index, Expression exp, Select0Provider select, SelectGroupingProvider grouping, List whereCascadeExpression, List findIncludeMany, bool isAllDtoMap) { Func getTSC = () => new ExpTSC { _tables = _tables, grouping = grouping, tbtype = SelectTableInfoType.From, isQuoteName = true, isDisableDiyParse = false, style = ExpressionStyle.Where, whereCascadeExpression = whereCascadeExpression }; switch (exp.NodeType) { - case ExpressionType.Quote: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand, grouping, whereCascadeExpression, isAllDtoMap); - case ExpressionType.Lambda: return ReadAnonymousField(_tables, field, parent, ref index, (exp as LambdaExpression)?.Body, grouping, whereCascadeExpression, isAllDtoMap); + case ExpressionType.Quote: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand, select, grouping, whereCascadeExpression, findIncludeMany, isAllDtoMap); + case ExpressionType.Lambda: return ReadAnonymousField(_tables, field, parent, ref index, (exp as LambdaExpression)?.Body, select, grouping, whereCascadeExpression, findIncludeMany, isAllDtoMap); case ExpressionType.Negate: case ExpressionType.NegateChecked: parent.DbField = $"-({ExpressionLambdaToSql(exp, getTSC())})"; @@ -43,7 +43,7 @@ namespace FreeSql.Internal else if (index == ReadAnonymousFieldAsCsName && string.IsNullOrEmpty(parent.CsName) == false) field.Append(_common.FieldAsAlias(parent.CsName)); if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type; return false; - case ExpressionType.Convert: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand, grouping, whereCascadeExpression, isAllDtoMap); + case ExpressionType.Convert: return ReadAnonymousField(_tables, field, parent, ref index, (exp as UnaryExpression)?.Operand, select, grouping, whereCascadeExpression, findIncludeMany, isAllDtoMap); case ExpressionType.Constant: var constExp = exp as ConstantExpression; //处理自定义SQL语句,如: ToList(new { @@ -133,12 +133,36 @@ namespace FreeSql.Internal MapType = memProp.PropertyType }; parent.Childs.Add(child); - ReadAnonymousField(_tables, field, child, ref index, Expression.MakeMemberAccess(exp, memProp), grouping, whereCascadeExpression, false); + ReadAnonymousField(_tables, field, child, ref index, Expression.MakeMemberAccess(exp, memProp), select, grouping, whereCascadeExpression, findIncludeMany, false); } } } else { + if (select != null && findIncludeMany != null && select._includeToList.Any() && exp.Type.IsGenericType && + typeof(IEnumerable).IsAssignableFrom(exp.Type) && + typeof(ICollection<>).MakeGenericType(exp.Type.GetGenericArguments().FirstOrDefault()).IsAssignableFrom(exp.Type)) + { + var includeKey = ""; + var memExp = exp as MemberExpression; + while (memExp != null) + { + includeKey = $"{memExp.Member.Name}.{includeKey}"; + if (memExp.Expression.NodeType == ExpressionType.Parameter) break; + memExp = memExp.Expression as MemberExpression; + } + if (memExp != null && string.IsNullOrEmpty(includeKey) == false) + { + includeKey = includeKey.TrimEnd('.'); + if (select._includeInfo.ContainsKey(includeKey)) + { + parent.IncludeManyKey = includeKey; + parent.CsType = exp.Type.GetGenericArguments().FirstOrDefault(); + findIncludeMany?.Add(includeKey); + return false; + } + } + } if (grouping != null && exp is MemberExpression expMem2 && expMem2.Member.Name == "Key" && expMem2.Expression.Type.FullName.StartsWith("FreeSql.ISelectGroupingAggregate`")) { field.Append(grouping._field); @@ -173,7 +197,7 @@ namespace FreeSql.Internal MapType = initExp.NewExpression.Arguments[a].Type }; parent.Childs.Add(child); - ReadAnonymousField(_tables, field, child, ref index, initExp.NewExpression.Arguments[a], grouping, whereCascadeExpression, false); + ReadAnonymousField(_tables, field, child, ref index, initExp.NewExpression.Arguments[a], select, grouping, whereCascadeExpression, findIncludeMany, false); } } else if (isAllDtoMap && _tables != null && _tables.Any() && initExp.NewExpression.Type != _tables.FirstOrDefault().Table.Type) @@ -196,7 +220,7 @@ namespace FreeSql.Internal }; parent.Childs.Add(child); if (dtTb.Parameter != null) - ReadAnonymousField(_tables, field, child, ref index, Expression.Property(dtTb.Parameter, dtTb.Table.Properties[trydtocol.CsName]), grouping, whereCascadeExpression, isAllDtoMap); + ReadAnonymousField(_tables, field, child, ref index, Expression.Property(dtTb.Parameter, dtTb.Table.Properties[trydtocol.CsName]), select, grouping, whereCascadeExpression, findIncludeMany, isAllDtoMap); else { child.DbField = $"{dtTb.Alias}.{_common.QuoteSqlName(trydtocol.Attribute.Name)}"; @@ -222,7 +246,7 @@ namespace FreeSql.Internal MapType = initAssignExp.Expression.Type }; parent.Childs.Add(child); - ReadAnonymousField(_tables, field, child, ref index, initAssignExp.Expression, grouping, whereCascadeExpression, false); + ReadAnonymousField(_tables, field, child, ref index, initAssignExp.Expression, select, grouping, whereCascadeExpression, findIncludeMany, false); } } if (parent.Childs.Any() == false) throw new Exception($"映射异常:{initExp.NewExpression.Type.Name} 没有一个属性名相同"); @@ -255,7 +279,7 @@ namespace FreeSql.Internal MapType = newExp.Arguments[a].Type }; parent.Childs.Add(child); - ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], grouping, whereCascadeExpression, false); + ReadAnonymousField(_tables, field, child, ref index, newExp.Arguments[a], select, grouping, whereCascadeExpression, findIncludeMany, false); } } else @@ -279,7 +303,7 @@ namespace FreeSql.Internal }; parent.Childs.Add(child); if (dtTb.Parameter != null) - ReadAnonymousField(_tables, field, child, ref index, Expression.Property(dtTb.Parameter, dtTb.Table.Properties[trydtocol.CsName]), grouping, whereCascadeExpression, isAllDtoMap); + ReadAnonymousField(_tables, field, child, ref index, Expression.Property(dtTb.Parameter, dtTb.Table.Properties[trydtocol.CsName]), select, grouping, whereCascadeExpression, findIncludeMany, isAllDtoMap); else { child.DbField = $"{dtTb.Alias}.{_common.QuoteSqlName(trydtocol.Attribute.Name)}"; @@ -300,9 +324,9 @@ namespace FreeSql.Internal if (parent.CsType == null && exp.Type.IsValueType) parent.CsType = exp.Type; return false; } - public object ReadAnonymous(ReadAnonymousTypeInfo parent, DbDataReader dr, ref int index, bool notRead, ReadAnonymousDbValueRef dbValue) + public object ReadAnonymous(ReadAnonymousTypeInfo parent, DbDataReader dr, ref int index, bool notRead, ReadAnonymousDbValueRef dbValue, int rowIndex, List> fillIncludeMany) { - if (parent.Childs.Any() == false) + if (parent.Childs.Any() == false && string.IsNullOrEmpty(parent.IncludeManyKey)) { if (notRead) { @@ -316,19 +340,24 @@ namespace FreeSql.Internal if (parent.CsType != parent.MapType) objval = Utils.GetDataReaderValue(parent.MapType, objval); objval = Utils.GetDataReaderValue(parent.CsType, objval); - if (parent.Property != null && parent.CsType != parent.Property.PropertyType) + if (parent.Property != null && parent.CsType != parent.Property.PropertyType) objval = Utils.GetDataReaderValue(parent.Property.PropertyType, objval); return objval; } var ctorParmsLength = 0; object ret; - if (parent.IsDefaultCtor || parent.IsEntity || (ctorParmsLength = parent.Consturctor.GetParameters()?.Length ?? 0) == 0) + if (string.IsNullOrEmpty(parent.IncludeManyKey) == false) + { + ret = typeof(List<>).MakeGenericType(parent.CsType).CreateInstanceGetDefaultValue(); + fillIncludeMany?.Add(NativeTuple.Create(parent.IncludeManyKey, ret as IList, rowIndex)); + } + else if (parent.IsDefaultCtor || parent.IsEntity || (ctorParmsLength = parent.Consturctor.GetParameters()?.Length ?? 0) == 0) ret = parent.CsType?.CreateInstanceGetDefaultValue() ?? parent.Consturctor.Invoke(null); else { var ctorParms = new object[ctorParmsLength]; for (var c = 0; c < ctorParmsLength; c++) - ctorParms[c] = ReadAnonymous(parent.Childs[c], dr, ref index, notRead, null); + ctorParms[c] = ReadAnonymous(parent.Childs[c], dr, ref index, notRead, null, rowIndex, fillIncludeMany); ret = parent.Consturctor.Invoke(ctorParms); } @@ -337,7 +366,7 @@ namespace FreeSql.Internal { var prop = parent.Childs[b].Property; var dbval = parent.IsEntity ? new ReadAnonymousDbValueRef() : null; - var objval = ReadAnonymous(parent.Childs[b], dr, ref index, notRead, dbval); + var objval = ReadAnonymous(parent.Childs[b], dr, ref index, notRead, dbval, rowIndex, fillIncludeMany); if (isnull == false && parent.IsEntity && dbval.DbValue == null && parent.Table != null && parent.Table.ColumnsByCs.TryGetValue(parent.Childs[b].CsName, out var trycol) && trycol.Attribute.IsPrimary) isnull = true; if (isnull == false && prop.CanWrite) diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 89ea4ce5..bde34d53 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -37,6 +37,7 @@ namespace FreeSql.Internal.CommonProvider #else public List> _includeToListAsync = new List>(); #endif + public Dictionary _includeInfo = new Dictionary(); public bool _distinct; public Expression _selectExpression; public List _whereCascadeExpression = new List(); @@ -57,6 +58,7 @@ namespace FreeSql.Internal.CommonProvider #else _includeToListAsync.Clear(); #endif + _includeInfo.Clear(); _selectExpression = null; _whereCascadeExpression.Clear(); _whereGlobalFilter = _orm.GlobalFilter.GetFilters(); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs index 4ff5160c..78014870 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0ProviderReader.cs @@ -85,6 +85,7 @@ namespace FreeSql.Internal.CommonProvider var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); + var retCount = 0; Exception exception = null; try { @@ -95,8 +96,9 @@ namespace FreeSql.Internal.CommonProvider { var idx = af.FieldCount - 1; foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null)); + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, retCount, null)); } + retCount++; }, CommandType.Text, sql, dbParms); } catch (Exception ex) @@ -143,13 +145,13 @@ namespace FreeSql.Internal.CommonProvider _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { ret.Object.Add(af.Read(_orm, fetch.Object)); - retCount++; if (otherData != null) { var idx = af.FieldCount - 1; foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null)); + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, ret.Object.Count - 1, null)); } + retCount++; if (chunkSize > 0 && chunkSize == ret.Object.Count) { checkDoneTimes++; @@ -219,7 +221,7 @@ namespace FreeSql.Internal.CommonProvider _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var index = -1; - ret.Object.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); + ret.Object.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, ret.Object.Count, af.fillIncludeMany)); retCount++; if (chunkSize > 0 && chunkSize == ret.Object.Count) { @@ -300,16 +302,18 @@ namespace FreeSql.Internal.CommonProvider var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); + var retCount = 0; Exception exception = null; try { _orm.Ado.ExecuteReader(_connection, _transaction, fetch => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, retCount, af.fillIncludeMany)); if (otherData != null) foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null)); + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null, retCount, null)); + retCount++; }, CommandType.Text, sql, dbParms); } catch (Exception ex) @@ -349,7 +353,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; - _commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp, null, _whereCascadeExpression, true); + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, newexp, this, null, _whereCascadeExpression, null, true); return new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); } static ConcurrentDictionary _dicGetAllFieldExpressionTree = new ConcurrentDictionary(); @@ -652,7 +656,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = -10000; //临时规则,不返回 as1 - _commonExpression.ReadAnonymousField(_tables, field, map, ref index, columns, null, _whereCascadeExpression, false); //不走 DTO 映射 + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, columns, null, null, _whereCascadeExpression, null, false); //不走 DTO 映射,不处理 IncludeMany var sql = field.ToString(); this.GroupBy(sql.Length > 0 ? sql.Substring(2) : null); return new SelectGroupingProvider(_orm, this, map, sql, _commonExpression, _tables); @@ -711,7 +715,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = 0; - _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, _whereCascadeExpression, false); //不走 DTO 映射 + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, null, _whereCascadeExpression, null, false); //不走 DTO 映射,不处理 IncludeMany return this.ToListMapReader(new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null)).FirstOrDefault(); } @@ -788,6 +792,7 @@ namespace FreeSql.Internal.CommonProvider var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); + var retCount = 0; Exception exception = null; try { @@ -798,8 +803,9 @@ namespace FreeSql.Internal.CommonProvider { var idx = af.FieldCount - 1; foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null)); + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref idx, false, null, retCount, null)); } + retCount++; return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } @@ -876,16 +882,18 @@ namespace FreeSql.Internal.CommonProvider var before = new Aop.CurdBeforeEventArgs(_tables[0].Table.Type, _tables[0].Table, Aop.CurdType.Select, sql, dbParms); _orm.Aop.CurdBeforeHandler?.Invoke(this, before); var ret = new List(); + var retCount = 0; Exception exception = null; try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, fetch => { var index = -1; - ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null)); + ret.Add((TReturn)_commonExpression.ReadAnonymous(af.map, fetch.Object, ref index, false, null, retCount, af.fillIncludeMany)); if (otherData != null) foreach (var other in otherData) - other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null)); + other.retlist.Add(_commonExpression.ReadAnonymous(other.read, fetch.Object, ref index, false, null, retCount, null)); + retCount++; return Task.FromResult(false); }, CommandType.Text, sql, dbParms); } @@ -963,7 +971,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = 0; - _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, _whereCascadeExpression, false); //不走 DTO 映射 + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select, null, null, _whereCascadeExpression, null, false); //不走 DTO 映射,不处理 IncludeMany return (await this.ToListMapReaderAsync(new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null))).FirstOrDefault(); } #endif diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 76fbf170..490acdbb 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -170,11 +170,84 @@ namespace FreeSql.Internal.CommonProvider return this.InternalSum(column.Body); } + class IncludeManyNewInit + { + public TableInfo Table { get; } + public Dictionary Childs { get; } = new Dictionary(); + public Expression CurrentExpression { get; } + public bool IsOutputPrimary { get; set; } + public IncludeManyNewInit(TableInfo table, Expression currentExpression) + { + this.Table = table; + this.CurrentExpression = currentExpression; + } + } public List ToList(Expression> select) { if (select == null) return this.InternalToList(select?.Body); _tables[0].Parameter = select.Parameters[0]; - return this.InternalToList(select.Body); + if (_includeToList?.Any() != true) return this.InternalToList(select.Body); + + var findIncludeMany = new List(); + var map = new ReadAnonymousTypeInfo(); + var field = new StringBuilder(); + var index = 0; + _commonExpression.ReadAnonymousField(_tables, field, map, ref index, select.Body, this, null, _whereCascadeExpression, findIncludeMany, true); + var af = new ReadAnonymousTypeAfInfo(map, field.Length > 0 ? field.Remove(0, 2).ToString() : null); + if (findIncludeMany.Any() == false) return this.ToListMapReaderPrivate(af, null); + + var parmExp = Expression.Parameter(_tables[0].Table.Type, _tables[0].Alias); + var incNewInit = new IncludeManyNewInit(_tables[0].Table, parmExp); + foreach (var inc in _includeInfo) + { + var curIncNewInit = incNewInit; + Expression curParmExp = parmExp; + for (var a = 0; a < inc.Value.Length - 1; a++) + { + curParmExp = Expression.MakeMemberAccess(parmExp, inc.Value[a].Member); + if (curIncNewInit.Childs.ContainsKey(inc.Value[a].Member.Name) == false) + curIncNewInit.Childs.Add(inc.Value[a].Member.Name, curIncNewInit = new IncludeManyNewInit(_orm.CodeFirst.GetTableByEntity(inc.Value[a].Type), curParmExp)); + else + curIncNewInit = curIncNewInit.Childs[inc.Value[a].Member.Name]; + } + curIncNewInit.IsOutputPrimary = true; + } + MemberInitExpression GetIncludeManyNewInitExpression(IncludeManyNewInit imni) + { + var bindings = new List(); + if (imni.IsOutputPrimary) bindings.AddRange(imni.Table.Primarys.Select(a => Expression.Bind(imni.Table.Properties[a.CsName], Expression.MakeMemberAccess(imni.CurrentExpression, imni.Table.Properties[a.CsName])))); + if (imni.Childs.Any()) bindings.AddRange(imni.Childs.Select(a => Expression.Bind(imni.Table.Properties[a.Key], GetIncludeManyNewInitExpression(a.Value)))); + return Expression.MemberInit(imni.Table.Type.InternalNewExpression(), bindings); + } + + var otherNewInit = GetIncludeManyNewInitExpression(incNewInit); //获取 IncludeMany 包含的最简化字段 + if (otherNewInit.Bindings.Any() == false) return this.ToListMapReaderPrivate(af, null); + + var otherMap = new ReadAnonymousTypeInfo(); + field.Clear(); + _commonExpression.ReadAnonymousField(_tables, field, otherMap, ref index, otherNewInit, this, null, _whereCascadeExpression, null, true); + var otherRet = new List(); + var otherAf = new ReadAnonymousTypeOtherInfo(field.ToString(), otherMap, otherRet); + + af.fillIncludeMany = new List>(); + var ret = this.ToListMapReaderPrivate(af, new[] { otherAf }); + this.SetList(otherRet.Select(a => (T1)a).ToList()); //级联加载 + + foreach (var fim in af.fillIncludeMany) + { + var splitKeys = fim.Item1.Split('.'); + var otherRetItem = otherRet[fim.Item3]; + var otherRetItemType = _tables[0].Table.Type; + foreach(var splitKey in splitKeys) + { + otherRetItem = _orm.GetEntityValueWithPropertyName(otherRetItemType, otherRetItem, splitKey); + otherRetItemType = _orm.CodeFirst.GetTableByEntity(otherRetItemType).Properties[splitKey].PropertyType; + } + if (otherRetItem == null) continue; + var otherList = otherRetItem as IEnumerable; + foreach (var otherListItem in otherList) fim.Item2.Add(otherListItem); + } + return ret; } public List ToList() => ToList(GetToListDtoSelector()); Expression> GetToListDtoSelector() @@ -1031,6 +1104,11 @@ namespace FreeSql.Internal.CommonProvider }); _includeToListAsync.Add(listObj => includeToListSyncOrAsync(listObj, true)); #endif + var includeValue = new MemberExpression[members.Count + 1]; + for (var a = 0; a < members.Count; a++) includeValue[a] = members[a]; + includeValue[includeValue.Length - 1] = expBody as MemberExpression; + var includeKey = $"{string.Join(".", includeValue.Select(a => a.Member.Name))}"; + if (_includeInfo.ContainsKey(includeKey) == false) _includeInfo.Add(includeKey, includeValue); return this; } diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs index 559befe1..3a3bfccf 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/SelectGroupingProvider.cs @@ -106,7 +106,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = 0; - _comonExp.ReadAnonymousField(null, field, map, ref index, select, this, null, false); + _comonExp.ReadAnonymousField(null, field, map, ref index, select, null, this, null, null, false); if (map.Childs.Any() == false && map.MapType == null) map.MapType = elementType; var method = _select.GetType().GetMethod(isAsync ? "ToListMapReaderAsync" : "ToListMapReader", BindingFlags.Instance | BindingFlags.NonPublic); method = method.MakeGenericMethod(elementType); @@ -118,7 +118,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = 0; - _comonExp.ReadAnonymousField(null, field, map, ref index, elementSelector, this, null, false); + _comonExp.ReadAnonymousField(null, field, map, ref index, elementSelector, null, this, null, null, false); if (map.Childs.Any() == false && map.MapType == null) map.MapType = elementType; var method = _select.GetType().GetMethod("ToListMapReaderPrivate", BindingFlags.Instance | BindingFlags.NonPublic); method = method.MakeGenericMethod(elementType); @@ -132,7 +132,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = fieldAlias == FieldAliasOptions.AsProperty ? CommonExpression.ReadAnonymousFieldAsCsName : 0; - _comonExp.ReadAnonymousField(null, field, map, ref index, select, this, null, false); + _comonExp.ReadAnonymousField(null, field, map, ref index, select, null, this, null, null, false); var method = _select.GetType().GetMethod("ToSql", new[] { typeof(string) }); return method.Invoke(_select, new object[] { field.Length > 0 ? field.Remove(0, 2).ToString() : null }) as string; } @@ -210,7 +210,7 @@ namespace FreeSql.Internal.CommonProvider var field = new StringBuilder(); var index = 0; - _comonExp.ReadAnonymousField(null, field, map, ref index, elementSelector, this, null, false); + _comonExp.ReadAnonymousField(null, field, map, ref index, elementSelector, null, this, null, null, false); if (map.Childs.Any() == false && map.MapType == null) map.MapType = typeof(TElement); var method = _select.GetType().GetMethod("ToListMapReaderPrivateAsync", BindingFlags.Instance | BindingFlags.NonPublic); method = method.MakeGenericMethod(typeof(TElement)); diff --git a/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs b/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs index c782cf5b..d2790276 100644 --- a/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs +++ b/FreeSql/Internal/Model/ReadAnonymousTypeInfo.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Text; @@ -17,6 +18,7 @@ namespace FreeSql.Internal.Model public TableInfo Table { get; set; } public bool IsEntity { get; set; } public bool IsDefaultCtor { get; set; } + public string IncludeManyKey { get; set; } //ToList(a => new { a.Childs }) 集合属性指定加载 public void CopyTo(ReadAnonymousTypeInfo target) { @@ -30,12 +32,14 @@ namespace FreeSql.Internal.Model target.Table = Table; target.IsEntity = IsEntity; target.IsDefaultCtor = IsDefaultCtor; + target.IncludeManyKey = IncludeManyKey; } } public class ReadAnonymousTypeAfInfo { public ReadAnonymousTypeInfo map { get; } public string field { get; } + public List> fillIncludeMany { get; set; } //回填集合属性的数据 public ReadAnonymousTypeAfInfo(ReadAnonymousTypeInfo map, string field) { this.map = map;