提问者:小点点

IEnumerable<T>/IQueryable<T>


我在VS2008动态LINQ示例中找到了一个示例,它允许您使用类似SQL的字符串(例如,OrderBy(“Name,Age DESC”))进行排序。不幸的是,包含的方法仅适用于IQueryable


共3个答案

匿名用户

只是偶然发现了这首老歌...

要在没有动态LINQ库的情况下实现这一点,您只需要以下代码。这涵盖了最常见的场景,包括嵌套属性。

要让它与IENumable一起工作

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

编辑:如果您想将其与动态混合使用,它会变得更有趣-不过请注意动态仅适用于LINQ to对象(ORM等的表达式树不能真正表示动态查询-成员表达式不支持它)。但这里有一种方法可以使用LINQ to对象来实现这一点。请注意,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);
        }
    }
}

匿名用户

太简单了,没有任何并发症:

  1. 使用系统添加。林克。动态的在顶部
  2. 使用车辆=车辆。AsQueryable()。订购人(“制造ASC,年份描述”)。托利斯特()

编辑:为了节省一些时间,系统将。林克。动态Core(System.Linq.Dynamic不推荐使用)程序集不是框架的一部分,但可以从nuget:System安装。林克。动态果心

匿名用户

只是偶然发现了这个问题。

使用上面Marc的ApplyOrder实现,我创建了一个扩展方法,可以处理类似SQL的字符串,如:

list.OrderBy("MyProperty DESC, MyOtherProperty ASC");

详情可在此找到:http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html