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

发布于 2024-11-28 22:01:41 字数 1939 浏览 0 评论 0 原文

如何对基本类型(如 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();
        }
    }
}

How can I run a dynamic LINQ query on a collection of a base type (like the IPerson interface) but access implementation specific properties (like Age).

I can be sure that all items in the collection are the same, i.e. looking at the first type I can assume that the others are the same.

I need this for a UI that can apply filters to different collections, the user sees all available properties.

Here's an example of what I'd like to do, the Expression.Call method throws an exception:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

时光与爱终年不遇 2024-12-05 22:01:41

您将生成以下代码:

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

persons 的类型为 IEnumerable,无法转换为 IEnumerable>。您想要的是添加对 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 调用和表达式。

You are generating the following code:

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

persons is of type IEnumerable<IPerson>, which can't be cast to IEnumerable<Person>. What you want is to add a call to Queryable.Cast to cast the IPerson objects to Person:

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

Use the following code:

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

However, note that you're in fact enumerating four times persons here. If it comes from a list, it doesn't have much impact. If the original enumerable comes from a database query, you might want to make sure you're enumerating it only once. Get the results inside a list, then make sure all the First calls and expressions are applied on it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文