Creating nested conditional expression trees

I am trying to dynamically build some sql queries depending on the given configuration only for queries of the necessary data:

When writing a simple linq, it will look like this:

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
});

whereas it TableOne.FirstTableTwoReference.Invoke(t1)is determined

public static Expression<Func<TableOne, TableTwo>> FirstTableTwoReference => (t1) => t1.TableTwoReferences.FirstOrDefault();

Currently, to build a dynamic TableOne table, the following exists:

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);
}

same for TableTwo(different properties and another db table).

This I can dynamically call like this:

dbContext.TableOne.Select (t => TableOneHelper.Init (cfg) .Invoke (t1));

whereas Invoke- this value is from LinqKit.

But I am stuck with the inside for TableOneTableTwoReferencewhere I need to make an listing to call Initfrom TableTwoHelper, but I don’t understand how this can be achieved.

, Expression.NewArrayInit(typeof(TableTwo), ...) . , t1.TableTwoReferences.FirstOrDefault() , Select on.

+6
2

, Expression.NewArrayInit(typeof(TableTwo), ...) . , t1.TableTwoReferences.FirstOrDefault() , Select on.

, , ,

new[] { TableOne.FirstTableTwoReference.Invoke(t1) }

. , Expression.NewArrayInit. , params Expression[] initializers, LINQKit Invoke Expression.Invoke TableOne.FirstTableTwoReference lambda theEntity ( "t1" ):

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 call:

var t2FirstOrDefault = Expression.Call(
    typeof(Enumerable), "FirstOrDefault", new[] { t2Selector.Body.Type },
    t2Select);

, , :

memberBindings.Add(Expression.Bind(
    selectType.GetProperty("TableOneTableTwoReference"),
    t2FirstOrDefault));

" linq".

+1

...

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);
}
0

Source: https://habr.com/ru/post/1016197/


All Articles