动态创建一个选择对象属性的表达式

时间:2016-02-11 15:08:58

标签: c# lambda expression-trees

我希望能够动态构建表达式,这本质上是一个属性选择器。

我正在尝试使用此功能,因此我可以提供灵活的搜索UI,然后将选定的搜索参数转换为实体框架查询。

由于我正在使用的另一个库,我有大部分需要的东西,但我错过了最后一部分,它将我的查询字符串参数转换为另一个库所需的相应表达式选择器。

该库的参数为:

Expression<Func<TObject, TPropertyType>>

如果将其编入应用程序,将如何编码的示例如下:

Expression<Func<MyObject, int>> expression = x=> x.IntegerProperty;

但是,我需要能够动态生成这个表达式,因为重要的是我将知道的是对象的类型(MyObject)和属性名称作为字符串值(“IntegerProperty”)。属性值显然将映射到对象上的属性,该属性可以是任何非复杂类型。

基本上我认为我想找到一种动态构建表达式的方法,它指定要返回的正确对象属性以及返回值由该属性类型确定的位置。

psuedo代码:

string ObjectPropertyName
Type ObjectType
Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property

 Expression<Func<[ObjectType], [ObjectPropertyType]>> expression = x=> x.[ObjectPropertyName];

更新:

我已经达到了这个目标

ParameterExpression objectParameter = Expression.Parameter(type, "x");
MemberExpression objectProperty = Expression.Property(objectParameter, "PropertyNameString");
Expression<Func<ObjectType, int>> expression = Expression.Lambda<Func<ObjectType, int>>(objectProperty, objectParameter);

但我遇到的问题是返回类型并不总是int,但也可能是其他类型。

4 个答案:

答案 0 :(得分:3)

做你所问的有点棘手,但并非不可能。由于属性类型在运行时才知道,因此您无法声明Expression<Func<,>>,因此可以通过反射完成。

public static class QueryableExtension
{
    public static object Build<Tobject>(this Tobject source, string propertyName)
    {
        var propInfo = typeof(Tobject).GetProperty(propertyName);

        var parameter = Expression.Parameter(typeof(Tobject), "x");

        var property = Expression.Property(parameter, propInfo);

        var delegateType = typeof(Func<,>)
                           .MakeGenericType(typeof(Tobject), propInfo.PropertyType);

        var lambda = GetExpressionLambdaMethod()
                        .MakeGenericMethod(delegateType)
                        .Invoke(null, new object[] { property, new[] { parameter } });

        return lambda;
    }

    public static MethodInfo GetExpressionLambdaMethod()
    {
       return typeof(Expression)
                     .GetMethods()
                     .Where(m => m.Name == "Lambda")
                     .Select(m => new
                     {
                         Method = m,
                         Params = m.GetParameters(),
                         Args = m.GetGenericArguments()
                     })
                     .Where(x => x.Params.Length == 2
                                 && x.Args.Length == 1
                                 )
                     .Select(x => x.Method)
                     .First();
    }
}

用法 -

var expression = testObject.Build("YourPropertyName");

现在,这将使用返回类型的属性构建所需的表达式。但是因为我们不了解您的库,但我建议您通过反射调用您的库方法并传递包裹在对象下的表达式。

答案 1 :(得分:1)

正如我在评论中提到的,在不知道属性类型的情况下构建表达式很容易(即使使用嵌套属性支持):

static LambdaExpression MakeSelector(Type objectType, string path)
{
    var item = Expression.Parameter(objectType, "item");
    var body = path.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
    return Expression.Lambda(body, item);
}

但是你需要找到一种方法来调用你的通用库方法 - 使用反射或动态调用。

答案 2 :(得分:0)

如果您同时将ObjectTypeObjectPropertyType作为通用类型参数,则可以使用Expression类执行以下操作:

public static Expression<Func<TObject, TPropertyType>> Generate<TObject, TPropertyType>(
    string property_name)
{
    var parameter = Expression.Parameter(typeof (TObject));

    return Expression.Lambda<Func<TObject, TPropertyType>>(
        Expression.Property(parameter, property_name), parameter);
}

答案 3 :(得分:0)

有一个古老的有趣的图书馆DynamicLinq。可能对你有用。它扩展了System.Linq.Dynamic以支持在字符串中定义的Lambda表达式的执行。使用DynamicLinq你可以做一些事情,如:

abc.abstractproperty