IEnumerable <T>上的dynamicLINQ OrderBy

我在VS2008的dynamicLINQ示例中find了一个示例,它允许您使用类似sql的string(例如OrderBy("Name, Age DESC"))进行sorting。 不幸的是,这个方法只能用于IQueryable<T> ; 在IEnumerable<T>上有没有办法获得这个function?

刚才偶然发现了这个老头儿

要做到这一点没有dynamic的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的expression式树等不能真正代表dynamic查询 – MemberExpression不支持它)。 但是,有一种方法可以用LINQ到对象来实现。 请注意, Hashtable的select是由于有利的locking语义:

 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(); aX = "abc"; bX = "ghi"; cX = "def"; dynamic[] data = new[] { new { Y = a },new { Y = b }, new { Y = c } }; var ordered = data.OrderByDescending("YX").ToArray(); foreach (var obj in ordered) { Console.WriteLine(obj.YX); } } } 

太容易没有任何并发​​症:

  1. using System.Linq.Dynamic;添加using System.Linq.Dynamic; 在顶部。
  2. 使用vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();

我find了答案。 我可以使用.AsQueryable<>()扩展方法将我的列表转换为IQueryable,然后通过.AsQueryable<>()运行dynamic顺序。

刚才偶然发现了这个问题。

从上面的Marc的ApplyOrder实现中,我使用了一个处理类似SQL的string的扩展方法:

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

详情可以在这里find: http : //aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html

我想这将使用reflection来获得你想要sorting的任何属性:

 IEnumerable<T> myEnumerables var query=from enumerable in myenumerables where some criteria orderby GetPropertyValue(enumerable,"SomeProperty") select enumerable private static object GetPropertyValue(object obj, string property) { System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property); return propertyInfo.GetValue(obj, null); } 

请注意,使用reflection比直接访问属性要慢很多,所以必须调查性能。

只是build立在别人的话上。 我发现以下工作相当好。

 public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> input, string queryString) { if (string.IsNullOrEmpty(queryString)) return input; int i = 0; foreach (string propname in queryString.Split(',')) { var subContent = propname.Split('|'); if (Convert.ToInt32(subContent[1].Trim()) == 0) { if (i == 0) input = input.OrderBy(x => GetPropertyValue(x, subContent[0].Trim())); else input = ((IOrderedEnumerable<T>)input).ThenBy(x => GetPropertyValue(x, subContent[0].Trim())); } else { if (i == 0) input = input.OrderByDescending(x => GetPropertyValue(x, subContent[0].Trim())); else input = ((IOrderedEnumerable<T>)input).ThenByDescending(x => GetPropertyValue(x, subContent[0].Trim())); } i++; } return input; } 

我绊倒了这个问题寻找Linq multiple orderby子句,也许这就是作者正在寻找的东西

以下是如何做到这一点:

 var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age); 

我试图做到这一点,但与Kjetil Watnedal的解决scheme有问题,因为我不使用内联linq语法 – 我更喜欢方法风格的语法。 我的具体问题是尝试使用自定义IComparer进行dynamicsorting。

我的解决scheme是这样结束的:

给定一个像这样的IQueryable查询:

 List<DATA__Security__Team> teams = TeamManager.GetTeams(); var query = teams.Where(team => team.ID < 10).AsQueryable(); 

给出一个运行时sorting字段参数:

 string SortField; // Set at run-time to "Name" 

dynamicOrderBy看起来像这样:

 query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField)); 

这就是使用一个名为GetReflectedPropertyValue()的小帮手方法:

 public static string GetReflectedPropertyValue(this object subject, string field) { object reflectedValue = subject.GetType().GetProperty(field).GetValue(subject, null); return reflectedValue != null ? reflectedValue.ToString() : ""; } 

最后一件事 – 我提到我希望OrderBy使用自定义IComparer – 因为我想做自然sorting 。

要做到这一点,我只是改变OrderBy

 query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField), new NaturalSortComparer<string>()); 

