我正在尝试根据给定的配置动态构建一些sql查询,以仅查询所需的数据:
编写简单的linq时,它看起来像这样:
var data = dbContext
.TableOne
.Select(t1 => new TableOneSelect
{
TableOneId = t1.TableOneId,
TableOneTableTwoReference = new[] { TableOne.FirstTableTwoReference.Invoke(t1) }
.Select(t2 => new TableTwoSelect
{
TableTowId = (Guid?)t2.TableTwoId,
// ... some more properties of t2
}).FirstOrDefault(),
// ... some more properties of t1
});
而TableOne.FirstTableTwoReference.Invoke(t1)
已定义
public static Expression<Func<TableOne, TableTwo>> FirstTableTwoReference => (t1) => t1.TableTwoReferences.FirstOrDefault();
目前我有以下动态构建TableOne-part:
public Expression<Func<TableOne, TableOneSelect>> Init(TableOneConfig cfg)
{
var memberBindings = new List<MemberBinding>();
var selectType = typeof(TableOneSelect);
var newExpression = Expression.New(selectType);
var theEntity = Expression.Parameter(typeof(TableOne), "t1");
// decide if the property is needed and add to the object-initializer
if (cfg.Select("TableOneId"))
memberBindings.Add(Expression.Bind(selectType.GetProperty("TableOneId"), Expression.Property(theEntity, nameof("TableOneId"))));
// ... check other properties of TableOneSelect depending on given config
var memberInit = Expression.MemberInit(newExpression, memberBindings);
return Expression.Lambda<Func<tblTournament, EventResourceSelect>>(memberInit, theEntity);
}
TableTwo
(不同的属性和不同的db-table)相同。
我可以像这样动态调用
dbContext.TableOne.Select(t =&gt; TableOneHelper.Init(cfg).Invoke(t1));
而Invoke
是来自LinqKit
的那个。
但我对TableOneTableTwoReference
的内部部分感到困惑,我需要在其中进行枚举以调用Init
的{{1}},但我不明白这是怎么回事实现。
我猜TableTwoHelper
将是第一步。但是我仍然不知道如何将Expression.NewArrayInit(typeof(TableTwo), ...)
传递给调用t1.TableTwoReferences.FirstOrDefault()
的数组。
答案 0 :(得分:2)
我猜
Expression.NewArrayInit(typeof(TableTwo), ...)
将是第一步。但是我仍然不知道如何将t1.TableTwoReferences.FirstOrDefault()
传递给调用Select
的数组。
据我所知,问题是什么是等同于
的表达式new[] { TableOne.FirstTableTwoReference.Invoke(t1) }
这很简单。正如您所说,您需要Expression.NewArrayInit
表达。但是,由于它需要params Expression[] initializers
,而不是LINQKit Invoke
扩展方法,您应该使用Expression.Invoke
方法使用外部TableOne.FirstTableTwoReference
发出对theEntity
lambda表达式的调用( &#34; t1&#34;)参数:
var t2Array = Expression.NewArrayInit(
typeof(TableTwo),
Expression.Invoke(TableOne.FirstTableTwoReference, theEntity));
您可以发出Select
表达式的相同方式:
var t2Selector = TableTwoHelper.Init(cfg2);
// t2Selector is Expression<Func<TableTwo, TableTwoSelect>>
var t2Select = Expression.Call(
typeof(Enumerable), "Select", new[] { t2Selector.Parameters[0].Type, t2Selector.Body.Type },
t2Array, t2Selector);
然后FirstOrDefault
致电:
var t2FirstOrDefault = Expression.Call(
typeof(Enumerable), "FirstOrDefault", new[] { t2Selector.Body.Type },
t2Select);
最后是外部成员绑定:
memberBindings.Add(Expression.Bind(
selectType.GetProperty("TableOneTableTwoReference"),
t2FirstOrDefault));
这将产生相当于你的&#34;普通linq&#34;方法
答案 1 :(得分:0)
添加成员绑定...
memberBindings.Add(Expression.Bind(selectType.GetProperty("TableOneTableTwoReference"), BuildTableTwoExpression(theEntity)));
...然后构建TableTwo
的表达式
private Expression BuildTableTwoExpression(ParameterExpression t1)
{
var arrayEx = Expression.NewArrayInit(typeof(TableTwo), Expression.Invoke(TableOne.FirstTableTwoReference, t1));
Expression<Func<TableTwo, TableTwoSelect>> selector = (t2 => new TableTwoSelect
{
TableTowId = (Guid?)t2.TableTwoId,
// ... some more properties of t2
});
Expression<Func<IEnumerable<TableTwo>, TableTwoSelect>> selectEx =
((t1s) => Enumerable.Select(t1s, selector.Compile()).FirstOrDefault());
return Expression.Invoke(selectEx, arrayEx);
}