动态表达式树要过滤的嵌套集合属性 [英] Dynamic expression tree to filter on nested collection properties

查看:99
本文介绍了动态表达式树要过滤的嵌套集合属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的导航属性动态实体框架和建立查询。



有关我的大多数使用案例,以下工作正常:

 私有静态MethodCallExpression GetNavigationPropertyExpression< T>(字符串propertyName的,诠释测试,
ParameterExpression参数,串子参数)
{
VAR navigationPropertyCollection = Expression.Property(参数propertyName的);

变种childType = navigationPropertyCollection.Type.GetGenericArguments()[0];

VAR anyMethod = typeof运算(可枚举).GetMethods()单(M = GT; m.Name ==任何与&&安培; m.GetParameters()长度== 2)。 MakeGenericMethod(childType);

VAR aclAttribute = GetAclAttribute(typeof运算(T),propertyName的);
VAR childProperty = aclAttribute.ChildProperty;

VAR propertyCollectionGenericArg = childType;
VAR serviceLocationsParam = Expression.Parameter(propertyCollectionGenericArg,子参数);

无功左= Expression.Property(serviceLocationsParam,childProperty);
无功权= Expression.Constant(测试的typeof(INT));

的isEqual VAR = Expression.Equal(左,右);
无功子波长= Expression.Lambda(isEqual:方法,serviceLocationsParam);

VAR resultExpression = Expression.Call(anyMethod,navigationPropertyCollection,子波长);

返回resultExpression;
}



我用AclAttribute类通过元数据类型和局部类分配给属性的自定义。对于导航性能,提供了ChildProperty使表达式生成器知道如何寻找更深层次的所需属性。



例如:表服务引用另一个表称为ServiceLocations。我需要从ServiceLocations参考LocationId值(S)。这部分工作正常。



我的问题是,当存在多于1嵌套属性要经过。又如:表ServiceCategories引用服务这又引用ServiceLocations。像以前一样,我需要从ServiceLocations的LocationId值(S)。



我手动通过使用两个任何的方法,结合,并返回结果表达式做到了这一点,但不会有次在那里导航属性不会收藏或导航属性的一个孩子可能集合。对于这些情况,我需要某种形式的递归选项。我一直在尝试了一段时间,现在和未来的短了。



既然我提到这个问题,这里的测试方法我放在一起为我的第二个例子。这工作,但我违反干。 (注:我没有命名表):

 私有静态MethodCallExpression GetNestedNavigationPropertyExpression(INT测试,ParameterExpression rootParameter)
{
VAR servicesProperty = Expression.Property(rootParametertblServices);
VAR servicesParameter = Expression.Parameter(servicesProperty.Type.GetGenericArguments()[0],SS);

VAR serviceLocationsProperty = Expression.Property(servicesParametertblServiceLocations);
变种serviceLocationsParameter = Expression.Parameter(serviceLocationsProperty.Type.GetGenericArguments()[0],S);

VAR servicesAnyMethod = typeof运算(可枚举).GetMethods()单。(M => m.Name ==任何与&&安培; m.GetParameters()长度== 2)。 MakeGenericMethod(servicesProperty.Type.GetGenericArguments()[0]);
VAR serviceLocationsAnyMethod = typeof运算(可枚举).GetMethods()单。(M => m.Name ==任何与&&安培; m.GetParameters()长度== 2).MakeGenericMethod(serviceLocationsProperty。 Type.GetGenericArguments()[0]);

VAR aclAttribute = GetAclAttribute(typeof运算(tblService),tblServiceLocations);

无功左= Expression.Property(serviceLocationsParameter,aclAttribute.ChildProperty);
无功权= Expression.Constant(测试的typeof(INT));

的isEqual VAR = Expression.Equal(左,右);
无功子波长= Expression.Lambda(isEqual:方法,serviceLocationsParameter);

VAR endExpression = Expression.Call(serviceLocationsAnyMethod,serviceLocationsProperty,子波长);
VAR intermediaryLamba = Expression.Lambda(endExpression,servicesParameter);
VAR resultExpression = Expression.Call(servicesAnyMethod,servicesProperty,intermediaryLamba);

返回resultExpression;
}


解决方案

