在访问特定于类型的属性时,对基类型的集合进行动态Linq查询

时间:2011-08-08 17:20:42

标签: c# linq dynamic-linq

如何在基类型集合(如IPerson接口)上运行动态LINQ查询,但访问特定于实现的属性(如Age)。

我可以确定集合中的所有项目都是相同的,即查看第一种类型我可以假设其他项目是相同的。

我需要这个用于可以将过滤器应用于不同集合的UI,用户可以看到所有可用的属性。

这是我想做的一个例子,Expression.Call方法抛出异常:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Linq.Dynamic;
using DynamicExpression = System.Linq.Dynamic.DynamicExpression;

namespace DynamicLinqTest
{
    public interface IPerson
    {
        string Name { get; set; }
    }
    public class Person : IPerson
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public double Income { get; set; }
    }

    class Program
    {
        public static IEnumerable<Person> GetPersons()
        {
            yield return new Person { Name = "Sam", Age = 26, Income = 50000 };
            yield return new Person { Name = "Rick", Age = 27, Income = 0 };
            yield return new Person { Name = "Joe", Age = 45, Income = 35000 };
            yield return new Person { Name = "Bill", Age = 31, Income = 40000 };
            yield return new Person { Name = "Fred", Age = 56, Income = 155000 };
        } 

        static void Main(string[] args)
        {
            IEnumerable<IPerson> persons = GetPersons();
            var personsQueriable = persons.AsQueryable();

            //what I would like to do:
            // personsQueriable.Where("Age > 30");

            var l = DynamicExpression.ParseLambda(persons.First().GetType(), typeof(bool), "Age > 30");
            var filtered = personsQueriable.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable), "Where",
                    new Type[] { persons.First().GetType() },
                    personsQueriable.Expression, Expression.Quote(l)));

            ObjectDumper.Write(filtered);
            Console.Read();
        }
    }
}

1 个答案:

答案 0 :(得分:2)

您正在生成以下代码:

persons.Where((Person p) => p.Age > 30)

persons的类型为IEnumerable<IPerson>,无法转换为IEnumerable<Person&gt;。您想要的是添加对Queryable.Cast的调用以将IPerson个对象转换为Person

persons.Cast<Person>().Where(p => p.Age > 30)

使用以下代码:

var castedQueryable = personsQueriable.Provider.CreateQuery(
    Expression.Call(
        typeof(Queryable), "Cast",
        new Type[] { persons.First().GetType() },
        personsQueriable.Expression));

var l = DynamicExpression.ParseLambda(persons.First().GetType(), typeof(bool), "Age > 30");
var filtered = personsQueriable.Provider.CreateQuery(
    Expression.Call(
        typeof(Queryable), "Where",
        new Type[] { persons.First().GetType() },
        castedQueryable.Expression, Expression.Quote(l)));

但是,请注意,您实际上在此处列举persons四次。如果它来自列表,则没有太大影响。如果原始枚举来自数据库查询,您可能希望确保只枚举一次。在列表中获取结果,然后确保在其上应用所有First调用和表达式。

相关问题