IEnumerable / IQueryable 上的动态 LINQ OrderBy

2024-04-29

我在中找到了一个例子VS2008示例 http://msdn2.microsoft.com/en-us/bb330936.aspx用于动态 LINQ,允许您使用类似 SQL 的字符串(例如OrderBy("Name, Age DESC"))用于订购。不幸的是,所包含的方法仅适用于IQueryable<T>。有什么办法可以开启这个功能吗IEnumerable<T>?


刚刚无意中发现了这个老梗……

要在不使用动态 LINQ 库的情况下执行此操作,您只需要如下代码。这涵盖了最常见的场景,包括嵌套属性。

为了让它工作IEnumerable<T>你可以添加一些包装方法AsQueryable- 但下面的代码是核心Expression需要逻辑。

public static IOrderedQueryable<T> OrderBy<T>(
    this IQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "OrderBy");
}

public static IOrderedQueryable<T> OrderByDescending<T>(
    this IQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "OrderByDescending");
}

public static IOrderedQueryable<T> ThenBy<T>(
    this IOrderedQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "ThenBy");
}

public static IOrderedQueryable<T> ThenByDescending<T>(
    this IOrderedQueryable<T> source, 
    string property)
{
    return ApplyOrder<T>(source, property, "ThenByDescending");
}

static IOrderedQueryable<T> ApplyOrder<T>(
    IQueryable<T> source, 
    string property, 
    string methodName) 
{
    string[] props = property.Split('.');
    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;
    foreach(string prop in props) {
        // use reflection (not ComponentModel) to mirror LINQ
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
    LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);

    object result = typeof(Queryable).GetMethods().Single(
            method => method.Name == methodName
                    && method.IsGenericMethodDefinition
                    && method.GetGenericArguments().Length == 2
                    && method.GetParameters().Length == 2)
            .MakeGenericMethod(typeof(T), type)
            .Invoke(null, new object[] {source, lambda});
    return (IOrderedQueryable<T>)result;
}

编辑:如果你想将其与dynamic- 尽管请注意dynamic仅适用于 LINQ 到对象(ORM 等的表达式树不能真正表示dynamic查询 -MemberExpression不支持)。但这里有一种使用 LINQ-to-Objects 来实现这一点的方法。请注意,选择Hashtable是由于有利的锁定语义:

using Microsoft.CSharp.RuntimeBinder;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Runtime.CompilerServices;
static class Program
{
    private static class AccessorCache
    {
        private static readonly Hashtable accessors = new Hashtable();

        private static readonly Hashtable callSites = new Hashtable();

        private static CallSite<Func<CallSite, object, object>> GetCallSiteLocked(
            string name) 
        {
            var callSite = (CallSite<Func<CallSite, object, object>>)callSites[name];
            if(callSite == null)
            {
                callSites[name] = callSite = CallSite<Func<CallSite, object, object>>
                    .Create(Binder.GetMember(
                                CSharpBinderFlags.None, 
                                name, 
                                typeof(AccessorCache),
                                new CSharpArgumentInfo[] { 
                                    CSharpArgumentInfo.Create(
                                        CSharpArgumentInfoFlags.None, 
                                        null) 
                                }));
            }
            return callSite;
        }

        internal static Func<dynamic,object> GetAccessor(string name)
        {
            Func<dynamic, object> accessor = (Func<dynamic, object>)accessors[name];
            if (accessor == null)
            {
                lock (accessors )
                {
                    accessor = (Func<dynamic, object>)accessors[name];
                    if (accessor == null)
                    {
                        if(name.IndexOf('.') >= 0) {
                            string[] props = name.Split('.');
                            CallSite<Func<CallSite, object, object>>[] arr 
                                = Array.ConvertAll(props, GetCallSiteLocked);
                            accessor = target =>
                            {
                                object val = (object)target;
                                for (int i = 0; i < arr.Length; i++)
                                {
                                    var cs = arr[i];
                                    val = cs.Target(cs, val);
                                }
                                return val;
                            };
                        } else {
                            var callSite = GetCallSiteLocked(name);
                            accessor = target =>
                            {
                                return callSite.Target(callSite, (object)target);
                            };
                        }
                        accessors[name] = accessor;
                    }
                }
            }
            return accessor;
        }
    }

