C# LINQ 匿名类 动态排序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C# LINQ 匿名类 动态排序相关的知识,希望对你有一定的参考价值。

条件
var f=from s in db select new a1=s.a1,a2=s.a2;
问题
想对f 进行动态排序
比如传入 按降序 a1 就按a1降序排列,传入升序a2就按a2升序排列
传入参数 字符串 desc和asc 分别代表降序和升序
传入参数 字符串 a1 或a2 甚至a3 a4 都是字符串类型
f.OrderByDescending(s => Convert.ToString(s.GetType().GetProperty("a1").GetValue(s, null))); 没有成功
用网上提供动态排序 Expression.Lambda 方法 却不知道匿名类的 类型
给出能完成此功能的代码,谢谢,万分感谢!

private static IEnumerable<T> Sort<T>(IEnumerable<T> source, string propertyName, bool asc)
        
            Func<T, object> func = s => s.GetType().GetProperty(propertyName).GetValue(s, null);
            if (asc)
                return source.OrderBy(func);
            return source.OrderByDescending(func);
        


//这代码测试通过的


这面是用Expression完成的,加了缓存,效率会高很多

       

private static readonly ConcurrentDictionary<string, Delegate> exp_handlers = new ConcurrentDictionary<string, Delegate>();
        private static IEnumerable<T> Sort<T>(IEnumerable<T> source, string propertyName, bool asc)
        
            var key = typeof(T).FullName + "," + propertyName;
            var func = (Func<T, object>)exp_handlers.GetOrAdd(key, k => 
                var exp_param = Expression.Parameter(typeof(T), "param");
                var exp_property = Expression.Property(exp_param, propertyName);
                var exp_return = Expression.Convert(exp_property, typeof(object));
                return Expression.Lambda<Func<T, object>>(Expression.Block(exp_property, exp_return), exp_param).Compile();
            );
 
            //Func<T, object> func = s => s.GetType().GetProperty(propertyName).GetValue(s, null);
            if (asc)
                return source.OrderBy(func);
            return source.OrderByDescending(func);
        

 使用方式

var a = Sort(f, "a1", false)//按a1属性倒序排

追问

返回 无法按类型“System.Object”排序。

追答

你是ef吗,还是linq to sql

追问

Linq to sql 吧

参考技术A var fnew = (from s in f
        orderby s.GetType().GetProperty("a2").GetValue(s, null) descending
        select s).ToList();

参考技术B 强转为dynamic类型就可以了。
f.OrderByDescending(s => ((dynamic)s).a1);追问

详细一点吧

追答

我后面补充了,你可以试试。

追问

a1是字符串,指定排序, 我也知道 f.OrderByDescending(s => s.a1); 这样就可以了,不用强转

追答

那就更好了啥,我其实想说的是如果一个匿名类对象你要访问它的成员除了使用反射,你还可以把它强转为dynamic类型,然后就可以直接用.来访问了。

参考技术C 重写Comparable的sort方法

C# 动态Linq 建立模糊查询通用工具类

微软文档

