通过扩展方法

时间:2016-05-03 19:11:47

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

我查看了这个问题的其他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

我在演员阵容中获得了一个例外。此时T类型为Personobjectstring。我的lambda.GetType()报告的类型为Expression<Func<Person, string>>,例外情况为:

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

3 个答案:

答案 0 :(得分:1)

您的代码正在创建if (0 % 3 === 0) // remainder is 0, so it is true if (1 % 3 === 0) // remainder is 1, so it is false if (2 % 3 === 0) // remainder is 2, so it is false if (3 % 3 === 0) // remainder is 0, so it is true ,因为您正在使用

创建内在类型
Func<UserQuery, String>

如果您想要返回var propertyInfo = properties.Single(p => p.Name == propertyName); var funcType = typeof(Func<,>).MakeGenericType(typeof(T), propertyInfo.PropertyType); ,请创建Func<T, object>,而不是Func<T, object>,否则更好的解决方案是使用Func<T, (reflected property type)>并创建完全通用的功能

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

我得到了我想要的结果。在@Gusman,@ IvanStoev和@PetSerAl的评论之后,我得到了它的功能。我可以继续探索和学习。非常感谢你。最终结果是模板化Propertytype。

user