mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
Merge pull request #1130 from hd2y/issues@1113
DynamicFilterCustom 增加支持 Expression 返回值
This commit is contained in:
commit
fe65aa0f00
@ -733,15 +733,6 @@
|
|||||||
<param name="modelBuilder"></param>
|
<param name="modelBuilder"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:FreeSqlDbContextExtensions.ApplyConfigurationsFromAssembly(FreeSql.ICodeFirst,System.Reflection.Assembly,System.Func{System.Type,System.Boolean})">
|
|
||||||
<summary>
|
|
||||||
根据Assembly扫描所有继承IEntityTypeConfiguration<T>的配置类
|
|
||||||
</summary>
|
|
||||||
<param name="codeFirst"></param>
|
|
||||||
<param name="assembly"></param>
|
|
||||||
<param name="predicate"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:FreeSqlDbContextExtensions.CreateDbContext(IFreeSql)">
|
<member name="M:FreeSqlDbContextExtensions.CreateDbContext(IFreeSql)">
|
||||||
<summary>
|
<summary>
|
||||||
创建普通数据上下文档对象
|
创建普通数据上下文档对象
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="4.0.0" />
|
<PackageReference Include="Microsoft.Data.SqlClient" Version="4.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||||
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.18" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
using FreeSql.DataAnnotations;
|
using FreeSql.DataAnnotations;
|
||||||
|
using FreeSql.Internal.Model;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Dynamic.Core;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace FreeSql.Tests.Issues
|
namespace FreeSql.Tests.Issues
|
||||||
@ -9,7 +15,7 @@ namespace FreeSql.Tests.Issues
|
|||||||
public class _1113
|
public class _1113
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PadLeft()
|
public void IncludeLevelTest()
|
||||||
{
|
{
|
||||||
using (var freeSql = new FreeSqlBuilder()
|
using (var freeSql = new FreeSqlBuilder()
|
||||||
.UseConnectionString(DataType.Sqlite, "Data Source=:memory:;")
|
.UseConnectionString(DataType.Sqlite, "Data Source=:memory:;")
|
||||||
@ -59,9 +65,153 @@ namespace FreeSql.Tests.Issues
|
|||||||
// 只能查询到Orgnization
|
// 只能查询到Orgnization
|
||||||
var list2 = freeSql.Select<Order>().IncludeMany(t => t.Orgnization.Company.Departments).ToList();
|
var list2 = freeSql.Select<Order>().IncludeMany(t => t.Orgnization.Company.Departments).ToList();
|
||||||
//freeSql.Select<Order>().IncludeMany(t => t.OrderItems, then => then.IncludeMany(t => t.Material.Units)).ToList().Dump();
|
//freeSql.Select<Order>().IncludeMany(t => t.OrderItems, then => then.IncludeMany(t => t.Material.Units)).ToList().Dump();
|
||||||
|
// 使用扩展方法加载到指定层级
|
||||||
|
var list3 = freeSql.Select<Order>().IncludeLevel(3).ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DynamicLinqTest()
|
||||||
|
{
|
||||||
|
using (var freeSql = new FreeSqlBuilder()
|
||||||
|
.UseConnectionString(DataType.Sqlite, "Data Source=:memory:;")
|
||||||
|
.UseAutoSyncStructure(true)
|
||||||
|
.Build())
|
||||||
|
{
|
||||||
|
freeSql.Aop.CurdBefore += (s, e) =>
|
||||||
|
{
|
||||||
|
Trace.WriteLine(e.Sql);
|
||||||
|
};
|
||||||
|
|
||||||
|
freeSql.Insert(
|
||||||
|
Enumerable.Range(1, 100)
|
||||||
|
.Select(i => new Topic { Title = $"new topic {i}", Clicks = 100 })
|
||||||
|
.ToList())
|
||||||
|
.ExecuteAffrows();
|
||||||
|
|
||||||
|
// 常规用法
|
||||||
|
var list1 = freeSql.Select<Topic>()
|
||||||
|
.Where(t => t.Title.StartsWith("new topic 1"))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 借助 DynamicExpressionParser
|
||||||
|
var list2 = freeSql.Select<Topic>()
|
||||||
|
.WhereDynamicLinq("Title.StartsWith(\"new topic 1\")")
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// 拓展 DynamicFilter
|
||||||
|
var dynmaicFilterInfo = new DynamicFilterInfo
|
||||||
|
{
|
||||||
|
Field = $"{nameof(DynamicLinqCustom.DynamicLinq)} {typeof(DynamicLinqCustom).FullName},{typeof(DynamicLinqCustom).Assembly.FullName}",
|
||||||
|
Operator = DynamicFilterOperator.Custom,
|
||||||
|
Value = "Title.StartsWith(\"new topic 1\")",
|
||||||
|
};
|
||||||
|
var list3 = freeSql.Select<Topic>()
|
||||||
|
.WhereDynamicFilter(dynmaicFilterInfo)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WhereByPropertyTest()
|
||||||
|
{
|
||||||
|
using (var freeSql = new FreeSqlBuilder()
|
||||||
|
.UseConnectionString(DataType.Sqlite, "Data Source=:memory:;")
|
||||||
|
.UseAutoSyncStructure(true)
|
||||||
|
.Build())
|
||||||
|
{
|
||||||
|
freeSql.Aop.CurdBefore += (s, e) =>
|
||||||
|
{
|
||||||
|
Trace.WriteLine(e.Sql);
|
||||||
|
};
|
||||||
|
|
||||||
|
var company = new Company { Id = Guid.NewGuid(), Code = "CO001" };
|
||||||
|
var department = new Department { Id = Guid.NewGuid(), Code = "D001", CompanyId = company.Id };
|
||||||
|
var orgnization = new Orgnization { Code = "C001", CompanyId = company.Id };
|
||||||
|
freeSql.Insert(company).ExecuteAffrows();
|
||||||
|
freeSql.Insert(orgnization).ExecuteAffrows();
|
||||||
|
freeSql.Insert(department).ExecuteAffrows();
|
||||||
|
|
||||||
|
var materials = new[]
|
||||||
|
{
|
||||||
|
new Material{Code="TEST1",Units=new List<Unit>{new Unit{Code = "KG"}}},
|
||||||
|
new Material{Code="TEST2",Units=new List<Unit>{new Unit{Code = "KG"}}}
|
||||||
|
};
|
||||||
|
|
||||||
|
var repo1 = freeSql.GetGuidRepository<Material>();
|
||||||
|
repo1.DbContextOptions.EnableCascadeSave = true;
|
||||||
|
repo1.Insert(materials);
|
||||||
|
|
||||||
|
|
||||||
|
var order = new Order
|
||||||
|
{
|
||||||
|
Code = "X001",
|
||||||
|
OrgnizationId = orgnization.Id,
|
||||||
|
OrderItems = new List<OrderItem>
|
||||||
|
{
|
||||||
|
new OrderItem{ ItemCode = "01", MaterialId = materials[0].Id },
|
||||||
|
new OrderItem { ItemCode = "02", MaterialId = materials[1].Id },
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var repo2 = freeSql.GetGuidRepository<Order>();
|
||||||
|
repo2.DbContextOptions.EnableCascadeSave = true;
|
||||||
|
repo2.Insert(order);
|
||||||
|
|
||||||
|
// 根据导航属性过滤数据
|
||||||
|
//var list1 = freeSql.Select<Order>().Where(t => t.OrderItems.Any(t1 => t1.Material.Units.Any(t2 => t2.Code == "KG"))).ToList();
|
||||||
|
var filterInfo1 = new DynamicFilterInfo
|
||||||
|
{
|
||||||
|
Field = "Code",
|
||||||
|
Operator = DynamicFilterOperator.Eq,
|
||||||
|
Value = "KG",
|
||||||
|
};
|
||||||
|
var list1 = freeSql.Select<Order>().Where(t => t.OrderItems.Any(t1 => t1.Material.Units.AsSelect().WhereDynamicFilter(filterInfo1).Any())).ToList();
|
||||||
|
|
||||||
|
// 导航属性如果是 OneToOne 或者 ManyToOne 默认支持
|
||||||
|
var filterInfo2 = new DynamicFilterInfo
|
||||||
|
{
|
||||||
|
Field = "Orgnization.Company.Code",
|
||||||
|
Operator = DynamicFilterOperator.Eq,
|
||||||
|
Value = "CO001",
|
||||||
|
};
|
||||||
|
//var list2 = freeSql.Select<Order>().Where(t => t.Orgnization.Company.Code == "CO001").ToList();
|
||||||
|
var list2 = freeSql.Select<Order>().WhereDynamicFilter(filterInfo2).ToList();
|
||||||
|
|
||||||
|
// 实现效果 OrderItems.Material.Units.Code == "KG"
|
||||||
|
var list3 = freeSql.Select<Order>().Where("OrderItems.Material.Units.Code", DynamicFilterOperator.Eq, "KG").ToList();
|
||||||
|
|
||||||
|
// 实现效果 OrderItems.Material.Code == "TEST1"
|
||||||
|
// Error SQL:
|
||||||
|
// SELECT a."Id", a."Code", a."OrgnizationId"
|
||||||
|
// FROM "Order" a
|
||||||
|
// WHERE (exists(SELECT 1
|
||||||
|
// FROM "OrderItem" a
|
||||||
|
// LEFT JOIN "Material" a__Material ON a__Material."Id" = a."MaterialId"
|
||||||
|
// WHERE (a__Material."Code" = 'TEST1') AND (a."OrderId" = a."Id")
|
||||||
|
// limit 0,1))
|
||||||
|
var list4 = freeSql.Select<Order>().Where("OrderItems.Material.Code", DynamicFilterOperator.Eq, "TEST1").ToList();
|
||||||
|
|
||||||
|
|
||||||
|
// 拓展 DynamicFilter
|
||||||
|
var dynmaicFilterInfo = new DynamicFilterInfo
|
||||||
|
{
|
||||||
|
Field = $"{nameof(DynamicLinqCustom.WhereNavigation)} {typeof(DynamicLinqCustom).FullName},{typeof(DynamicLinqCustom).Assembly.FullName}",
|
||||||
|
Operator = DynamicFilterOperator.Custom,
|
||||||
|
Value = JsonConvert.SerializeObject(new DynamicFilterInfo { Field = "OrderItems.Material.Units.Code", Operator = DynamicFilterOperator.Eq, Value = "KG" }),
|
||||||
|
};
|
||||||
|
var list5 = freeSql.Select<Order>()
|
||||||
|
.WhereDynamicFilter(dynmaicFilterInfo)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Topic
|
||||||
|
{
|
||||||
|
[Column(IsPrimary = true)] public Guid Id { get; set; }
|
||||||
|
public string Title { get; set; }
|
||||||
|
public int Clicks { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class Order
|
public class Order
|
||||||
{
|
{
|
||||||
@ -125,4 +275,262 @@ namespace FreeSql.Tests.Issues
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DynamicLinqCustom
|
||||||
|
{
|
||||||
|
[DynamicFilterCustom]
|
||||||
|
public static LambdaExpression DynamicLinq(object sender, string value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value)) value = "1==2";
|
||||||
|
ParameterExpression t = Expression.Parameter(sender.GetType().GetGenericArguments()[0], "t");
|
||||||
|
var exp = DynamicExpressionParser.ParseLambda(new ParameterExpression[] { t }, typeof(bool), value);
|
||||||
|
return exp;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DynamicFilterCustom]
|
||||||
|
public static LambdaExpression WhereNavigation(object sender, string value)
|
||||||
|
{
|
||||||
|
var filter = Newtonsoft.Json.JsonConvert.DeserializeObject<DynamicFilterInfo>(value);
|
||||||
|
var method = typeof(FreeSql_1113_Extensions).GetMethods().First(a => a.Name == "GetWhereExpression" && a.GetParameters().Length == 2);
|
||||||
|
method = method.MakeGenericMethod(sender.GetType().GenericTypeArguments[0]);
|
||||||
|
var exp = method.Invoke(null, new[] { sender, filter });
|
||||||
|
return exp as LambdaExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FreeSql_1113_Extensions
|
||||||
|
{
|
||||||
|
public static ISelect<T1> Where<T1>(this ISelect<T1> select, string field, DynamicFilterOperator filterOperator, object value)
|
||||||
|
{
|
||||||
|
var filter = new DynamicFilterInfo { Field = field, Operator = filterOperator, Value = value };
|
||||||
|
Expression<Func<T1, bool>> exp = GetWhereExpression(select, filter);
|
||||||
|
return select.Where(exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Expression<Func<T1, bool>> GetWhereExpression<T1>(ISelect<T1> select, DynamicFilterInfo filter)
|
||||||
|
{
|
||||||
|
var properties = filter.Field.Split('.');
|
||||||
|
var tree = TableRefTree.GetTableRefTree(select, properties.Length, properties);
|
||||||
|
|
||||||
|
// 检索
|
||||||
|
var treeList = GetTreeList(tree).ToList();
|
||||||
|
var deepest = treeList.Last();
|
||||||
|
var collectionNodes = treeList.Where(a => a.TableRef.RefType == TableRefType.OneToMany || a.TableRef.RefType == TableRefType.ManyToMany).ToList();
|
||||||
|
|
||||||
|
if (deepest.Level != properties.Length)
|
||||||
|
throw new Exception($"当前类型{typeof(T1)}导航属性{filter.Field}匹配检索失败");
|
||||||
|
if (collectionNodes.Count == 0)
|
||||||
|
throw new Exception($"当前类型{typeof(T1)}导航属性{filter.Field}不包含{TableRefType.OneToMany}或者{nameof(TableRefType.ManyToMany)}关系");
|
||||||
|
|
||||||
|
var selectMethod = typeof(FreeSqlGlobalExtensions).GetMethods().First(a => a.ContainsGenericParameters && a.GetParameters().Count() == 1);
|
||||||
|
var parameterExpression = Expression.Parameter(typeof(T1), "t");
|
||||||
|
var manyLevel = 0;
|
||||||
|
var exp = GetWhereExpression(select, deepest, filter, properties, null, ref manyLevel);
|
||||||
|
return exp as Expression<Func<T1, bool>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Expression GetWhereExpression<T1>(ISelect<T1> select, TableRefTree deepest, DynamicFilterInfo filterInfo, string[] properties, Expression body, ref int manyLevel)
|
||||||
|
{
|
||||||
|
while (deepest.Parent != null && deepest.TableRef.RefType != TableRefType.ManyToMany && deepest.TableRef.RefType != TableRefType.OneToMany)
|
||||||
|
{
|
||||||
|
deepest = deepest.Parent;
|
||||||
|
}
|
||||||
|
manyLevel++;
|
||||||
|
if (manyLevel == 1)
|
||||||
|
{
|
||||||
|
filterInfo = new DynamicFilterInfo
|
||||||
|
{
|
||||||
|
Field = string.Join(".", properties.Skip(deepest.Level - 1)),
|
||||||
|
Operator = filterInfo.Operator,
|
||||||
|
Value = filterInfo.Value,
|
||||||
|
};
|
||||||
|
deepest = deepest.Parent;
|
||||||
|
return GetWhereExpression(select, deepest, filterInfo, properties, body, ref manyLevel);
|
||||||
|
}
|
||||||
|
if (body == null)
|
||||||
|
{
|
||||||
|
body = Expression.Parameter(deepest.TableInfo.Type, $"t{deepest.Level}");
|
||||||
|
var sub = deepest;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sub = sub.Subs.First();
|
||||||
|
body = Expression.Property(body, sub.TableRef.Property);
|
||||||
|
} while (sub.TableRef.RefType != TableRefType.ManyToMany && sub.TableRef.RefType != TableRefType.OneToMany);
|
||||||
|
|
||||||
|
var selectMethod = typeof(FreeSqlGlobalExtensions).GetMethods().First(a => a.Name == "AsSelect" && a.ContainsGenericParameters && a.GetParameters().Count() == 1).MakeGenericMethod(sub.TableRef.RefEntityType);
|
||||||
|
body = Expression.Call(null, selectMethod, body);
|
||||||
|
var asMethod = typeof(ISelect<>).MakeGenericType(sub.TableRef.RefEntityType).GetMethod("As");
|
||||||
|
var constExpression = Expression.Constant($"t{deepest.Level}");
|
||||||
|
body = Expression.Call(body, asMethod, constExpression);
|
||||||
|
var whereDynamicFilterMethod = typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(sub.TableRef.RefEntityType), sub.TableRef.RefEntityType).GetMethod("WhereDynamicFilter");
|
||||||
|
body = Expression.Call(body, whereDynamicFilterMethod, Expression.Constant(filterInfo));
|
||||||
|
var anyMethod = typeof(ISelect0<,>).MakeGenericType(typeof(ISelect<>).MakeGenericType(sub.TableRef.RefEntityType), sub.TableRef.RefEntityType).GetMethod("Any");
|
||||||
|
body = Expression.Call(body, anyMethod);
|
||||||
|
|
||||||
|
deepest = deepest.Parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Expression subBody = Expression.Parameter(deepest.TableInfo.Type, $"t{deepest.Level}");
|
||||||
|
var sub = deepest;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sub = sub.Subs.First();
|
||||||
|
subBody = Expression.Property(subBody, sub.TableRef.Property);
|
||||||
|
} while (sub.TableRef.RefType != TableRefType.ManyToMany && sub.TableRef.RefType != TableRefType.OneToMany);
|
||||||
|
|
||||||
|
var selectMethod = typeof(FreeSqlGlobalExtensions).GetMethods().First(a => a.Name == "AsSelect" && a.ContainsGenericParameters && a.GetParameters().Count() == 1).MakeGenericMethod(sub.TableRef.RefEntityType);
|
||||||
|
subBody = Expression.Call(null, selectMethod, subBody);
|
||||||
|
var anyMethod = typeof(ISelect<>).MakeGenericType(sub.TableRef.RefEntityType).GetMethod("Any");
|
||||||
|
|
||||||
|
var funcType = typeof(Func<,>).MakeGenericType(sub.TableRef.RefEntityType, typeof(bool));
|
||||||
|
var parameterBody = Expression.Parameter(sub.TableInfo.Type, $"t{sub.Level}");
|
||||||
|
var lambda = Expression.Lambda(funcType, body, parameterBody);
|
||||||
|
body = Expression.Call(subBody, anyMethod, lambda);
|
||||||
|
|
||||||
|
deepest = deepest.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deepest == null)
|
||||||
|
{
|
||||||
|
var funcType = typeof(Func<,>).MakeGenericType(typeof(T1), typeof(bool));
|
||||||
|
var parameterBody = Expression.Parameter(typeof(T1), "t1");
|
||||||
|
return Expression.Lambda(funcType, body, parameterBody);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetWhereExpression(select, deepest, filterInfo, properties, body, ref manyLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<TableRefTree> GetTreeList(TableRefTree tree)
|
||||||
|
{
|
||||||
|
if (tree.Subs == null || tree.Subs.Count == 0) yield break;
|
||||||
|
yield return tree.Subs[0];
|
||||||
|
foreach (var sub in GetTreeList(tree.Subs[0]))
|
||||||
|
{
|
||||||
|
yield return sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISelect<T1> WhereDynamicLinq<T1>(this ISelect<T1> select, string expression)
|
||||||
|
{
|
||||||
|
ParameterExpression t = Expression.Parameter(typeof(T1), "t");
|
||||||
|
var exp = DynamicExpressionParser.ParseLambda(new ParameterExpression[] { t }, typeof(bool), expression);
|
||||||
|
return select.Where((Expression<Func<T1, bool>>)exp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISelect<T1> IncludeLevel<T1>(this ISelect<T1> select, int level)
|
||||||
|
{
|
||||||
|
var tree = TableRefTree.GetTableRefTree(select, level);
|
||||||
|
return select.IncludeLevel(level, tree);
|
||||||
|
}
|
||||||
|
private static ISelect<T1> IncludeLevel<T1>(this ISelect<T1> select, int level, TableRefTree tree, ParameterExpression parameterExpression = null, MemberExpression bodyExpression = null)
|
||||||
|
{
|
||||||
|
var includeMethod = select.GetType().GetMethod("Include");
|
||||||
|
var includeManyMethod = select.GetType().GetMethod("IncludeMany");
|
||||||
|
parameterExpression ??= Expression.Parameter(tree.TableInfo.Type, "t");
|
||||||
|
foreach (var sub in tree.Subs)
|
||||||
|
{
|
||||||
|
switch (sub.TableRef.RefType)
|
||||||
|
{
|
||||||
|
case TableRefType.ManyToOne:
|
||||||
|
case TableRefType.OneToOne:
|
||||||
|
{
|
||||||
|
var body = bodyExpression == null ? Expression.Property(parameterExpression, sub.TableRef.Property) : Expression.Property(bodyExpression, sub.TableRef.Property);
|
||||||
|
if (sub.Subs.Count == 0)
|
||||||
|
{
|
||||||
|
var funcType = typeof(Func<,>).MakeGenericType(parameterExpression.Type, sub.TableRef.RefEntityType);
|
||||||
|
var navigateSelector = Expression.Lambda(funcType, body, parameterExpression);
|
||||||
|
includeMethod.MakeGenericMethod(sub.TableRef.RefEntityType).Invoke(select, new object[] { navigateSelector });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
select.IncludeLevel(level, sub, parameterExpression, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToMany:
|
||||||
|
case TableRefType.OneToMany:
|
||||||
|
{
|
||||||
|
var body = bodyExpression == null ? Expression.Property(parameterExpression, sub.TableRef.Property) : Expression.Property(bodyExpression, sub.TableRef.Property);
|
||||||
|
object then = null;
|
||||||
|
if (sub.Subs.Count > 0)
|
||||||
|
{
|
||||||
|
//var thenSelectType = select.GetType().GetGenericTypeDefinition().MakeGenericType(sub.TableRef.RefEntityType);
|
||||||
|
var thenSelectType = typeof(ISelect<>).MakeGenericType(sub.TableRef.RefEntityType);
|
||||||
|
var thenType = typeof(Action<>).MakeGenericType(thenSelectType);
|
||||||
|
var thenParameter = Expression.Parameter(thenSelectType, "then");
|
||||||
|
var thenMethod = typeof(FreeSql_1113_Extensions).GetMethod(nameof(IncludeLevel)).MakeGenericMethod(sub.TableRef.RefEntityType);
|
||||||
|
var thenLevelConst = Expression.Constant(level - sub.Level + 1);
|
||||||
|
var thenBody = Expression.Call(null, thenMethod, thenParameter, thenLevelConst);
|
||||||
|
var thenExpression = Expression.Lambda(thenType, thenBody, thenParameter);
|
||||||
|
then = thenExpression.Compile();
|
||||||
|
}
|
||||||
|
var funcType = typeof(Func<,>).MakeGenericType(parameterExpression.Type, typeof(IEnumerable<>).MakeGenericType(sub.TableRef.RefEntityType));
|
||||||
|
var navigateSelector = Expression.Lambda(funcType, body, parameterExpression);
|
||||||
|
includeManyMethod.MakeGenericMethod(sub.TableRef.RefEntityType).Invoke(select, new object[] { navigateSelector, then });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckRepeat(this TableRefTree tree, List<Type> types = null)
|
||||||
|
{
|
||||||
|
if (tree.Parent == null) return false;
|
||||||
|
if (types == null)
|
||||||
|
{
|
||||||
|
types = new List<System.Type> { tree.TableInfo.Type };
|
||||||
|
return CheckRepeat(tree.Parent, types);
|
||||||
|
}
|
||||||
|
return types.Contains(tree.TableInfo.Type) || CheckRepeat(tree.Parent, types);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TableRefTree
|
||||||
|
{
|
||||||
|
public int Level { get; set; }
|
||||||
|
public TableRefTree Parent { get; set; }
|
||||||
|
public TableInfo TableInfo { get; set; }
|
||||||
|
public TableRef TableRef { get; set; }
|
||||||
|
public List<TableRefTree> Subs { get; set; }
|
||||||
|
|
||||||
|
public static TableRefTree GetTableRefTree<T1>(ISelect<T1> select, int maxLevel, string[] properties = null)
|
||||||
|
{
|
||||||
|
var orm = select.GetType().GetField("_orm").GetValue(select) as IFreeSql;
|
||||||
|
var tableInfo = orm.CodeFirst.GetTableByEntity(typeof(T1));
|
||||||
|
var tree = new TableRefTree()
|
||||||
|
{
|
||||||
|
Level = 1,
|
||||||
|
TableInfo = tableInfo,
|
||||||
|
};
|
||||||
|
tree.Subs = GetTableRefTree(orm, tree, maxLevel, properties).ToList();
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TableRefTree> GetTableRefTree(IFreeSql orm, TableRefTree tree, int maxLevel, string[] properties = null)
|
||||||
|
{
|
||||||
|
if (tree.Level > maxLevel) yield break;
|
||||||
|
var tableRefs = tree.TableInfo.Properties.Where(property => properties == null || string.Equals(properties[tree.Level - 1], property.Key, StringComparison.OrdinalIgnoreCase)).Select(a => tree.TableInfo.GetTableRef(a.Key, false)).Where(a => a != null).ToList();
|
||||||
|
foreach (var tableRef in tableRefs)
|
||||||
|
{
|
||||||
|
var tableInfo = orm.CodeFirst.GetTableByEntity(tableRef.RefEntityType);
|
||||||
|
var sub = new TableRefTree()
|
||||||
|
{
|
||||||
|
Level = tree.Level + 1,
|
||||||
|
TableInfo = tableInfo,
|
||||||
|
TableRef = tableRef,
|
||||||
|
Parent = tree,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 排除重复类型
|
||||||
|
if (sub.CheckRepeat()) continue;
|
||||||
|
|
||||||
|
sub.Subs = GetTableRefTree(orm, sub, maxLevel, properties).ToList();
|
||||||
|
yield return sub;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -832,8 +832,8 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
var fiValue0MethodReturn = fiValue0Method?.Invoke(null, fiValue0Method.GetParameters()
|
var fiValue0MethodReturn = fiValue0Method?.Invoke(null, fiValue0Method.GetParameters()
|
||||||
.Select(a => a.ParameterType == typeof(object) ? (object)this :
|
.Select(a => a.ParameterType == typeof(object) ? (object)this :
|
||||||
(a.ParameterType == typeof(string) ? (object)(fi.Value?.ToString()) : (object)null))
|
(a.ParameterType == typeof(string) ? (object)(fi.Value?.ToString()) : (object)null))
|
||||||
.ToArray())?.ToString();
|
.ToArray());
|
||||||
exp = Expression.Call(typeof(SqlExt).GetMethod("InternalRawSql", BindingFlags.NonPublic | BindingFlags.Static), Expression.Constant(fiValue0MethodReturn, typeof(string)));
|
exp = fiValue0MethodReturn is Expression expression ? expression : Expression.Call(typeof(SqlExt).GetMethod("InternalRawSql", BindingFlags.NonPublic | BindingFlags.Static), Expression.Constant(fiValue0MethodReturn?.ToString(), typeof(string)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DynamicFilterOperator.Contains:
|
case DynamicFilterOperator.Contains:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user