Постройте дерево выражений, используя Expression.Синтаксис динамически - PullRequest
2 голосов
/ 01 февраля 2011

У меня есть следующий код, который создает IQueryable

Expression<Func<TParentEntity, TChildEntity, bool>> joinExpr =
    ctx.GetJoinExpression<TParentEntity, TChildEntity>();

Expression<Func<TChildEntity, bool>> childSelectionExpression =
    GetExpression<TChildEntity>(ctx);

return
    from parentEntity in ctx.GetQueryable<TParentEntity>()
        .AsExpandable()
    let childEntities = 
        from child in ctx.GetQueryable<TChildEntity>()
        where joinExpr.Invoke(parentEntity, child)
        select child
    where childEntities.Any(childSelectionExpression)
    select parentEntity;

Я пытался (безуспешно) построить дерево выражений, которое бы возвращало тот же результат.Я хотел бы иметь выражение Expression<Func<TParentEntity,bool> parentExpression, которое я мог бы использовать следующим образом:

from parentEntity in ctx.GetQueryable<TParentEntity>()
    .AsExpandable()
where parentExpression.Invoke(parentEntity)
select parentEntity

Кто-нибудь думает, что это возможно?Я верю в это, но я не могу решить это ...

Ответы [ 2 ]

0 голосов
/ 24 февраля 2011

Eureaka: -)

Метод в конце этого поста возвращает выражение типа Expression<Func<TParentEntity,bool>>, назовем его родительским выражением.

При использовании так:


var data = (from parentEntity in parentEntities.AsExpandable() where parentExpression.Invoke(parentEntity) select parentEntiy).ToList()


То, что происходит на самом деле, выглядит примерно так:


var data = (from parentEntity in parentEntities.AsExpandable() let childeren = childEntities.Where(c => c.parentId = parentEntity.Id) where childeren.Any([childSelectionLambda]) select parent).ToList()


способный выразить дочерние критерии в одной лямбде типа
Expression<Func<TParentEntity, bool>>, легко объединить несколько дочерних критериев (которые выражаются в качестве родительских критериев) или другие (нормальные) родительские критерии с помощью PredicateBuilder.And ().

Тесты зеленого цвета для EF и linq to SQL (и sql ce): -)

override public Expression<Func<TParentEntity, bool>> 
        GetParentExpression<TParentEntity>( IDynamicQueryAdapter ctx ) 
    {

        // Define parameters needed in expresion tree
        ParameterExpression parentParameter = 
            Expression.Parameter (typeof (TParentEntity), "parent");
        ParameterExpression childParameter = 
            Expression.Parameter (typeof (TChildEntity), "child");

        // Define the IQueryable<TChildEntity> as 
        // a constant for use in the expression tree.
        IQueryable<TChildEntity> childDatasource = 
            ctx.GetQueryable<TChildEntity>().AsExpandable();
        ConstantExpression childDatasourceConstant = 
            Expression.Constant (childDatasource);

        // Get MethodInfo instance, needed for the MethodCallExpression
        MethodInfo anyMethodInfoChildEntity = 
            QueryHelper.GetQueryableAnyMethod<TChildEntity> ();

        // Get the lambda expression
        // required to select only those child entities
        // which meet the user defined criteria
        Expression<Func<TChildEntity, bool>> childSelectionExpression = 
            GetExpression<TChildEntity> (ctx);

        // Use the ExpressionParameter childParamter for the
        // ChildSelectionExpression and strip Expression.Invoke using Expand()
        Expression<Func<TChildEntity, bool>> lambda5 =
            Expression.Lambda<Func<TChildEntity, bool>> (
            Expression.Invoke (childSelectionExpression, childParameter),
                    childParameter).Expand ();


        #region Express the parent child relation
        PropertyInfo parentKeyPropertyInfo = null;
        PropertyInfo childKeyPropertyInfo = null;
        ctx.GetParentChildAssociationProperties (
            typeof (TParentEntity), typeof (TChildEntity),
            out parentKeyPropertyInfo, out childKeyPropertyInfo);
        Expression parentPropertyExpression =
            Expression.Property (parentParameter, parentKeyPropertyInfo.Name);
        Expression childPropertyExpression =
            Expression.Property (childParameter, childKeyPropertyInfo.Name);

        if( childKeyPropertyInfo.PropertyType != parentKeyPropertyInfo.PropertyType )
        {
            // TODO: what if the property types are incomparable >> exception.
            // some more code is needed!!
            Type nullableParentType = 
                typeof (Nullable<>)
                .MakeGenericType (parentKeyPropertyInfo.PropertyType);

            if( childKeyPropertyInfo.PropertyType == nullableParentType )
            {
                childPropertyExpression =
                    Expression.Convert (childPropertyExpression,
                        parentKeyPropertyInfo.PropertyType);
            }
            else if( childKeyPropertyInfo.PropertyType.IsValueType )
            {
                Type nullableChildType =
                    typeof (Nullable<>).MakeGenericType (childKeyPropertyInfo.PropertyType);
                if( parentKeyPropertyInfo.PropertyType == nullableChildType )
                {
                    parentPropertyExpression =
                        Expression.Convert (parentPropertyExpression,
                        childKeyPropertyInfo.PropertyType);
                }
            }
        } 
        #endregion
        var lambda4 = Expression.Lambda<Func<TChildEntity, bool>> (
            Expression.Equal (
                parentPropertyExpression, 
                childPropertyExpression), childParameter );

        var predicate = lambda4.And(lambda5).Expand();

        Expression<Func<TParentEntity, bool>> parentSelectionExpression = 
            Expression.Lambda<Func<TParentEntity,bool>>(
            Expression.Call (
                null, 
                anyMethodInfoChildEntity, 
                new Expression[] { childDatasourceConstant, predicate }),
            new[]{parentParameter});

        return parentSelectionExpression;
    }

примечание: PredicateBuilder, расширение AsExpandable (), расширение Expand () ... являются частью LinqKit Forбольше информации о тех посещениях: Linqkit в

0 голосов
/ 09 февраля 2011

Я знаю, что вы хотите, и, потому что я пробую это, мне жаль сообщать вам, что вы попали в мир боли. Вот частичный обходной путь:

Expression<Func<TParentEntity, TChildEntity, bool>> joinExpr =
    ctx.GetJoinExpression<TParentEntity, TChildEntity>();

Expression<Func<TChildEntity, bool>> childSelectionExpression =
    GetExpression<TChildEntity>(ctx);

return
    (from parentEntity in ctx.GetQueryable<TParentEntity>()
        .AsExpandable()
    let childEntities = 
        from child in ctx.GetQueryable<TChildEntity>()
        where joinExpr.Invoke(parentEntity, child)
        select child
    select parentEntity).Where(childSelectionExpression);

Вы должны преобразовать все вызовы запроса, которые используют переменную выражения, как я это сделал. Не всегда возможно, но я думаю, что в вашем случае это так.

...