【问题标题】:Dynamically creating an expression which selects an objects property动态创建选择对象属性的表达式
【发布时间】:2016-02-11 15:08:58
【问题描述】:

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

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

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

库接受一个参数:

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

【问题讨论】:

  • 您有ObjectType 作为Type 还是泛型类型参数?您将如何调用库中正确版本的方法?
  • 你有 ExpressionBuilder 类来处理这些事情,但我认为可能有更简单的方法来解决你的问题。哪个库是“其他库”,你在什么类上调用什么方法?
  • 我将 ObjectType 作为泛型类型参数
  • ObjectPropertyType?
  • 构建这样的表达式没有问题,但我不明白你将如何调用没有TPropertyType 的目标函数。请注意,Expression&lt;Func&lt;TObject, TPropertyType&gt;&gt; 不能被视为Expression&lt;TObject, object&gt;&gt;,反之亦然。您最好发布一个真实的示例,因为我们需要开发整个方法调用,而不仅仅是表达式。

标签: c# lambda expression-trees


【解决方案1】:

按照您的要求做有点棘手,但并非不可能。由于属性类型直到运行时才知道,所以你不能声明Expression&lt;Func&lt;,&gt;&gt;,所以它将通过反射来完成。

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

现在这将使用返回类型的属性构建您想要的表达式。但是由于我们不知道您的库,但我建议您通过反射调用您的库方法并传递包裹在对象下的表达式。

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

      【解决方案3】:

      如果你同时拥有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);
      }
      

      【讨论】:

      • 谢谢。我认为您已经为我指出了我的问题,因为我没有 ObjectPropertyType 作为泛型类型参数。我会从对象类型中确定 ObjectTypeProperty,如下所示: Type ObjectPropertyType = typeof(ObjectType).GetProperty(ObjectPropertyName).Property
      • 如果有ObjectPropertyType 作为Type,那么你需要使用反射来调用你拥有的类库中的方法。能否提供类库中方法的完整签名?
      • @Kramer00 我为您提供了如何做到这一点的详细信息和实现,因为它只能通过反射来完成。检查我的答案。
      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 2016-12-25
        • 1970-01-01
        • 1970-01-01
        • 2014-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-16
        相关资源
        最近更新 更多