通过这个应该可以建立你的查询:

 公共静态表达GetNavigationPropertyExpression(Expression参数,诠释测试,则params字符串[]属性)
{
表达resultExpression = NULL;
表达childParameter,navigationPropertyPredicate;
型childType = NULL;

如果(properties.Count()> 1)
{
//构建路径
参数= Expression.Property(参数,属性[0]);
VAR isCollection = typeof运算(IEnumerable的).IsAssignableFrom(parameter.Type);
//如果it'sa收集我们后来需要使用谓词在methodexpressioncall
如果(isCollection)
{
childType = parameter.Type.GetGenericArguments()[0] ;
childParameter = Expression.Parameter(childType,childType.Name);
}
,否则
{
childParameter =参数;
}
//跳过当前的属性并获得导航属性表达recursivly
VAR innerProperties = properties.Skip(1).ToArray();
navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter,测试,innerProperties);
如果(isCollection)
{
//建立methodexpressioncall
VAR anyMethod = typeof运算(可枚举).GetMethods()单(M =方式> m.Name ==任何&放大器;&安培; m.GetParameters()长度== 2);
anyMethod = anyMethod.MakeGenericMethod(childType);
navigationPropertyPredicate = Expression.Call(anyMethod,参数,navigationPropertyPredicate);
resultExpression = MakeLambda(参数,navigationPropertyPredicate);
}
,否则
{
resultExpression = navigationPropertyPredicate;
}
}
,否则
{
//以前从ACLAttribute
VAR childProperty = parameter.Type.GetProperty(属性[0]);
变种左= Expression.Property(参数,childProperty);
无功权= Expression.Constant(测试的typeof(INT));
navigationPropertyPredicate = Expression.Equal(左,右);
resultExpression = MakeLambda(参数,navigationPropertyPredicate);
}
返回resultExpression;
}

私有静态表达MakeLambda(Expression参数,表达谓语)
{
变种resultParameterVisitor =新ParameterVisitor();
resultParameterVisitor.Visit(参数);
VAR resultParameter = resultParameterVisitor.Parameter;
返回Expression.Lambda(谓词(ParameterExpression)resultParameter);
}

私有类ParameterVisitor:ExpressionVisitor
{
公众表达参数
{
搞定;
私人集;
}
保护覆盖表达VisitParameter(ParameterExpression节点)
{
参数=节点;
返回节点;
}
}



调用它:

 变量参数= Expression.Parameter(typeof运算(A),A); 
变种表达式= ExpressionBuilder.GetNavigationPropertyExpression(参数,8,CollectionOfB,CollectionOfC,ID);


I'm using Entity Framework and building queries using navigation properties dynamically.

For most of my use cases, the following works fine:

private static MethodCallExpression GetNavigationPropertyExpression<T>(string propertyName, int test,
        ParameterExpression parameter, string subParameter)
    {
        var navigationPropertyCollection = Expression.Property(parameter, propertyName);

        var childType = navigationPropertyCollection.Type.GetGenericArguments()[0];

        var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(childType);

        var aclAttribute = GetAclAttribute(typeof(T), propertyName);
        var childProperty = aclAttribute.ChildProperty;

        var propertyCollectionGenericArg = childType;
        var serviceLocationsParam = Expression.Parameter(propertyCollectionGenericArg, subParameter);

        var left = Expression.Property(serviceLocationsParam, childProperty);
        var right = Expression.Constant(test, typeof(int));

        var isEqual = Expression.Equal(left, right);
        var subLambda = Expression.Lambda(isEqual, serviceLocationsParam);

        var resultExpression = Expression.Call(anyMethod, navigationPropertyCollection, subLambda);

        return resultExpression;
    }

I use a custom AclAttribute class assigned to properties via metadata type and partial classes. For navigation properties, a ChildProperty is provided so that the expression builder knows to look deeper for the desired property.

For example: the table Services references another table called ServiceLocations. I need the LocationId value(s) from the ServiceLocations reference. This part works fine.

My problem is when there is more than 1 nested property to go through. Another example: the table ServiceCategories references Services which, again, references ServiceLocations. Like before, I need the LocationId value(s) from ServiceLocations.

I've done this manually by using two "Any" methods, combining, and returning the resulting expression, but there will be times where navigation properties won't be collections, or a child of a navigation property may be a collection. For those cases, I need some kind of recursive option. I've been trying for a while now, and coming up short.

