2016-02-11 232 views
2

我希望能够动态地构建表达式,它本质上是一个属性选择器。动态创建一个选择对象属性的表达式

我想使用这个,所以我可以提供一个灵活的搜索用户界面,然后将选定的搜索参数转换为实体框架查询。

我有大部分需要感谢我使用的另一个库,但缺少将我的查询字符串参数转换为其他库所需的适当表达式选择器的最后部分。

库中取得的参数:

Expression<Func<TObject, TPropertyType>> 

如何,如果烘烤成一个应用程序将是这将被编码的一个例子:

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

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

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

伪代码:

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但可能是其他类型。

+0

你有'ObjectType'作为'Type'或泛型类型参数吗?你打算如何在库中调用正确版本的方法? –

+0

您有这些类型的ExpressionBuilder类,但我认为可能有更简单的方法来解决您的问题。哪个图书馆是“其他图书馆”,以及你在什么类别上调用什么方法? – Tewr

+0

我有ObjectType作为泛型类型参数 – Kramer00

回答

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

谢谢。我认为你有针对我的问题,因为我没有ObjectPropertyType作为泛型类型参数。 我将不得不从物体确定ObjectTypeProperty键入如下: 类型ObjectPropertyType = typeof运算(对象类型).GetProperty(ObjectPropertyName).Property – Kramer00

+0

如果有有''ObjectPropertyType作为'Type',则需要使用反射调用你拥有的类库中的方法。你能提供类库中方法的完整签名吗? –

+0

@ Kramer00我为您提供了详细信息和实施方法,因为它只能通过反射完成。检查我的答案。 – vendettamit

0

有老intresting库DynamicLinq。可能会对你有用。它扩展了System.Linq.Dynamic以支持在字符串中定义的Lambda表达式的执行。随着使用DynamicLinq你可以做somethink,如:

public class IndexViewModel 
    { 
     public bool HasPassword { get; set; } 
     public string PhoneNumber { get; set; } 
     public bool TwoFactor { get; set; } 
     public bool BrowserRemembered { get; set; } 
    } 

    //........... 

    Expression<Func<IndexViewModel, bool>> ex = 
    System.Linq.Dynamic.DynamicExpression.ParseLambda<IndexViewModel, bool>("TwoFactor"); 
     var model = new ReactJs.NET.Models.IndexViewModel() { TwoFactor = true }; 
     var res = ex.Compile()(model); 
     // res == true 
     System.Diagnostics.Debug.Assert(res); 
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

做你所问的有点棘手,但并非不可能。由于属性类型在运行时间之前是未知的,因此您无法声明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"); 

现在,这将建立你的财产返回类型所需的表达。但由于我们不知道你的库,但我建议你通过反射调用你的库方法,并传递包装在对象下的表达式。