【问题标题】:LambdaExpression to Expression via Extensions Method通过扩展方法将 LambdaExpression 转换为表达式
【发布时间】:2016-05-03 19:11:46
【问题描述】:

我查看了这个问题的其他 SO 版本,但似乎淘汰一种方法对其他人有效。我不确定我在这里做错了什么。我是 Linq 的 Expression Building 部分的新手。

我的扩展方法如下:

void Main()
{
    var people = LoadData().AsQueryable();

    var expression = people.PropertySelector<Person>("LastName");
    expression.Should().BeOfType(typeof(Expression<Func<Person, object>>));

    var result = people.OrderBy(expression);
}

public static class Extensions
{
    public static Expression<Func<T, object>> PropertySelector<T>(this IEnumerable<T> collection, string propertyName)
    {
        if (string.IsNullOrWhiteSpace(propertyName))
        {
            throw new ArgumentException(nameof(propertyName));
        }

        var properties = typeof(T).GetProperties();
        if (!properties.Any(p => p.Name == propertyName))
        {
            throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]");
        }

        var propertyInfo = properties.Single(p => p.Name == propertyName);

        var alias = Expression.Parameter(typeof(T), "_");
        var property = Expression.Property(alias, propertyInfo);
        var funcType =  typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);
        var lambda = Expression.Lambda(funcType, property, alias);

        return (Expression<Func<T, object>>)lambda;
    }
}


#region 

private Random rand = new Random();

// Define other methods and classes here
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

public IEnumerable<Person> LoadData()
{
    IList<Person> people = new List<Person>();
    for (var i = 0; i < 15; i++)
    {
        people.Add(new Person
        {
            FirstName = $"FirstName {i}",
            LastName = $"LastName {i}",
            Age = rand.Next(1, 100)
        });
    }
    return people;
}

#endregion

在演员阵容中,我在返回时遇到了异常。此时TPerson 类型,objectstring。我的lambda.GetType() 报告它的类型是Expression&lt;Func&lt;Person, string&gt;&gt; 例外是:

Unable to cast object of type 'System.Linq.Expressions.Expression`1[System.Func`2[UserQuery+Person,System.String]]' to type 'System.Linq.Expressions.Expression`1[System.Func`2[UserQuery+Person,System.Object]]'.

我的演员阵容不正确怎么办?谢谢。

编辑: 我更新了我在LinqPad 中使用的完整代码。我真的只是想弄清楚是否有一种简单的方法可以通过传入属性名称来生成 lambda 表达式。我之前做过类似的事情,但我只是在属性名称上做一个开关,然后使用 lambda 语法动态创建 OrderBy 查询。

严格来说,这是我试图了解如何使用 Expression 静态方法来实现与以下示例相同的结果。我试图通过扩展方法模仿以下内容。但不一定是这样。在 LinqPad 中用餐时尝试一下是最简单的。

Expression<Func<Loan, object>> sortExpression;

    switch (propertyFilter)
    {
        case "Age":
            sortExpression = (l => l.Age);
            break;
        case "LastName":
            sortExpression = (l => l.LastName);
            break;
        default:
            sortExpression = (l => l.FirstName);
            break;
    }

    var sortedLoans = loans.AsQueryable().OrderBy(sortExpression);
    sortedLoans.Dump("Filtered Property Result");

【问题讨论】:

  • 类是不变的。因此,即使允许从 TFunc1 转换为 TFunc2,您也不能将 Expression&lt;TFunc1&gt; 转换为 Expression&lt;TFunc2&gt;
  • 你想用这个方法完成什么?我试图理解你为什么需要 Func。您是否尝试根据特定属性进行过滤?
  • 您可以通过Expression.Convert(property, typeof (object))投射到对象。
  • 你真的需要输出为Expression&lt;Func&lt;T, object&gt;&gt;吗?或Expression&lt;Func&lt;T, TProperty&gt;&gt;?
  • 加入其他 cmets。扩展方法也很奇怪——它“扩展”了IEnumerable&lt;T&gt; 实例(collection 参数)但不使用它。您最好展示一个示例预期用途。

标签: c# linq lambda extension-methods linq-expressions


【解决方案1】:

您的代码正在创建 Func&lt;UserQuery, String&gt;,因为您正在获取它的固有类型

var propertyInfo = properties.Single(p => p.Name == propertyName);
var funcType =  typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);

如果您想返回 Func&lt;T, object&gt;,则创建 Func&lt;T, object&gt;,而不是 Func&lt;T, (reflected property type)&gt;,否则更好的解决方案是使用 Func&lt;TOut, TIn&gt; 并创建一个完全通用的函数。

【讨论】:

    【解决方案2】:

    要保留当前方法签名,您可以这样做:

            var funcType = typeof(Func<,>).MakeGenericType(typeof(T), typeof(object));
            var typeAs = Expression.TypeAs(property, typeof(object));
            var lambda = Expression.Lambda(funcType, typeAs, alias);
    

    更好的方法是将您的方法更改为

        public static Expression<Func<T, Tout>> PropertySelector<T, Tout>(this IEnumerable<T> collection, string propertyName)
        {
            if (string.IsNullOrWhiteSpace(propertyName))
            {
                throw new ArgumentException(nameof(propertyName));
            }
    
            var properties = typeof(T).GetProperties();
            if (!properties.Any(p => p.Name == propertyName))
            {
                throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]");
            }
    
            var propertyInfo = properties.Single(p => p.Name == propertyName);
    
            var alias = Expression.Parameter(typeof(T), "_");
            var property = Expression.Property(alias, propertyInfo);
            var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType);
            var lambda = Expression.Lambda(funcType, property, alias);
    
            return (Expression<Func<T, Tout>>)lambda;
        }
    

    并调用它

            var expression = people.PropertySelector<Person, string>("LastName");
    

    【讨论】:

      【解决方案3】:

      我得到了我想要的结果。在 @Gusman、@IvanStoev 和 @PetSerAl 的 cmets 之后,我让它发挥作用。我可以继续我的探索和学习。非常感谢。最终结果是对 Propertytype 进行模板化。

        public static Expression<Func<T, TPropertyType>> PropertySelector<T, TPropertyType>(this IEnumerable<T> collection, string propertyName)
          {
              if (string.IsNullOrWhiteSpace(propertyName))
              {
                  throw new ArgumentException(nameof(propertyName));
              }
      
              var properties = typeof(T).GetProperties();
              if (!properties.Any(p => p.Name == propertyName))
              {
                  throw new ObjectNotFoundException($"Property: {propertyName} not found for type [{typeof(T).Name}]");
              }
      
              var propertyInfo = properties.Single(p => p.Name == propertyName);
      
              var alias = Expression.Parameter(typeof(T), "_");
              var property = Expression.Property(alias, propertyInfo);
              var funcType =  typeof(Func<,>).MakeGenericType(typeof(T), typeof(TPropertyType));
              var lambda = Expression.Lambda(funcType, property, alias);
      
              return (Expression<Func<T, TPropertyType>>)lambda;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-22
        • 2010-11-03
        • 1970-01-01
        • 2011-04-25
        • 1970-01-01
        相关资源
        最近更新 更多