Since I mentioned it, here's the test method I put together for my second example. This works, but I'm violating DRY. (Note: I didn't name the tables):

private static MethodCallExpression GetNestedNavigationPropertyExpression(int test, ParameterExpression rootParameter)
    {
        var servicesProperty = Expression.Property(rootParameter, "tblServices");
        var servicesParameter = Expression.Parameter(servicesProperty.Type.GetGenericArguments()[0], "ss");

        var serviceLocationsProperty = Expression.Property(servicesParameter, "tblServiceLocations");
        var serviceLocationsParameter = Expression.Parameter(serviceLocationsProperty.Type.GetGenericArguments()[0], "s");

        var servicesAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(servicesProperty.Type.GetGenericArguments()[0]);
        var serviceLocationsAnyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2).MakeGenericMethod(serviceLocationsProperty.Type.GetGenericArguments()[0]);

        var aclAttribute = GetAclAttribute(typeof(tblService), "tblServiceLocations");

        var left = Expression.Property(serviceLocationsParameter, aclAttribute.ChildProperty);
        var right = Expression.Constant(test, typeof(int));

        var isEqual = Expression.Equal(left, right);
        var subLambda = Expression.Lambda(isEqual, serviceLocationsParameter);

        var endExpression = Expression.Call(serviceLocationsAnyMethod, serviceLocationsProperty, subLambda);
        var intermediaryLamba = Expression.Lambda(endExpression, servicesParameter);
        var resultExpression = Expression.Call(servicesAnyMethod, servicesProperty, intermediaryLamba);

        return resultExpression;
    }

解决方案

With this it should be possible to build your queries:

public static Expression GetNavigationPropertyExpression(Expression parameter, int test, params string[] properties)
{
    Expression resultExpression = null;
    Expression childParameter, navigationPropertyPredicate;
    Type childType = null;

    if (properties.Count() > 1)
    {
        //build path
        parameter = Expression.Property(parameter, properties[0]);
        var isCollection = typeof(IEnumerable).IsAssignableFrom(parameter.Type);
        //if it´s a collection we later need to use the predicate in the methodexpressioncall
        if (isCollection)
        {
            childType = parameter.Type.GetGenericArguments()[0];
            childParameter = Expression.Parameter(childType, childType.Name);
        }
        else
        {
            childParameter = parameter;
        }
        //skip current property and get navigation property expression recursivly
        var innerProperties = properties.Skip(1).ToArray();
        navigationPropertyPredicate = GetNavigationPropertyExpression(childParameter, test, innerProperties);
        if (isCollection)
        {
            //build methodexpressioncall
            var anyMethod = typeof(Enumerable).GetMethods().Single(m => m.Name == "Any" && m.GetParameters().Length == 2);
            anyMethod = anyMethod.MakeGenericMethod(childType);
            navigationPropertyPredicate = Expression.Call(anyMethod, parameter, navigationPropertyPredicate);
            resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
        }
        else
        {
            resultExpression = navigationPropertyPredicate;
        }
    }
    else
    {
        //Formerly from ACLAttribute
        var childProperty = parameter.Type.GetProperty(properties[0]);
        var left = Expression.Property(parameter, childProperty);
        var right = Expression.Constant(test, typeof(int));
        navigationPropertyPredicate = Expression.Equal(left, right);
        resultExpression = MakeLambda(parameter, navigationPropertyPredicate);
    }
    return resultExpression;
} 

private static Expression MakeLambda(Expression parameter, Expression predicate)
{
    var resultParameterVisitor = new ParameterVisitor();
    resultParameterVisitor.Visit(parameter);
    var resultParameter = resultParameterVisitor.Parameter;
    return Expression.Lambda(predicate, (ParameterExpression)resultParameter);
}

private class ParameterVisitor : ExpressionVisitor
{
    public Expression Parameter
    {
        get;
        private set;
    }
    protected override Expression VisitParameter(ParameterExpression node)
    {
        Parameter = node;
        return node;
    }
}

Call it like:

var parameter = Expression.Parameter(typeof(A), "A");
var expression = ExpressionBuilder.GetNavigationPropertyExpression(parameter, 8,"CollectionOfB", "CollectionOfC", "ID");

这篇关于动态表达式树要过滤的嵌套集合属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