C# 访问特定于类型的属性时对基类型集合的动态Linq查询

C# 访问特定于类型的属性时对基类型集合的动态Linq查询,c#,linq,dynamic-linq,C#,Linq,Dynamic Linq,如何在基本类型(如IPerson接口)的集合上运行动态LINQ查询,但访问特定于实现的属性(如年龄) 我可以确定集合中的所有项目都是相同的,也就是说,查看第一种类型,我可以假设其他项目是相同的 我需要一个UI,可以应用过滤器到不同的集合,用户看到所有可用的属性 下面是我想做的一个示例,Expression.Call方法抛出一个异常: using System; using System.Collections.Generic; using System.Linq; using System.Li

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

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

我需要一个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();
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Linq.Expressions;
使用System.Linq.Dynamic;
使用DynamicExpression=System.Linq.Dynamic.DynamicExpression;
名称空间动态ClinQTest
{
公共接口IPerson
{
字符串名称{get;set;}
}
公共类人士:IPerson
{
公共字符串名称{get;set;}
公共整数{get;set;}
公共双收入{get;set;}
}
班级计划
{
公共静态IEnumerable GetPersons()
{
收益回报新人{Name=“Sam”,年龄=26岁,收入=50000};
收益回报新人{Name=“Rick”,年龄=27,收入=0};
收益回报新人{Name=“Joe”,年龄=45岁,收入=35000};
收益回报新人{Name=“Bill”,年龄=31岁,收入=40000};
收益回报新人{Name=“Fred”,年龄=56岁,收入=155000};
} 
静态void Main(字符串[]参数)
{
IEnumerable persons=GetPersons();
var personsquerable=persons.AsQueryable();
//我想做的是:
//可询问的人员。其中(“年龄>30”);
var l=DynamicExpression.ParseLambda(persons.First().GetType(),typeof(bool),“年龄>30”);
var filtered=personsQueriable.Provider.CreateQuery(
表情,打电话(
typeof(可查询),“Where”,
新类型[]{persons.First().GetType()},
personsquerable.Expression,Expression.Quote(l));
ObjectDumper.Write(已过滤);
Console.Read();
}
}
}

您正在生成以下代码:

persons.Where((Person 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
的类型为
IEnumerable
,不能强制转换为
IEnumerable。您需要添加对
Queryable.Cast
的调用,以将
IPerson
对象强制转换为
Person

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

但是,请注意,实际上您在这里枚举了四次人员。如果它来自一个列表,它没有太大的影响。如果原始可枚举项来自数据库查询,则可能需要确保只枚举一次。在列表中获取结果,然后确保所有的
第一个
调用和表达式都应用于该列表。

甜蜜的解决方案!我也在走那条路,但我不知道怎么走。非常感谢!我知道多重枚举的问题,我只是确保示例尽可能简单。