VS2008 예제 의 Dynamic 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;
}
편집 : LINQ-to-Object에만 적용 dynamic
되지만 dynamic
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);
}
}
}
답변
합병증없이 너무 쉽습니다 :
using System.Linq.Dynamic;
상단에 추가하십시오 .- 사용하다
vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
답변
답을 찾았습니다. .AsQueryable<>()
확장 방법을 사용하여 내 목록을 IQueryable로 변환 한 다음 이에 대해 동적 순서를 실행할 수 있습니다.
답변
이 질문을 우연히 발견했습니다.
위에서 Marc의 ApplyOrder 구현을 사용하여 다음과 같은 SQL과 같은 문자열을 처리하는 Extension 메서드를 함께 사용했습니다.
list.OrderBy("MyProperty DESC, MyOtherProperty ASC");
자세한 내용은 여기에서 찾을 수 있습니다 : http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html
답변
리플렉션을 사용하여 정렬하려는 속성을 얻는 것이 좋습니다.
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);
}
리플렉션 사용은 속성에 직접 액세스하는 것보다 상당히 느리므로 성능을 조사해야합니다.
답변
다른 사람들이 말한 것을 바탕으로합니다. 다음이 잘 작동한다는 것을 알았습니다.
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 clauses를 찾고이 질문을 우연히 발견했을 것입니다. 아마도 이것이 저자가 찾고 있었던 것일 수 있습니다.
이를 수행하는 방법은 다음과 같습니다.
var query = pets.OrderBy(pet => pet.Name).ThenByDescending(pet => pet.Age);