Kin*_*sin 7 c# linq expression expression-trees
我正在尝试动态构建一些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-表).
我可以像这样动态调用
dbContext.TableOne.Select(t => TableOneHelper.Init(cfg).Invoke(t1));
而Invoke来自LinqKit.
但是我对内部部分感到困惑,TableOneTableTwoReference因为我需要在其中进行枚举来调用它Init,TableTwoHelper但我不明白这是如何实现的.
我想Expression.NewArrayInit(typeof(TableTwo), ...)这将是第一步.但我仍然陷入如何传递t1.TableTwoReferences.FirstOrDefault()给这个调用Selecton的数组.
我想
Expression.NewArrayInit(typeof(TableTwo), ...)这是第一步。但我仍然陷入如何传递t1.TableTwoReferences.FirstOrDefault()到这个数组调用Selecton.
据我了解,问题是等价于的表达式是什么
new[] { TableOne.FirstTableTwoReference.Invoke(t1) }
这真的很简单。正如您所说,您需要Expression.NewArrayInit表达。但是,由于它需要params Expression[] initializers,而不是 LINQKitInvoke扩展方法,您应该使用Expression.Invoke方法来发出对TableOne.FirstTableTwoReference具有外部theEntity(“t1”)参数的 lambda 表达式的调用:
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));
这将产生相当于“plain linq”方法的结果。