mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 18:52:50 +08:00
- 增加 IncludeMany 扩展方法重载,支持字符串参数;
This commit is contained in:
parent
7425f33f24
commit
52c5ca7da3
@ -1281,6 +1281,11 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
|
|||||||
};
|
};
|
||||||
Assert.Equal(5, g.sqlite.Insert(model4s).ExecuteAffrows());
|
Assert.Equal(5, g.sqlite.Insert(model4s).ExecuteAffrows());
|
||||||
|
|
||||||
|
var by0 = g.sqlite.Select<TestInclude_OneToManyModel2>()
|
||||||
|
.Where(a => a.model2id <= model1.id)
|
||||||
|
.ToList();
|
||||||
|
by0.IncludeMany(g.sqlite, "childs", "model2111Idaaa=model2id", 2, "id");
|
||||||
|
|
||||||
var t0 = g.sqlite.Select<TestInclude_OneToManyModel2>()
|
var t0 = g.sqlite.Select<TestInclude_OneToManyModel2>()
|
||||||
.IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id))
|
.IncludeMany(a => a.childs.Where(m3 => m3.model2111Idaaa == a.model2id))
|
||||||
.Where(a => a.model2id <= model1.id)
|
.Where(a => a.model2id <= model1.id)
|
||||||
|
@ -282,8 +282,8 @@ public static partial class FreeSqlGlobalExtensions
|
|||||||
#region IncludeMany
|
#region IncludeMany
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
/// 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
||||||
/// 示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(g.sqlite, a => a.Tags);<para></para>
|
/// 示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, a => a.Tags);<para></para>
|
||||||
/// 文档:https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd#%E5%AF%BC%E8%88%AA%E5%B1%9E%E6%80%A7-onetomanymanytomany
|
/// 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T1"></typeparam>
|
/// <typeparam name="T1"></typeparam>
|
||||||
/// <typeparam name="TNavigate"></typeparam>
|
/// <typeparam name="TNavigate"></typeparam>
|
||||||
@ -309,7 +309,6 @@ public static partial class FreeSqlGlobalExtensions
|
|||||||
select.SetList(list);
|
select.SetList(list);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if net40
|
#if net40
|
||||||
#else
|
#else
|
||||||
async public static Task<List<T1>> IncludeManyAsync<T1, TNavigate>(this List<T1> list, IFreeSql orm, Expression<Func<T1, IEnumerable<TNavigate>>> navigateSelector, Action<ISelect<TNavigate>> then = null, CancellationToken cancellationToken = default) where T1 : class where TNavigate : class
|
async public static Task<List<T1>> IncludeManyAsync<T1, TNavigate>(this List<T1> list, IFreeSql orm, Expression<Func<T1, IEnumerable<TNavigate>>> navigateSelector, Action<ISelect<TNavigate>> then = null, CancellationToken cancellationToken = default) where T1 : class where TNavigate : class
|
||||||
@ -326,6 +325,109 @@ public static partial class FreeSqlGlobalExtensions
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
/// <summary>
|
||||||
|
/// 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
||||||
|
/// 示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, "Tags", "ParentId=Id", 5, "Id,Name");<para></para>
|
||||||
|
/// 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T1"></typeparam>
|
||||||
|
/// <param name="list"></param>
|
||||||
|
/// <param name="orm"></param>
|
||||||
|
/// <param name="property">选择一个集合属性</param>
|
||||||
|
/// <param name="where">设置临时的关系映射,格式:子类属性=T1属性</param>
|
||||||
|
/// <param name="take">每个子集合只取条数</param>
|
||||||
|
/// <param name="select">设置只查询部分字段</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public static List<T1> IncludeMany<T1>(this List<T1> list, IFreeSql orm, string property, string where = null, int take = 0, string select = null) where T1 : class
|
||||||
|
{
|
||||||
|
if (list == null || list.Any() == false) return list;
|
||||||
|
IncludeManyByPropertyNameCommonGetSelect<T1>(orm, property, where, take, select).SetList(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
#if net40
|
||||||
|
#else
|
||||||
|
async public static Task<List<T1>> IncludeManyAsync<T1>(this List<T1> list, IFreeSql orm, string property, string where = null, int take = 0, string select = null) where T1 : class
|
||||||
|
{
|
||||||
|
if (list == null || list.Any() == false) return list;
|
||||||
|
await IncludeManyByPropertyNameCommonGetSelect<T1>(orm, property, where, take, select).SetListAsync(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static Select1Provider<T1> IncludeManyByPropertyNameCommonGetSelect<T1>(IFreeSql orm, string property, string where = null, int take = 0, string select = null) where T1 : class
|
||||||
|
{
|
||||||
|
if (orm.CodeFirst.IsAutoSyncStructure)
|
||||||
|
{
|
||||||
|
var tb = orm.CodeFirst.GetTableByEntity(typeof(T1));
|
||||||
|
if (tb == null || tb.Primarys.Any() == false)
|
||||||
|
(orm.CodeFirst as CodeFirstProvider)._dicSycedTryAdd(typeof(T1)); //._dicSyced.TryAdd(typeof(TReturn), true);
|
||||||
|
}
|
||||||
|
var sel = orm.Select<T1>() as Select1Provider<T1>;
|
||||||
|
var exp = sel.ConvertStringPropertyToExpression(property, true);
|
||||||
|
if (exp == null) throw new ArgumentException($"{nameof(property)} 无法解析为表达式树");
|
||||||
|
var propElementType = exp.Type.GetGenericArguments().FirstOrDefault() ?? exp.Type.GetElementType();
|
||||||
|
var reftb = orm.CodeFirst.GetTableByEntity(propElementType);
|
||||||
|
if (reftb == null) throw new ArgumentException($"{nameof(property)} 参数错误,它不是集合属性,必须为 IList<T> 或者 ICollection<T>");
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(where) == false)
|
||||||
|
{
|
||||||
|
var refparamExp = Expression.Parameter(reftb.Type);
|
||||||
|
var reffuncType = typeof(Func<,>).MakeGenericType(reftb.Type, typeof(bool));
|
||||||
|
var refWhereMethod = Select0Provider.GetMethodEnumerable("Where").MakeGenericMethod(reftb.Type);
|
||||||
|
|
||||||
|
var whereSplit = where.Split(',');
|
||||||
|
Expression whereExp = null;
|
||||||
|
for (var a = 0; a < whereSplit.Length; a++)
|
||||||
|
{
|
||||||
|
var keyval = whereSplit[a].Split('=').Select(x => x.Trim()).Where(x => string.IsNullOrWhiteSpace(x) == false).ToArray();
|
||||||
|
if (keyval.Length != 2) throw new ArgumentException($"{nameof(where)} 参数错误,格式 \"TopicId=Id,多组使用逗号连接\" ");
|
||||||
|
|
||||||
|
if (reftb.ColumnsByCs.TryGetValue(keyval[0], out var keycol) == false)
|
||||||
|
throw new ArgumentException($"{nameof(where)} 参数错误,{where[a]} 不是有效的属性名,在实体类 {reftb.Type.DisplayCsharp()} 无法找到");
|
||||||
|
if (sel._tables[0].Table.ColumnsByCs.TryGetValue(keyval[1], out var valcol) == false)
|
||||||
|
throw new ArgumentException($"{nameof(where)} 参数错误,{where[a + 1]} 不是有效的属性名,在实体类 {sel._tables[0].Table.Type.DisplayCsharp()} 无法找到");
|
||||||
|
|
||||||
|
var tmpExp = Expression.Equal(
|
||||||
|
Expression.Convert(Expression.MakeMemberAccess(refparamExp, reftb.Properties[keyval[0]]), valcol.CsType),
|
||||||
|
Expression.MakeMemberAccess(sel._tables[0].Parameter, sel._tables[0].Table.Properties[keyval[1]]));
|
||||||
|
whereExp = whereExp == null ? tmpExp : Expression.And(whereExp, tmpExp);
|
||||||
|
}
|
||||||
|
whereExp = Expression.Lambda(reffuncType, whereExp, refparamExp);
|
||||||
|
exp = Expression.Call(refWhereMethod, exp, whereExp);
|
||||||
|
}
|
||||||
|
if (take > 0)
|
||||||
|
{
|
||||||
|
var takeMethod = Select0Provider.GetMethodEnumerable("Take").MakeGenericMethod(reftb.Type);
|
||||||
|
exp = Expression.Call(takeMethod, exp, Expression.Constant(take, typeof(int)));
|
||||||
|
}
|
||||||
|
if (select?.Any() == true)
|
||||||
|
{
|
||||||
|
var refparamExp = Expression.Parameter(reftb.Type);
|
||||||
|
var reffuncType = typeof(Func<,>).MakeGenericType(reftb.Type, reftb.Type);
|
||||||
|
var refWhereMethod = Select0Provider.GetMethodEnumerable("Select").MakeGenericMethod(reftb.Type, reftb.Type);
|
||||||
|
|
||||||
|
Expression memberInitExp = Expression.MemberInit(
|
||||||
|
reftb.Type.InternalNewExpression(),
|
||||||
|
select.Split(',').Select(x => x.Trim()).Where(x => string.IsNullOrWhiteSpace(x) == false).Select(a =>
|
||||||
|
{
|
||||||
|
if (reftb.ColumnsByCs.TryGetValue(a, out var col) == false)
|
||||||
|
throw new ArgumentException($"{nameof(select)} 参数错误,{a} 不是有效的属性名,在实体类 {reftb.Type.DisplayCsharp()} 无法找到");
|
||||||
|
return Expression.Bind(reftb.Properties[col.CsName], Expression.MakeMemberAccess(refparamExp, reftb.Properties[col.CsName]));
|
||||||
|
}).ToArray());
|
||||||
|
|
||||||
|
memberInitExp = Expression.Lambda(reffuncType, memberInitExp, refparamExp);
|
||||||
|
exp = Expression.Call(refWhereMethod, exp, memberInitExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcType = typeof(Func<,>).MakeGenericType(sel._tables[0].Table.Type, typeof(IEnumerable<>).MakeGenericType(reftb.Type));
|
||||||
|
var navigateSelector = Expression.Lambda(funcType, exp, sel._tables[0].Parameter);
|
||||||
|
var incMethod = sel.GetType().GetMethod("IncludeMany");
|
||||||
|
if (incMethod == null) throw new Exception("运行时错误,反射获取 IncludeMany 方法失败");
|
||||||
|
incMethod.MakeGenericMethod(reftb.Type).Invoke(sel, new object[] { navigateSelector, null });
|
||||||
|
return sel;
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region ToTreeList() 父子分类
|
#region ToTreeList() 父子分类
|
||||||
|
@ -2540,7 +2540,7 @@
|
|||||||
<member name="M:FreeSql.ISelect`1.IncludeMany``1(System.Linq.Expressions.Expression{System.Func{`0,System.Collections.Generic.IEnumerable{``0}}},System.Action{FreeSql.ISelect{``0}})">
|
<member name="M:FreeSql.ISelect`1.IncludeMany``1(System.Linq.Expressions.Expression{System.Func{`0,System.Collections.Generic.IEnumerable{``0}}},System.Action{FreeSql.ISelect{``0}})">
|
||||||
<summary>
|
<summary>
|
||||||
贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装<para></para>
|
贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装<para></para>
|
||||||
文档:https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd#%E5%AF%BC%E8%88%AA%E5%B1%9E%E6%80%A7-onetomanymanytomany
|
文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="TNavigate"></typeparam>
|
<typeparam name="TNavigate"></typeparam>
|
||||||
<param name="navigateSelector">选择一个集合的导航属性,如: .IncludeMany(a => a.Tags)<para></para>
|
<param name="navigateSelector">选择一个集合的导航属性,如: .IncludeMany(a => a.Tags)<para></para>
|
||||||
@ -4638,8 +4638,8 @@
|
|||||||
<member name="M:FreeSqlGlobalExtensions.IncludeMany``2(System.Collections.Generic.List{``0},IFreeSql,System.Linq.Expressions.Expression{System.Func{``0,System.Collections.Generic.IEnumerable{``1}}},System.Action{FreeSql.ISelect{``1}})">
|
<member name="M:FreeSqlGlobalExtensions.IncludeMany``2(System.Collections.Generic.List{``0},IFreeSql,System.Linq.Expressions.Expression{System.Func{``0,System.Collections.Generic.IEnumerable{``1}}},System.Action{FreeSql.ISelect{``1}})">
|
||||||
<summary>
|
<summary>
|
||||||
本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
||||||
示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(g.sqlite, a => a.Tags);<para></para>
|
示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, a => a.Tags);<para></para>
|
||||||
文档:https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd#%E5%AF%BC%E8%88%AA%E5%B1%9E%E6%80%A7-onetomanymanytomany
|
文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
</summary>
|
</summary>
|
||||||
<typeparam name="T1"></typeparam>
|
<typeparam name="T1"></typeparam>
|
||||||
<typeparam name="TNavigate"></typeparam>
|
<typeparam name="TNavigate"></typeparam>
|
||||||
@ -4653,6 +4653,23 @@
|
|||||||
<param name="then">即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?)</param>
|
<param name="then">即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?)</param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:FreeSqlGlobalExtensions.IncludeMany``1(System.Collections.Generic.List{``0},IFreeSql,System.String,System.String,System.Int32,System.String)">
|
||||||
|
<summary>
|
||||||
|
本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载<para></para>
|
||||||
|
示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, "Tags", "ParentId=Id", 5, "Id,Name");<para></para>
|
||||||
|
文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
|
</summary>
|
||||||
|
<typeparam name="T1"></typeparam>
|
||||||
|
<param name="list"></param>
|
||||||
|
<param name="orm"></param>
|
||||||
|
<param name="property">选择一个集合属性</param>
|
||||||
|
<param name="where">设置临时的关系映射,格式:子类属性=T1属性</param>
|
||||||
|
<param name="take">每个子集合只取条数</param>
|
||||||
|
<param name="select">设置只查询部分字段</param>
|
||||||
|
<returns></returns>
|
||||||
|
<exception cref="T:System.ArgumentException"></exception>
|
||||||
|
<exception cref="T:System.Exception"></exception>
|
||||||
|
</member>
|
||||||
<member name="M:FreeSqlGlobalExtensions.ToTreeList``1(FreeSql.ISelect{``0})">
|
<member name="M:FreeSqlGlobalExtensions.ToTreeList``1(FreeSql.ISelect{``0})">
|
||||||
<summary>
|
<summary>
|
||||||
查询数据,加工为树型 List 返回<para></para>
|
查询数据,加工为树型 List 返回<para></para>
|
||||||
|
@ -331,7 +331,7 @@ namespace FreeSql
|
|||||||
ISelect<T1> IncludeIf<TNavigate>(bool condition, Expression<Func<T1, TNavigate>> navigateSelector) where TNavigate : class;
|
ISelect<T1> IncludeIf<TNavigate>(bool condition, Expression<Func<T1, TNavigate>> navigateSelector) where TNavigate : class;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装<para></para>
|
/// 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装<para></para>
|
||||||
/// 文档:https://github.com/2881099/FreeSql/wiki/%e8%b4%aa%e5%a9%aa%e5%8a%a0%e8%bd%bd#%E5%AF%BC%E8%88%AA%E5%B1%9E%E6%80%A7-onetomanymanytomany
|
/// 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TNavigate"></typeparam>
|
/// <typeparam name="TNavigate"></typeparam>
|
||||||
/// <param name="navigateSelector">选择一个集合的导航属性,如: .IncludeMany(a => a.Tags)<para></para>
|
/// <param name="navigateSelector">选择一个集合的导航属性,如: .IncludeMany(a => a.Tags)<para></para>
|
||||||
|
@ -168,6 +168,18 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
}
|
}
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MethodInfo MethodStringContains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
|
||||||
|
public static MethodInfo MethodStringStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
|
||||||
|
public static MethodInfo MethodStringEndsWith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
|
||||||
|
static ConcurrentDictionary<string, MethodInfo> MethodEnumerableDic = new ConcurrentDictionary<string, MethodInfo>();
|
||||||
|
public static MethodInfo GetMethodEnumerable(string methodName) => MethodEnumerableDic.GetOrAdd(methodName, et =>
|
||||||
|
{
|
||||||
|
var methods = typeof(Enumerable).GetMethods().Where(a => a.Name == et);
|
||||||
|
if (et == "Select")
|
||||||
|
return methods.Where(a => a.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>)).FirstOrDefault();
|
||||||
|
return methods.FirstOrDefault();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract partial class Select0Provider<TSelect, T1> : Select0Provider, ISelect0<TSelect, T1> where TSelect : class
|
public abstract partial class Select0Provider<TSelect, T1> : Select0Provider, ISelect0<TSelect, T1> where TSelect : class
|
||||||
@ -588,11 +600,6 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
return this.OrderBy($"{field} DESC");
|
return this.OrderBy($"{field} DESC");
|
||||||
}
|
}
|
||||||
|
|
||||||
static MethodInfo MethodStringContains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
|
|
||||||
static MethodInfo MethodStringStartsWith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
|
|
||||||
static MethodInfo MethodStringEndsWith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
|
|
||||||
static ConcurrentDictionary<Type, MethodInfo> MethodEnumerableContainsDic = new ConcurrentDictionary<Type, MethodInfo>();
|
|
||||||
static MethodInfo GetMethodEnumerableContains(Type elementType) => MethodEnumerableContainsDic.GetOrAdd(elementType, et => typeof(Enumerable).GetMethods().Where(a => a.Name == "Contains").FirstOrDefault().MakeGenericMethod(elementType));
|
|
||||||
public TSelect WhereDynamicFilter(DynamicFilterInfo filter)
|
public TSelect WhereDynamicFilter(DynamicFilterInfo filter)
|
||||||
{
|
{
|
||||||
if (filter == null) return this as TSelect;
|
if (filter == null) return this as TSelect;
|
||||||
@ -686,7 +693,7 @@ namespace FreeSql.Internal.CommonProvider
|
|||||||
var fiValueAnyArray = getFiListValue();
|
var fiValueAnyArray = getFiListValue();
|
||||||
if (fiValueAnyArray.Length == 0) break;
|
if (fiValueAnyArray.Length == 0) break;
|
||||||
var fiValueAnyArrayType = exp.Type.MakeArrayType();
|
var fiValueAnyArrayType = exp.Type.MakeArrayType();
|
||||||
exp = Expression.Call(GetMethodEnumerableContains(exp.Type), Expression.Constant(Utils.GetDataReaderValue(fiValueAnyArrayType, fiValueAnyArray), fiValueAnyArrayType), exp);
|
exp = Expression.Call(GetMethodEnumerable("Contains").MakeGenericMethod(exp.Type), Expression.Constant(Utils.GetDataReaderValue(fiValueAnyArrayType, fiValueAnyArray), fiValueAnyArrayType), exp);
|
||||||
if (fi.Operator == DynamicFilterOperator.NotAny) exp = Expression.Not(exp);
|
if (fi.Operator == DynamicFilterOperator.NotAny) exp = Expression.Not(exp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user