diff --git a/FreeSql.Tests/FreeSql.Tests.csproj b/FreeSql.Tests/FreeSql.Tests.csproj index cfe75c0d..d12b032a 100644 --- a/FreeSql.Tests/FreeSql.Tests.csproj +++ b/FreeSql.Tests/FreeSql.Tests.csproj @@ -7,7 +7,7 @@ - + diff --git a/FreeSql.Tests/UnitTest1.cs b/FreeSql.Tests/UnitTest1.cs index 2bfea54e..75085107 100644 --- a/FreeSql.Tests/UnitTest1.cs +++ b/FreeSql.Tests/UnitTest1.cs @@ -96,6 +96,9 @@ namespace FreeSql.Tests { public int M2Id { get; set; } + [Column(IsIgnore = true)] + public List TestManys { get; set; } + } public class Model2 { @@ -133,11 +136,28 @@ namespace FreeSql.Tests { var includet1 = g.sqlite.Select() .IncludeMany(a => a.Childs, s => s.Where(a => a.id > 0)) + .IncludeMany(a => a.TestManys.Where(b => b.id == a.id)) .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/FreeSql.csproj b/FreeSql/FreeSql.csproj index fb201dc6..ffbb4a96 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.5.16 + 0.5.17 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index c6f7dded..ae999852 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1271,12 +1271,12 @@ 选择一个导航属性 - + 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 - 选择一个集合的导航属性 + 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index 278d49f1..d3881e32 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -305,9 +305,9 @@ namespace FreeSql { /// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 /// /// - /// 选择一个集合的导航属性 + /// 选择一个集合的导航属性,也可通过 .Where 设置临时的关系映射 /// 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) /// - ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class; + ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class; } } \ No newline at end of file diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index e9a8ff8e..edefa288 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -316,10 +316,20 @@ namespace FreeSql.Internal.CommonProvider { static MethodInfo GetEntityValueWithPropertyNameMethod = typeof(EntityUtilExtensions).GetMethod("GetEntityValueWithPropertyName"); static ConcurrentDictionary> _dicTypeMethod = new ConcurrentDictionary>(); - public ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class { + public ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class { + var throwNavigateSelector = new Exception("IncludeMany 参数1 类型错误,表达式类型应该为 MemberAccess"); + var expBody = navigateSelector?.Body; if (expBody == null) return this; - if (expBody.NodeType != ExpressionType.MemberAccess) throw new Exception("IncludeMany 参数1 类型错误,表达式类型应该为 MemberAccess"); + MethodCallExpression whereExp = null; + if (expBody.NodeType == ExpressionType.Call) { + throwNavigateSelector = new Exception($"IncludeMany {nameof(navigateSelector)} 参数类型错误,表达式格式应该是 a.collection.Where(c => c.aid == a.id)"); + whereExp = (expBody as MethodCallExpression); + if (whereExp.Method.Name != "Where") throw throwNavigateSelector; + expBody = whereExp.Object ?? whereExp.Arguments.FirstOrDefault(); + } + + if (expBody.NodeType != ExpressionType.MemberAccess) throw throwNavigateSelector; var collMem = expBody as MemberExpression; Expression tmpExp = collMem.Expression; var members = new Stack(); @@ -335,11 +345,67 @@ namespace FreeSql.Internal.CommonProvider { isbreak = true; break; default: - throw new Exception("IncludeMany 参数1 类型错误"); + throw throwNavigateSelector; } } var tb = _commonUtils.GetTableByEntity(collMem.Expression.Type); - if (tb == null) throw new Exception("IncludeMany 参数1 类型错误"); + if (tb == null) throw throwNavigateSelector; + var tbNav = _commonUtils.GetTableByEntity(typeof(TNavigate)); + if (tbNav == null) throw new Exception($"类型 {typeof(TNavigate).FullName} 错误,不能使用 IncludeMany"); + TableRef tbref = null; + + if (whereExp == null) { + + tbref = tb.GetTableRef(collMem.Member.Name, true); + + } else { + //处理临时关系映射 + tbref = new TableRef { + RefType = TableRefType.OneToMany, + Property = tb.Properties[collMem.Member.Name], + RefEntityType = tbNav.Type + }; + foreach (var whereExpArg in whereExp.Arguments) { + if (whereExpArg.NodeType != ExpressionType.Lambda) continue; + var whereExpArgLamb = whereExpArg as LambdaExpression; + + Action actWeiParse = null; + actWeiParse = expOrg => { + var binaryExp = expOrg as BinaryExpression; + if (binaryExp == null) throw throwNavigateSelector; + + switch (binaryExp.NodeType) { + case ExpressionType.AndAlso: + actWeiParse(binaryExp.Left); + actWeiParse(binaryExp.Right); + break; + case ExpressionType.Equal: + var leftP1MemberExp = binaryExp.Left as MemberExpression; + var rightP1MemberExp = binaryExp.Right as MemberExpression; + if (leftP1MemberExp == null || rightP1MemberExp == null) throw throwNavigateSelector; + if (leftP1MemberExp.Expression != tmpExp && leftP1MemberExp.Expression != whereExpArgLamb.Parameters[0] || + rightP1MemberExp.Expression != tmpExp && rightP1MemberExp.Expression != whereExpArgLamb.Parameters[0]) throw throwNavigateSelector; + + if (leftP1MemberExp.Expression == tmpExp && rightP1MemberExp.Expression == whereExpArgLamb.Parameters[0]) { + tbref.Columns.Add(tb.ColumnsByCs[leftP1MemberExp.Member.Name]); + tbref.RefColumns.Add(tbNav.ColumnsByCs[rightP1MemberExp.Member.Name]); + return; + } + if (rightP1MemberExp.Expression == tmpExp && leftP1MemberExp.Expression == whereExpArgLamb.Parameters[0]) { + tbref.Columns.Add(tb.ColumnsByCs[rightP1MemberExp.Member.Name]); + tbref.RefColumns.Add(tbNav.ColumnsByCs[leftP1MemberExp.Member.Name]); + return; + } + + throw throwNavigateSelector; + default: throw throwNavigateSelector; + } + }; + actWeiParse(whereExpArgLamb.Body); + break; + } + if (tbref.Columns.Any() == false) throw throwNavigateSelector; + } if (collMem.Expression.NodeType != ExpressionType.Parameter) _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(collMem.Expression, tb.Properties[tb.ColumnsByCs.First().Value.CsName]), null); @@ -349,7 +415,6 @@ namespace FreeSql.Internal.CommonProvider { if (list == null) return; if (list.Any() == false) return; - var tbref = tb.GetTableRef(collMem.Member.Name, true); if (tbref.Columns.Any() == false) return; var t1parm = Expression.Parameter(typeof(T1));