看到这个职位的NaturalSortComparer()的代码。

你可以添加它:

 public static IEnumerable<T> OrderBy( this IEnumerable<T> input, string queryString) { //parse the string into property names //Use reflection to get and sort by properties //something like foreach( string propname in queryString.Split(',')) input.OrderBy( x => GetPropertyValue( x, propname ) ); // I used Kjetil Watnedal's reflection example } 

GetPropertyValue函数来自Kjetil Watnedal的答案

这个问题是为什么呢? 任何这样的sorting都会在运行时抛出exception,而不是编译时间(就像D2VIANT的回答)。

如果你正在处理Linq到Sql,而orderby是一个expression式树,它将被转换成SQL来执行。

这是我觉得有趣的东西。 如果您的源代码是DataTable,则可以使用dynamicsorting而不使用Dynamic Linq

 DataTable orders = dataSet.Tables["SalesOrderHeader"]; EnumerableRowCollection<DataRow> query = from order in orders.AsEnumerable() orderby order.Field<DateTime>("OrderDate") select order; DataView view = query.AsDataView(); bindingSource1.DataSource = view; 

参考: http : //msdn.microsoft.com/en-us/library/bb669083.aspx (使用DataSetExtensions)

这是通过将其转换为DataView来实现的另一种方法:

 DataTable contacts = dataSet.Tables["Contact"]; DataView view = contacts.AsDataView(); view.Sort = "LastName desc, FirstName asc"; bindingSource1.DataSource = view; dataGridView1.AutoResizeColumns(); 

感谢Maarten( 在LINQ中使用PropertyInfo对象来查询集合 ),我得到了这个解决scheme:

 myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList(); 

在我的情况下,我正在“ColumnHeaderMouseClick”(WindowsForm),所以刚刚find特定的列按下和其相应的PropertyInfo:

 foreach (PropertyInfo column in (new Process()).GetType().GetProperties()) { if (column.Name == dgvProcessList.Columns[e.ColumnIndex].Name) {} } 

要么

 PropertyInfo column = (new Process()).GetType().GetProperties().Where(x => x.Name == dgvProcessList.Columns[e.ColumnIndex].Name).First(); 

(请确保您的列名称与对象属性匹配)

干杯

经过大量的search,这为我工作:

 public static IEnumerable<TEntity> OrderBy<TEntity>(this IEnumerable<TEntity> source, string orderByProperty, bool desc) { string command = desc ? "OrderByDescending" : "OrderBy"; var type = typeof(TEntity); var property = type.GetProperty(orderByProperty); var parameter = Expression.Parameter(type, "p"); var propertyAccess = Expression.MakeMemberAccess(parameter, property); var orderByExpression = Expression.Lambda(propertyAccess, parameter); var resultExpression = Expression.Call(typeof(Queryable), command, new[] { type, property.PropertyType }, source.AsQueryable().Expression, Expression.Quote(orderByExpression)); return source.AsQueryable().Provider.CreateQuery<TEntity>(resultExpression); } 

您可以将IEnumerable转换为IQueryable。

 items = items.AsQueryable().OrderBy("Name ASC"); 

另一种解决scheme使用以下类/接口。 这不是真正的dynamic,但它的作品。

 public interface IID { int ID { get; set; } } public static class Utils { public static int GetID<T>(ObjectQuery<T> items) where T:EntityObject, IID { if (items.Count() == 0) return 1; return items.OrderByDescending(u => u.ID).FirstOrDefault().ID + 1; } } 

将列表转换为IEnumerable或Iquerable,使用System.LINQ.Dynamic命名空间添加,然后你可以在逗号分隔的string中提到属性名称为OrderBy方法,默认情况下来自System.LINQ.Dynamic。

 var result1 = lst.OrderBy(a=>a.Name);// for ascending order. var result1 = lst.OrderByDescending(a=>a.Name);// for desc order.