基于运行时状态进行查询 (C#)

问题描述

假设你有多个实体类型:

record Person(string LastName, string FirstName, DateTime DateOfBirth);
record Car(string Model, int Year);

对于这些实体类型中的任何一个,你都需要筛选并仅返回那些在其某个 string 字段内具有给定文本的实体。 对于 Person,你希望搜索 FirstName 和 LastName 属性:

string term = /* ... */;
var personsQry = new List<Person>()
    .AsQueryable()
    .Where(x => x.FirstName.Contains(term) || x.LastName.Contains(term));

但对于 Car,你希望仅搜索 Model 属性:

string term = /* ... */;
var carsQry = new List<Car>()
    .AsQueryable()
    .Where(x => x.Model.Contains(term));

尽管可以为 IQueryable<Person> 编写一个自定义函数,并为 IQueryable<Car> 编写另一个自定义函数,但以下函数会将此筛选添加到任何现有查询,而不考虑特定的元素类型如何。

微软示例

// using static System.Linq.Expressions.Expression;

IQueryable<T> TextFilter<T>(IQueryable<T> source, string term)
{
    if (string.IsNullOrEmpty(term)) { return source; }

    // T is a compile-time placeholder for the element type of the query.
    Type elementType = typeof(T);

    // Get all the string properties on this specific type.
    PropertyInfo[] stringProperties =
        elementType.GetProperties()
            .Where(x => x.PropertyType == typeof(string))
            .ToArray();
    if (!stringProperties.Any()) { return source; }

    // Get the right overload of String.Contains
    MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;

    // Create a parameter for the expression tree:
    // the 'x' in 'x => x.PropertyName.Contains("term")'
    // The type of this parameter is the query's element type
    ParameterExpression prm = Parameter(elementType);

    // Map each property to an expression tree node
    IEnumerable<Expression> expressions = stringProperties
        .Select(prp =>
            // For each property, we have to construct an expression tree node like x.PropertyName.Contains("term")
            Call(                  // .Contains(...) 
                Property(          // .PropertyName
                    prm,           // x 
                    prp
                ),
                containsMethod,
                Constant(term)     // "term" 
            )
        );

    // Combine all the resultant expression nodes using ||
    Expression body = expressions
        .Aggregate(
            (prev, current) => Or(prev, current)
        );

    // Wrap the expression body in a compile-time-typed lambda expression
    Expression<Func<T, bool>> lambda = Lambda<Func<T, bool>>(body, prm);

    // Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
    return source.Where(lambda);
}

由于 TextFilter 函数采用并返回 IQueryable(而不仅仅是 IQueryable),因此你可以在文本筛选器后添加更多的编译时类型的查询元素。

var qry = TextFilter(
        new List<Person>().AsQueryable(), 
        "abcd"
    )
    .Where(x => x.DateOfBirth < new DateTime(2001, 1, 1));

var qry1 = TextFilter(
        new List<Car>().AsQueryable(), 
        "abcd"
    )
    .Where(x => x.Year == 2010);

利用反射 建立模糊查询通用工具类

public static IQueryable<T> TextFilter<T>(IQueryable<T> source, T param)
        {
            // 如果参数为空
            if (param == null) { return source; }
            // 获取对象参数
            Type elementType = typeof(T);
            // 在这个类型参数中获取所有的string类型
            // Get all the string properties on this specific type.
            PropertyInfo[] stringProperties =
                elementType.GetProperties()
                    .Where(x => x.PropertyType == typeof(string))
                    .ToArray();
            // 数组是否包含任何元素
            if (!stringProperties.Any()) { return source; }
            // Get the right overload of String.Contains
            // 创建 String 方法中的 Contains 重载
            MethodInfo containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            // Create a parameter for the expression tree:
            // the 'x' in 'x => x.PropertyName.Contains("term")'
            // The type of this parameter is the query's element type
            // 这是 Linq 表达式中的 x   the 'x' in 'x => x.PropertyName.Contains("term")'
            ParameterExpression prm = Expression.Parameter(elementType);

            List<Expression> list = new List<Expression>();
            // 遍历每个string类型
            foreach (PropertyInfo prp in stringProperties)
            {
                // 获取string类型的值
                String propertyValue = (String)prp.GetValue(param);
                // 如果是空,或者是null,不做处理
                if (String.IsNullOrEmpty(propertyValue))
                {
                    continue;
                }
                // 定义表达式
                var expression = Expression.Call(                  // .Contains(...) 
                    Expression.Property(                           // .PropertyName
                        prm,                                       // x 
                        prp
                    ),
                    containsMethod,
                    Expression.Constant(propertyValue)     // "term" 
                );
                list.Add(expression);
            }
            var expressions = list as IEnumerable<Expression>;
            // Combine all the resultant expression nodes using ||
            Expression body = expressions
                .Aggregate(
                    (prev, current) => Expression.And(prev, current)
                );

            // Wrap the expression body in a compile-time-typed lambda expression
            Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(body, prm);

            // Because the lambda is compile-time-typed (albeit with a generic parameter), we can use it with the Where method
            return source.Where(lambda);
        }

调用

List<Person> src = new List<Person>() { new Person { LastName = "abcd", FirstName = "abcd" }, new Person { LastName = "abcd", FirstName = "a" }, new Person { LastName = "a", FirstName = "abcd" } };
            List<Car> src1 = new List<Car>() { new Car { Model = "abcd" }, new Car { Model = "a" }, new Car { Model = "c" } };
            var query1 = DynamicLinqUtils.TextFilter(
                src.AsQueryable(),
               new Person { LastName = "c", FirstName = "c" }
            )
           .Where(x => x.DateOfBirth < new DateTime(2001, 1, 1));

            var list1 = query1.ToList();
            var query2 = DynamicLinqUtils.TextFilter(
                src1.AsQueryable(),
               new Car { Model = "c" }
            );
            var list2 = query2.ToList();

以上是关于C# LINQ 匿名类 动态排序的主要内容,如果未能解决你的问题,请参考以下文章

像我们在 C# linq 中一样在 java 8 中获取匿名对象

具有匿名类型的 C# LINQ 构建表达式

如何在 LINQ to DataSet 的匿名类中使用基于查询数据的条件?

C#中的匿名类型

C#泛型委托,匿名方法,匿名类

在linq查询中以匿名类型设置动态对象的所有属性