diff --git a/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests.csproj index df4aa29a..cfe75c0d 100644 --- a/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs b/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs index 17dcb60e..6fbc87da 100644 --- a/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs +++ b/FreeSql.Tests/Sqlite/Curd/SqliteSelectTest.cs @@ -756,5 +756,64 @@ namespace FreeSql.Tests.Sqlite { sql = query.ToSql().Replace("\r\n", ""); Assert.Equal("SELECT a.\"Id\", a.\"Clicks\", a.\"TypeGuid\", a.\"Title\", a.\"CreateTime\" FROM \"tb_topic22AsTable1\" a LEFT JOIN \"TestTypeInfo\" b on b.\"Guid\" = a.\"TypeGuid\" and b.\"Name\" = @bname", sql); } + + [Fact] + public void Include_OneToMany() { + + } + [Fact] + public void Include_OneToChilds() { + + } + + [Fact] + public void Include_ManyToMany() { + + var tag1 = new Tag { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_01_中国" + }; + tag1.Id = (int)g.sqlite.Insert(tag1).ExecuteIdentity(); + var tag2 = new Tag { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_02_美国" + }; + tag2.Id = (int)g.sqlite.Insert(tag2).ExecuteIdentity(); + var tag3 = new Tag { + Ddd = DateTime.Now.Second, + Name = "test_manytoMany_03_日本" + }; + tag3.Id = (int)g.sqlite.Insert(tag3).ExecuteIdentity(); + + var song1 = new Song { + Create_time = DateTime.Now, + Title = "test_manytoMany_01_我是中国人.mp3", + Url = "http://ww.baidu.com/" + }; + song1.Id = (int)g.sqlite.Insert(song1).ExecuteIdentity(); + var song2 = new Song { + Create_time = DateTime.Now, + Title = "test_manytoMany_02_爱你一万年.mp3", + Url = "http://ww.163.com/" + }; + song2.Id = (int)g.sqlite.Insert(song2).ExecuteIdentity(); + var song3 = new Song { + Create_time = DateTime.Now, + Title = "test_manytoMany_03_千年等一回.mp3", + Url = "http://ww.sina.com/" + }; + song3.Id = (int)g.sqlite.Insert(song3).ExecuteIdentity(); + + g.sqlite.Insert(new Song_tag { Song_id = song1.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.sqlite.Insert(new Song_tag { Song_id = song2.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.sqlite.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag1.Id }).ExecuteAffrows(); + g.sqlite.Insert(new Song_tag { Song_id = song1.Id, Tag_id = tag2.Id }).ExecuteAffrows(); + g.sqlite.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag2.Id }).ExecuteAffrows(); + g.sqlite.Insert(new Song_tag { Song_id = song3.Id, Tag_id = tag3.Id }).ExecuteAffrows(); + + var songs = g.sqlite.Select() + .IncludeMany(a => a.Tags) + .ToList(); + } } } diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 7218e6bb..bd548652 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -94,6 +94,8 @@ namespace FreeSql.Tests { public ICollection Childs { get; set; } + public int M2Id { get; set; } + } public class Model2 { @@ -110,6 +112,32 @@ namespace FreeSql.Tests { [Fact] public void Test1() { + g.sqlite.GetRepository().Insert(new Model1 { + title = "test_" + DateTime.Now.ToString("yyyyMMddHHmmss"), + M2Id = DateTime.Now.Second + DateTime.Now.Minute, + Childs = new[] { + new Model2 { + title = "model2Test_title_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "0001", + }, + new Model2 { + title = "model2Test_title_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "0002", + }, + new Model2 { + title = "model2Test_title_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "0003", + }, + new Model2 { + title = "model2Test_title_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "0004", + } + } + }); + + var includet1 = g.sqlite.Select() + .IncludeMany(a => a.Childs, s => s.Where(a => a.id > 0)) + .Where(a => a.id > 10) + .ToList(); + + + var ttt1 = g.sqlite.Select().Where(a => a.Childs.AsSelect().Any(b => b.title == "111")).ToList(); diff --git a/FreeSql/Extensions/EntityUtilExtensions.cs b/FreeSql/Extensions/EntityUtilExtensions.cs index 94c403b5..60cef3c7 100644 --- a/FreeSql/Extensions/EntityUtilExtensions.cs +++ b/FreeSql/Extensions/EntityUtilExtensions.cs @@ -602,15 +602,28 @@ namespace FreeSql.Extensions.EntityUtil { }); if (_table.Properties.ContainsKey(pn)) { var prop = _table.Properties[pn]; - exps.Add( - Expression.Assign( - Expression.MakeMemberAccess(var1Parm, prop), - Expression.Convert( - FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(prop.PropertyType, parm3), - prop.PropertyType + + if (_table.ColumnsByCs.ContainsKey(pn)) { + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(var1Parm, prop), + Expression.Convert( + FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(prop.PropertyType, parm3), + prop.PropertyType + ) ) - ) - ); + ); + } else { + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(var1Parm, prop), + Expression.Convert( + parm3, + prop.PropertyType + ) + ) + ); + } } return Expression.Lambda>(Expression.Block(new[] { var1Parm }, exps), new[] { parm1, parm2, parm3 }).Compile(); }); diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index d4e5bcfc..c6f7dded 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1263,6 +1263,23 @@ + + + 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 + + + 选择一个导航属性 + + + + + 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 + + + 选择一个集合的导航属性 + 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + + 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index ad53e04f..278d49f1 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -293,5 +293,21 @@ namespace FreeSql { /// 列 /// ISelect OrderByDescending(Expression> column); + + /// + /// 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 + /// + /// + /// 选择一个导航属性 + /// + ISelect Include(Expression> navigateSelector) where TNavigate : class; + /// + /// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 + /// + /// + /// 选择一个集合的导航属性 + /// 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + /// + ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class; } } \ No newline at end of file diff --git a/FreeSql/Internal/CommonExpression.cs b/FreeSql/Internal/CommonExpression.cs index 72eda1dc..3895180f 100644 --- a/FreeSql/Internal/CommonExpression.cs +++ b/FreeSql/Internal/CommonExpression.cs @@ -330,7 +330,7 @@ namespace FreeSql.Internal { static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectWhereMethodInfo = new ConcurrentDictionary(); static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectWhereSqlMethodInfo = new ConcurrentDictionary(); static ConcurrentDictionary _dicExpressionLambdaToSqlAsSelectAnyMethodInfo = new ConcurrentDictionary(); - static ConcurrentDictionary _dicNullableValueProperty = new ConcurrentDictionary(); + internal static ConcurrentDictionary _dicNullableValueProperty = new ConcurrentDictionary(); static ConcurrentDictionary _dicFreeSqlGlobalExtensionsAsSelectExpression = new ConcurrentDictionary(); internal string ExpressionBinary(string oper, Expression leftExp, Expression rightExp, ExpTSC tsc) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 175aced0..08c4da0c 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -30,6 +30,7 @@ namespace FreeSql.Internal.CommonProvider { protected DbTransaction _transaction; protected DbConnection _connection; protected Action _trackToList; + protected Queue> _includeToList = new Queue>(); protected bool _distinct; protected Expression _selectExpression; @@ -69,6 +70,7 @@ namespace FreeSql.Internal.CommonProvider { toType.GetField("_transaction", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._transaction); toType.GetField("_connection", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._connection); toType.GetField("_trackToList", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._trackToList); + toType.GetField("_includeToList", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._includeToList); toType.GetField("_distinct", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._distinct); toType.GetField("_selectExpression", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(to, from._selectExpression); } @@ -318,7 +320,7 @@ namespace FreeSql.Internal.CommonProvider { return ret; }); } - List ToListPrivate(GetAllFieldExpressionTreeInfo af) { + internal List ToListPrivate(GetAllFieldExpressionTreeInfo af, (ReadAnonymousTypeInfo, List)[] otherData) { var sql = this.ToSql(af.Field); if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = $"{sql}{string.Join("|", _params.Select(a => a.Value))}"; @@ -331,6 +333,11 @@ namespace FreeSql.Internal.CommonProvider { try { _orm.Ado.ExecuteReader(_connection, _transaction, dr => { ret.Add(af.Read(_orm, dr)); + if (otherData != null) { + var idx = af.FieldCount - 1; + foreach (var other in otherData) + other.Item2.Add(_commonExpression.ReadAnonymous(other.Item1, dr, ref idx, false)); + } }, CommandType.Text, sql, dbParms); } catch (Exception ex) { exception = ex; @@ -339,12 +346,13 @@ namespace FreeSql.Internal.CommonProvider { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfter?.Invoke(this, after); } + while (_includeToList.Any()) _includeToList.Dequeue()?.Invoke(ret); _orm.Aop.ToList?.Invoke(this, new Aop.ToListEventArgs(ret)); _trackToList?.Invoke(ret); return ret; }); } - async Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af) { + async internal Task> ToListPrivateAsync(GetAllFieldExpressionTreeInfo af, (ReadAnonymousTypeInfo, List)[] otherData) { var sql = this.ToSql(af.Field); if (_cache.seconds > 0 && string.IsNullOrEmpty(_cache.key)) _cache.key = $"{sql}{string.Join("|", _params.Select(a => a.Value))}"; @@ -357,6 +365,11 @@ namespace FreeSql.Internal.CommonProvider { try { await _orm.Ado.ExecuteReaderAsync(_connection, _transaction, dr => { ret.Add(af.Read(_orm, dr)); + if (otherData != null) { + var idx = af.FieldCount - 1; + foreach (var other in otherData) + other.Item2.Add(_commonExpression.ReadAnonymous(other.Item1, dr, ref idx, false)); + } return Task.CompletedTask; }, CommandType.Text, sql, dbParms); } catch (Exception ex) { @@ -366,6 +379,7 @@ namespace FreeSql.Internal.CommonProvider { var after = new Aop.CurdAfterEventArgs(before, exception, ret); _orm.Aop.CurdAfter?.Invoke(this, after); } + while (_includeToList.Any()) _includeToList.Dequeue()?.Invoke(ret); _orm.Aop.ToList?.Invoke(this, new Aop.ToListEventArgs(ret)); _trackToList?.Invoke(ret); return ret; @@ -373,11 +387,11 @@ namespace FreeSql.Internal.CommonProvider { } public List ToList(bool includeNestedMembers = false) { if (_selectExpression != null) return this.InternalToList(_selectExpression); - return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); + return this.ToListPrivate(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } public Task> ToListAsync(bool includeNestedMembers = false) { if (_selectExpression != null) return this.InternalToListAsync(_selectExpression); - return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll()); + return this.ToListPrivateAsync(includeNestedMembers == false ? this.GetAllFieldExpressionTreeLevel2() : this.GetAllFieldExpressionTreeLevelAll(), null); } public T1 ToOne() { this.Limit(1); @@ -460,6 +474,7 @@ namespace FreeSql.Internal.CommonProvider { static ConcurrentDictionary _dicGetAllFieldExpressionTree = new ConcurrentDictionary(); public class GetAllFieldExpressionTreeInfo { public string Field { get; set; } + public int FieldCount { get; set; } public Func Read { get; set; } } protected GetAllFieldExpressionTreeInfo GetAllFieldExpressionTreeLevelAll() { @@ -579,6 +594,7 @@ namespace FreeSql.Internal.CommonProvider { }); return new GetAllFieldExpressionTreeInfo { Field = field.ToString(), + FieldCount = index, Read = Expression.Lambda>(Expression.Block(new[] { retExp, dataIndexExp, readExp }, blockExp), new[] { ormExp, rowExp }).Compile() }; }); diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index 26b9f6ea..242c304b 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -1,9 +1,14 @@ -using FreeSql.Internal.Model; +using FreeSql.Extensions.EntityUtil; +using FreeSql.Internal.Model; using System; +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; +using System.Data.Common; using System.Linq; using System.Linq.Expressions; +using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -64,7 +69,6 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = column.Parameters[0]; return this.InternalAvg(column?.Body); } - public Task AvgAsync(Expression> column) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; @@ -73,19 +77,12 @@ namespace FreeSql.Internal.CommonProvider { public abstract ISelect From(Expression, T2, ISelectFromExpression>> exp) where T2 : class;// { this.InternalFrom(exp?.Body); var ret = new Select3Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } public abstract ISelect From(Expression, T2, T3, ISelectFromExpression>> exp) where T2 : class where T3 : class;// { this.InternalFrom(exp?.Body); var ret = new Select3Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class;// { this.InternalFrom(exp?.Body); var ret = new Select4Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class;// { this.InternalFrom(exp?.Body); var ret = new Select5Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class;// { this.InternalFrom(exp?.Body); var ret = new Select6Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class;// { this.InternalFrom(exp?.Body); var ret = new Select7Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class;// { this.InternalFrom(exp?.Body); var ret = new Select8Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class;// { this.InternalFrom(exp?.Body); var ret = new Select9Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } - public abstract ISelect From(Expression, T2, T3, T4, T5, T6, T7, T8, T9, T10, ISelectFromExpression>> exp) where T2 : class where T3 : class where T4 : class where T5 : class where T6 : class where T7 : class where T8 : class where T9 : class where T10 : class;// { this.InternalFrom(exp?.Body); var ret = new Select10Provider(_orm, _commonUtils, _commonExpression, null); Select0Provider, T1>.CopyData(this, ret, exp?.Parameters); return ret; } public ISelectGrouping GroupBy(Expression> columns) { @@ -99,7 +96,6 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = column.Parameters[0]; return this.InternalMax(column?.Body); } - public Task MaxAsync(Expression> column) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; @@ -111,7 +107,6 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = column.Parameters[0]; return this.InternalMin(column?.Body); } - public Task MinAsync(Expression> column) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; @@ -123,7 +118,6 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = column.Parameters[0]; return this.InternalOrderBy(column?.Body); } - public ISelect OrderByDescending(Expression> column) { if (column == null) return this; _tables[0].Parameter = column.Parameters[0]; @@ -135,7 +129,6 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = column.Parameters[0]; return this.InternalSum(column?.Body); } - public Task SumAsync(Expression> column) { if (column == null) return Task.FromResult(default(TMember)); _tables[0].Parameter = column.Parameters[0]; @@ -152,8 +145,8 @@ namespace FreeSql.Internal.CommonProvider { _tables[0].Parameter = select.Parameters[0]; return this.InternalToListAsync(select?.Body); } - List ISelect.ToList() => ToList(GetToListDtoSelector()); - Task> ISelect.ToListAsync() => ToListAsync(GetToListDtoSelector()); + public List ToList() => ToList(GetToListDtoSelector()); + public Task> ToListAsync() => ToListAsync(GetToListDtoSelector()); Expression> GetToListDtoSelector() { var ctor = typeof(TDto).GetConstructor(new Type[0]); return Expression.Lambda>(Expression.New(ctor), @@ -310,5 +303,259 @@ namespace FreeSql.Internal.CommonProvider { public TReturn First(Expression> select) => this.ToOne(select); public Task FirstAsync(Expression> select) => this.ToOneAsync(select); + + public ISelect Include(Expression> navigateSelector) where TNavigate : class { + var expBody = navigateSelector?.Body; + if (expBody == null) return this; + var tb = _commonUtils.GetTableByEntity(expBody.Type); + if (tb == null) throw new Exception("Include 参数类型错误"); + + _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(expBody, tb.Properties[tb.ColumnsByCs.First().Value.CsName]), null); + return this; + } + + static MethodInfo GetEntityValueWithPropertyNameMethod = typeof(EntityUtilExtensions).GetMethod("GetEntityValueWithPropertyName"); + static ConcurrentDictionary> _dicTypeMethod = new ConcurrentDictionary>(); + public ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class { + var expBody = navigateSelector?.Body; + if (expBody == null) return this; + if (expBody.NodeType != ExpressionType.MemberAccess) throw new Exception("IncludeMany 参数1 类型错误,表达式类型应该为 MemberAccess"); + var collMem = expBody as MemberExpression; + Expression tmpExp = collMem.Expression; + var members = new Stack(); + var isbreak = false; + while(isbreak == false) { + switch (tmpExp.NodeType) { + case ExpressionType.MemberAccess: + var memExp = tmpExp as MemberExpression; + tmpExp = memExp.Expression; + members.Push(memExp.Member); + continue; + case ExpressionType.Parameter: + isbreak = true; + break; + default: + throw new Exception("IncludeMany 参数1 类型错误"); + } + } + var tb = _commonUtils.GetTableByEntity(collMem.Expression.Type); + if (tb == null) throw new Exception("IncludeMany 参数1 类型错误"); + + if (collMem.Expression.NodeType != ExpressionType.Parameter) + _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(collMem.Expression, tb.Properties[tb.ColumnsByCs.First().Value.CsName]), null); + var tbref = tb.GetTableRef(collMem.Member.Name, true); + + _includeToList.Enqueue(listObj => { + var list = listObj as List; + if (list == null) return; + if (list.Any() == false) return; + + var t1parm = Expression.Parameter(typeof(T1)); + Expression membersExp = t1parm; + while (members.Any()) membersExp = Expression.MakeMemberAccess(membersExp, members.Pop()); + + var listValueExp = Expression.Parameter(typeof(List), "listValue"); + var setListValue = Expression.Lambda>>( + Expression.Assign( + Expression.MakeMemberAccess(membersExp, collMem.Member), + Expression.TypeAs(listValueExp, collMem.Type) + ), t1parm, listValueExp).Compile(); + + var returnTarget = Expression.Label(typeof(object)); + var propertyNameExp = Expression.Parameter(typeof(string), "propertyName"); + var getListValue = Expression.Lambda>( + Expression.Block( + Expression.Return(returnTarget, Expression.Call(null, GetEntityValueWithPropertyNameMethod, Expression.Constant(_orm), Expression.Constant(membersExp.Type), membersExp, propertyNameExp)), + Expression.Label(returnTarget, Expression.Default(typeof(object))) + ), t1parm, propertyNameExp).Compile(); + + foreach (var item in list) { + setListValue(item, null); + } + var subSelect = _orm.Select().WithConnection(_connection).WithTransaction(_transaction).TrackToList(_trackToList); + if (_tableRules?.Any() == true) + foreach (var tr in _tableRules) subSelect.AsTable(tr); + + switch (tbref.RefType) { + case TableRefType.OneToMany: + if (true) { + var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); + if (tbref.Columns.Count == 1) { + var arrExp = Expression.NewArrayInit(tbref.Columns[0].CsType, list.Select(a => Expression.Constant(Convert.ChangeType(getListValue(a, tbref.Columns[0].CsName), tbref.Columns[0].CsType))).ToArray()); + var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); + var containsMethod = _dicTypeMethod.GetOrAdd(tbref.Columns[0].CsType, et => new ConcurrentDictionary()).GetOrAdd("Contains", mn => + typeof(Enumerable).GetMethods().Where(a => a.Name == mn).First()).MakeGenericMethod(tbref.Columns[0].CsType); + var refCol = Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]); + if (refCol.Type.IsNullableType()) refCol = Expression.Property(refCol, CommonExpression._dicNullableValueProperty.GetOrAdd(refCol.Type, ct1 => ct1.GetProperty("Value"))); + subSelect.Where(Expression.Lambda>( + Expression.Call(null, containsMethod, arrExp, refCol), otmExpParm1)); + } else { + var otmExpParm1 = Expression.Parameter(typeof(TNavigate), "a"); + Expression expOr = null; + foreach (var item in list) { + Expression expAnd = null; + for (var z = 0; z < tbref.Columns.Count; z++) { + var colVal = getListValue(item, tbref.Columns[z].CsName); + var expTmp = Expression.Equal(Expression.MakeMemberAccess(otmExpParm1, tbref2.Properties[tbref.RefColumns[0].CsName]), Expression.Constant(colVal)); + if (z == 0) expAnd = expTmp; + else expAnd = Expression.AndAlso(expAnd, expTmp); + } + if (expOr == null) expOr = expAnd; + else expOr = Expression.OrElse(expOr, expAnd); + } + subSelect.Where(Expression.Lambda>(expOr, otmExpParm1)); + } + then?.Invoke(subSelect); + var subList = subSelect.ToList(true); + 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) { + dicList.Add(getListValue(item, tbref.Columns[0].CsName).ToString(), Tuple.Create(item, new List())); + } 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)); + } + dicList.Add(sb.Remove(0, 3).ToString(), Tuple.Create(item, new List())); + 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 t1item) == false) return; + t1item.Item2.Add(nav); + + //将子集合的,多对一,对象设置为当前对象 + foreach (var parentNav in parentNavs) + _orm.SetEntityValueWithPropertyName(tbref.RefMiddleEntityType, nav, parentNav, t1item.Item1); + } + foreach (var t1item in dicList.Values) + setListValue(t1item.Item1, t1item.Item2); + dicList.Clear(); + } + break; + case TableRefType.ManyToMany: + if (true) { + var tbref2 = _commonUtils.GetTableByEntity(tbref.RefEntityType); + var tbrefMid = _commonUtils.GetTableByEntity(tbref.RefMiddleEntityType); + if (tbref.Columns.Count == 1) { + //var midParmExp = Expression.Parameter(tbref.RefMiddleEntityType, "midtb"); + //(subSelect as Select1Provider)._tables.Add(new SelectTableInfo { + // Alias = "midtb", + // AliasInit = "midtb", + // On = $"{_commonUtils.QuoteSqlName(tbrefMid.DbName)} midtb ON midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[1].Attribute.Name)} = a.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)}", + // Parameter = midParmExp, + // Table = tbrefMid, + // Type = SelectTableInfoType.InnerJoin + //}); + subSelect.InnerJoin($"{_commonUtils.QuoteSqlName(tbrefMid.DbName)} midtb ON midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[1].Attribute.Name)} = a.{_commonUtils.QuoteSqlName(tbref.RefColumns[0].Attribute.Name)}"); + subSelect.Where(_commonUtils.FormatSql($"midtb.{_commonUtils.QuoteSqlName(tbref.MiddleColumns[0].Attribute.Name)} in {{0}}", list.Select(a => getListValue(a, tbref.Columns[0].CsName)))); + + } else { + + } + then?.Invoke(subSelect); + + List subList = null; + List midList = new List(); + + var subSelectP1 = (subSelect as Select1Provider); + var af = subSelectP1.GetAllFieldExpressionTreeLevelAll(); + if (_selectExpression == null) {// return this.InternalToList(_selectExpression).Select(a => (a, ()).ToList(); + var sb = new StringBuilder().Append(af.Field); + var read = new ReadAnonymousTypeInfo(); + read.ConsturctorType = ReadAnonymousTypeInfoConsturctorType.Properties; + read.Consturctor = tbrefMid.TypeLazy.GetConstructor(new Type[0]); + read.Table = tbrefMid; + foreach (var col in tbrefMid.Columns.Values) { + if (tbref.MiddleColumns.Where(a => a.CsName == col.CsName).Any() == false) continue; + var child = new ReadAnonymousTypeInfo { + CsName = col.CsName, + CsType = col.CsType, + DbField = $"midtb.{_commonUtils.QuoteSqlName(col.Attribute.Name)}", + MapType = col.Attribute.MapType, + Property = tbrefMid.Properties[col.CsName] + }; + read.Childs.Add(child); + sb.Append(", ").Append(_commonUtils.QuoteReadColumn(child.MapType, child.DbField)); + } + af.Field = sb.ToString(); + subList = subSelectP1.ToListPrivate(af, new[] { (read, midList) }); + } else + subList = subSelectP1.ToListPrivate(af, null); + + 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) { + dicList.Add(getListValue(item, tbref.Columns[0].CsName).ToString(), Tuple.Create(item, new List())); + } 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)); + } + dicList.Add(sb.Remove(0, 3).ToString(), Tuple.Create(item, new List())); + sb.Clear(); + } + } + for (var a = 0; a < subList.Count; a++) { + string key = null; + if (tbref.Columns.Count == 1) { + key = _orm.GetEntityValueWithPropertyName(tbref.RefMiddleEntityType, midList[a], tbref.MiddleColumns[0].CsName).ToString(); + } else { + var sb = new StringBuilder(); + for (var z = 0; z < tbref.Columns.Count; z++) { + if (z > 0) sb.Append("*$*"); + sb.Append(_orm.GetEntityValueWithPropertyName(tbref.RefMiddleEntityType, midList[a], tbref.MiddleColumns[z].CsName)); + } + key = sb.ToString(); + sb.Clear(); + } + if (dicList.TryGetValue(key, out var t1item) == false) return; + t1item.Item2.Add(subList[a]); + } + foreach (var t1item in dicList.Values) + setListValue(t1item.Item1, t1item.Item2); + dicList.Clear(); + } + break; + } + }); + return this; + } } } \ No newline at end of file