    public static IOrderedEnumerable<dynamic> OrderBy(
        this IEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.OrderBy<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> OrderByDescending(
        this IEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.OrderByDescending<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> ThenBy(
        this IOrderedEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.ThenBy<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    public static IOrderedEnumerable<dynamic> ThenByDescending(
        this IOrderedEnumerable<dynamic> source, 
        string property)
    {
        return Enumerable.ThenByDescending<dynamic, object>(
            source, 
            AccessorCache.GetAccessor(property), 
            Comparer<object>.Default);
    }

    static void Main()
    {
        dynamic a = new ExpandoObject(), 
                b = new ExpandoObject(), 
                c = new ExpandoObject();
        a.X = "abc";
        b.X = "ghi";
        c.X = "def";
        dynamic[] data = new[] { 
            new { Y = a },
            new { Y = b }, 
            new { Y = c } 
        };

        var ordered = data.OrderByDescending("Y.X").ToArray();
        foreach (var obj in ordered)
        {
            Console.WriteLine(obj.Y.X);
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IEnumerable / IQueryable 上的动态 LINQ OrderBy 的相关文章

随机推荐

  • 如何让服务器监听多个端口

    我想用同一台服务器监听 100 个不同的 TCP 端口 这是我目前正在做的事情 import socket import select def main server socket socket socket socket AF INET
  • __attribute__ ((已弃用)) 不适用于 Objective-C 协议方法?

    我需要弃用 Objective C 协议中的单个方法 在普通的类 实例方法上我添加 attribute deprecated 声明后 看来它不适用于协议方法 如果我将它们标记为已弃用并在某个地方使用它们 则项目编译正常 不会出现预期的弃用警
  • if ["$i" -gt "$count"];出现错误

    我试图将 f count f 1 f 2 名称放入数组中 下面是代码 echo Enter the count read count echo count arr i 1 while true do if i gt count then e
  • 在Matlab中使用中心切片定理实现滤波反投影算法

    我正在研究一种使用中心切片定理的滤波反投影算法作为家庭作业 虽然我理解纸上的理论 但在 Matlab 中实现它时遇到了问题 我得到了一个可以遵循的框架 但我认为我可能误解了一个步骤 这是我所拥有的 function img sampleFB
  • RSpec 请求 - 如何为所有请求设置 http 授权标头

    我正在使用 rspec 请求来测试 JSON API 该 API 需要在每个请求的标头中包含 api key 我知道我可以这样做 get v1 users janedoe json HTTP AUTHORIZATION gt Token t
  • 如何知道何时使用 XML 解析器以及何时使用 ActiveResource?

    我尝试使用 ActiveResource 来解析更像 HTML 文档的 Web 服务 但一直收到 404 错误 我是否需要使用 XML 解析器来完成此任务而不是 ActiveResource 我的猜测是 只有当您使用来自另一个 Rails
  • 什么是好的、免费的 PHP 图表套件?

    我要做的只是基本的折线图 任何人分享的经验将不胜感激 不是真正的 PHP 但我发现 amchart 非常容易实现 而且看起来很棒 http www amcharts com http www amcharts com 还可以查看 Googl
  • 证书吊销如何与中间 CA 配合使用?

    假设 PKI 层次结构如下所示 root CA gt inter 1 CA gt user 1 gt inter 2 CA gt user 2 我的问题是 根 CA 是否还需要定期从其子项 inter 1 和 inter 2 下载 CRL
  • NodeJS 最佳实践:流量控制错误?

    在 Node js 中 我应该使用错误来进行流程控制 还是应该更像异常一样使用它们 我正在 Sails js 中编写一个身份验证控制器和一些单元测试 目前 我的注册方法检查是否存在具有相同用户名的用户 如果用户已存在并具有该用户名 我的模型
  • Ionic 3 Uncaught(承诺):[object Object]

    我是 Ionic 3 和移动开发的新手 我正在尝试将 MySQL DB 连接到我的 Ionic 应用程序和 PHP Restful API 我用 Postman 测试了 API 它工作得很好 为了在 Ionic 中实现它 我做了以下操作 我
  • 具有 BLoC 模式的 BottomNavigationBar

    我真的很喜欢 BLoC 模式 并且正在尝试理解它 但我似乎无法弄清楚它应该如何应用BottomNavigationBar 制作导航页面列表并在导航栏点击事件上设置当前索引会导致整个应用程序重绘 因为setState 我可以使用Navigat
  • Java 8 列表到带有总和的 EnumMap

    我有以下课程 public class Mark private Long id private Student student private Integer value 0 private Subject subject public
  • 如何在 swagger 规范中表示十进制浮点数?

    我想在我的 api 文档中用 2 位小数表示小数点 用 1 位小数表示 我正在使用 swagger 2 0 规范中是否有内置定义的类型或任何其他 圆形 参数 或者我唯一的选择是使用 x 扩展 OpenAPI fka Swagger 规范使用
  • Magento - 分页生成错误的 URL

    除了网址之外 我的分页工作正常 第 2 页的链接是 example com products 21p 2 什么时候应该是 example com products p 2 当我在地址栏中输入后者时 它工作正常 这是生成链接的代码 li a
  • 使用 jQuery 中止 Ajax 请求

    是否有可能使用 jQuery 我取消 中止 Ajax 请求我还没有收到回复 大多数 jQuery Ajax 方法都会返回 XMLHttpRequest 或等效的 对象 因此您可以使用abort 请参阅文档 中止方法 http msdn mi
  • 适用于 Windows 的 C++11 编译器

    我刚刚在 Channel9 上看了一些视频 我发现 lambda 之类的东西真的很酷 当我尝试复制该示例时 它失败了 auto也没用 我正在使用诺基亚的 qtcreator 它随 gcc 4 4 0 一起提供 我想知道哪个编译器实现了有趣的
  • 尝试在类中定义静态常量变量

    我正在定义一个变量adc cmd 9 as a static const unsigned char在我的课堂上ADC私人之下 由于它是一个常量 我想我只需在它自己的类中定义它 但这显然不起作用 pragma once class ADC
  • 使用 for_each 调用成员函数

    这是我原来的代码 include stdafx h include
  • 在 AS3 中将 Little-endian ByteArray 转换为 Big-endian

    AS3中如何将Little endian ByteArray转换为Big endian 我将 bitmapData 转换为 Big endian ByteArray 然后使用 Adob e Alchemy 将其推入内存 然后当我从内存中读取
  • IEnumerable / IQueryable 上的动态 LINQ OrderBy

    我在中找到了一个例子VS2008示例 http msdn2 microsoft com en us bb330936 aspx用于动态 LINQ 允许您使用类似 SQL 的字符串 例如OrderBy Name Age DESC 用于订购 不