我在VS2008动态LINQ示例中找到了一个示例,允许您使用类似sql的字符串(例如OrderBy(“Name, Age DESC”))进行排序。不幸的是,所包含的方法只适用于IQueryable<T>。有什么办法得到这个功能IEnumerable<T>?
当前回答
将List转换为IEnumerable或Iquerable,使用System.LINQ.Dynamic命名空间添加,然后你可以用逗号分隔的字符串提到OrderBy方法,OrderBy方法默认来自System.LINQ.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;
}
}
我在寻找Linq多序子句时遇到了这个问题 也许这就是作者想要的
下面是如何做到这一点:
var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);
如果您正在使用规范(例如Ardalis规范)
using Microsoft.EntityFrameworkCore;
namespace TestExtensions;
public static class IQueryableExtensions
{
public static void ApplyOrder<T>(ISpecificationBuilder<T> query, string propertyName, bool ascendingOrder)
{
if (ascendingOrder)
query.OrderBy(T => EF.Property<object>(T!, propertyName));
else
query.OrderByDescending(T => EF.Property<object>(T!, propertyName));
}
}
只是无意中发现了这首古老的…
要在没有动态LINQ库的情况下做到这一点,只需要如下所示的代码。这涵盖了包括嵌套属性在内的大多数常见场景。
为了让它与IEnumerable<T>一起工作,你可以添加一些包装器方法,通过AsQueryable -但下面的代码是需要的核心表达式逻辑。
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-Objects (orm的表达式树等不能真正表示动态查询——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);
}
}
}
你可以添加它:
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的答案
问题是为什么?任何这样的排序都会在运行时抛出异常,而不是在编译时抛出异常(如D2VIANT的答案)。
如果你正在处理Linq to Sql, orderby是一个表达式树,它将被转换为Sql以执行。
推荐文章
- 如何获取HttpContext。目前在ASP。网络核心?
- 给定文件系统路径,是否有一种更短的方法来提取没有扩展名的文件名?
- 方法在字典中添加新项或更新现有项
- 使用Lambda/Linq对列表进行对象排序
- 为什么我不能在.NET中定义一个结构的默认构造函数?
- TypeLoadException表示“没有实现”,但它已经实现了
- 如何在ASP中返回当前操作。NET MVC视图?
- 存储Kinect 2.0版本的动作到BVH文件
- SortedList和SortedDictionary之间的区别是什么?
- jQuery用户界面对话框。NET按钮回发
- 合并。net中的两个数组
- 如何在Visual Studio中启用NuGet包恢复?
- 如何在实体框架6中调用存储过程(代码优先)?
- c#中的数组片
- 如何在LINQ中对单个连接中的多个字段进行连接