From b701ad8421bcebaa525b736290392520acac15f0 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Wed, 4 Nov 2020 18:03:47 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20IncludeByPropertyName?= =?UTF-8?q?=20=E6=8C=89=E5=B1=9E=E6=80=A7=E5=90=8D=E8=BF=9B=E8=A1=8C=20Inc?= =?UTF-8?q?lude/IncludeMany=20=E6=93=8D=E4=BD=9C=EF=BC=9B#278?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql.Tests/FreeSql.Tests/UnitTest3.cs | 25 ++++++++++++++++ FreeSql/FreeSql.xml | 7 +++++ FreeSql/Interface/Curd/ISelect/ISelect1.cs | 7 +++++ .../SelectProvider/Select0Provider.cs | 4 +-- .../SelectProvider/Select1Provider.cs | 30 +++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs index 626486b4..490e7498 100644 --- a/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs +++ b/FreeSql.Tests/FreeSql.Tests/UnitTest3.cs @@ -191,6 +191,10 @@ namespace FreeSql.Tests { [Column(IsIdentity = true)] public int? Id { get; set; } + + public string name { get; set; } + [Navigate(nameof(tshop01.cateId))] + public List tshops { get; set; } } public class tshop01 { @@ -203,7 +207,28 @@ namespace FreeSql.Tests [Fact] public void Test03() { + g.sqlite.Delete().Where("1=1").ExecuteAffrows(); + g.sqlite.Delete().Where("1=1").ExecuteAffrows(); + var tshoprepo = g.sqlite.GetRepository(); + tshoprepo.DbContextOptions.EnableAddOrUpdateNavigateList = true; + tshoprepo.Insert(new tcate01[] + { + new tcate01 { name = "tcate1", tshops = new List{ new tshop01(), new tshop01(), new tshop01() } }, + new tcate01 { name = "tcate1", tshops = new List{ new tshop01(), new tshop01(), new tshop01() } } + }); + var tshop01sql = g.sqlite.Select().Include(a => a.cate).ToSql(); + var tshop02sql = g.sqlite.Select().IncludeByPropertyName("cate").ToSql(); + + var tshop03sql = g.sqlite.Select().IncludeMany(a => a.cate.tshops).ToSql(); + var tshop04sql = g.sqlite.Select().IncludeByPropertyName("cate.tshops").ToSql(); + + var tshop01lst = g.sqlite.Select().Include(a => a.cate).ToList(); + var tshop02lst = g.sqlite.Select().IncludeByPropertyName("cate").ToList(); + + var tshop03lst = g.sqlite.Select().IncludeMany(a => a.cate.tshops).ToList(); + var tshop04lst = g.sqlite.Select().IncludeByPropertyName("cate.tshops").ToList(); + var testisnullsql1 = g.sqlite.Select().Where(a => SqlExt.IsNull(a.isxx, false).Equals( true)).ToSql(); diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 3844b008..b0083f17 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -2345,6 +2345,13 @@ 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + + + 按属性名字符串进行 Include/IncludeMany 操作 + + + + 实现 select .. from ( select ... from t ) a 这样的功能 diff --git a/FreeSql/Interface/Curd/ISelect/ISelect1.cs b/FreeSql/Interface/Curd/ISelect/ISelect1.cs index 88c8d412..cefb5ced 100644 --- a/FreeSql/Interface/Curd/ISelect/ISelect1.cs +++ b/FreeSql/Interface/Curd/ISelect/ISelect1.cs @@ -333,6 +333,13 @@ namespace FreeSql /// ISelect IncludeMany(Expression>> navigateSelector, Action> then = null) where TNavigate : class; + /// + /// 按属性名字符串进行 Include/IncludeMany 操作 + /// + /// + /// + ISelect IncludeByPropertyName(string property); + /// /// 实现 select .. from ( select ... from t ) a 这样的功能 /// 使用 AsTable 方法也可以达到效果 diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index bafaf7be..9d1d8782 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -121,13 +121,13 @@ namespace FreeSql.Internal.CommonProvider to._whereGlobalFilter = new List(from._whereGlobalFilter.ToArray()); } - public Expression ConvertStringPropertyToExpression(string property) + public Expression ConvertStringPropertyToExpression(string property, bool fromFirstTable = false) { if (string.IsNullOrEmpty(property)) return null; var field = property.Split('.').Select(a => a.Trim()).ToArray(); Expression exp = null; - if (field.Length == 1) + if (field.Length == 1 && fromFirstTable == false) { foreach (var tb in _tables) { diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs index cc8e1e7a..896d31f7 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select1Provider.cs @@ -392,6 +392,36 @@ namespace FreeSql.Internal.CommonProvider public int InsertInto(string tableName, Expression> select) where TTargetEntity : class => base.InternalInsertInto(tableName, select); + public ISelect IncludeByPropertyName(string property) + { + var exp = ConvertStringPropertyToExpression(property, true); + if (exp == null) throw new ArgumentException($"{nameof(property)} 无法解析为表达式树"); + var memExp = exp as MemberExpression; + if (memExp == null) throw new ArgumentException($"{nameof(property)} 无法解析为表达式树2"); + var parTb = _commonUtils.GetTableByEntity(memExp.Expression.Type); + if (parTb == null) throw new ArgumentException($"{nameof(property)} 无法解析为表达式树3"); + var parTbref = parTb.GetTableRef(memExp.Member.Name, true); + if (parTbref == null) throw new ArgumentException($"{nameof(property)} 不是有效的导航属性"); + switch (parTbref.RefType) + { + case TableRefType.ManyToMany: + case TableRefType.OneToMany: + var funcType = typeof(Func<,>).MakeGenericType(_tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(parTbref.RefEntityType)); + var navigateSelector = Expression.Lambda(funcType, exp, _tables[0].Parameter); + var incMethod = this.GetType().GetMethod("IncludeMany"); + if (incMethod == null) throw new Exception("运行时错误,反射获取 IncludeMany 方法失败"); + incMethod.MakeGenericMethod(parTbref.RefEntityType).Invoke(this, new object[] { navigateSelector, null }); + break; + case TableRefType.ManyToOne: + case TableRefType.OneToOne: + _isIncluded = true; + var curTb = _commonUtils.GetTableByEntity(exp.Type); + _commonExpression.ExpressionWhereLambda(_tables, Expression.MakeMemberAccess(exp, curTb.Properties[curTb.ColumnsByCs.First().Value.CsName]), null, null, null); + break; + } + return this; + } + bool _isIncluded = false; public ISelect Include(Expression> navigateSelector) where TNavigate : class {