답변:
이 노파에 빠졌어
동적 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);
}
}
}
IQueryable<T>
당신 같은이 그렇다면, List<T>
(인 IEnumerable<T>
) 당신이 사용해야 할 수도 있습니다 AsQueryable()
- 예를 들어var sorted = someList.AsQueryable().OrderBy("Foo.Bar");
합병증없이 너무 쉽습니다 :
using System.Linq.Dynamic;
상단에 추가하십시오 .vehicles = vehicles.AsQueryable().OrderBy("Make ASC, Year DESC").ToList();
System.Linq.Dynamic
습니까?
이 질문을 우연히 발견했습니다.
위에서 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);
인라인 linq 구문을 사용하지 않기 때문에이 작업을 시도했지만 Kjetil Watnedal의 솔루션에 문제가 있습니다. 메소드 스타일 구문을 선호합니다. 내 특정 문제는 custom을 사용하여 동적 정렬을 시도하는 것이 었습니다 IComparer
.
내 솔루션은 다음과 같이 끝났습니다.
다음과 같은 IQueryable 쿼리가 제공됩니다.
List<DATA__Security__Team> teams = TeamManager.GetTeams();
var query = teams.Where(team => team.ID < 10).AsQueryable();
그리고 런타임 정렬 필드 인수가 주어지면 :
string SortField; // Set at run-time to "Name"
동적 OrderBy는 다음과 같습니다.
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
이를 위해, 나는 OrderBy
to를 다음과 같이 변경한다 :
query = query.OrderBy(item => item.GetReflectedPropertyValue(SortField), new NaturalSortComparer<string>());
에 대한 코드는 이 게시물 을 참조하십시오 NaturalSortComparer()
.
동적 사용 linq
그냥 추가 using System.Linq.Dynamic;
다음과 같이 사용하여 모든 열을 주문하십시오.
string sortTypeStr = "ASC"; // or DESC
string SortColumnName = "Age"; // Your column name
query = query.OrderBy($"{SortColumnName} {sortTypeStr}");
당신은 그것을 추가 할 수 있습니다 :
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의 답변과 같이 컴파일 타임이 아닌 런타임에 예외를 throw합니다.
Linq to Sql을 처리하고 orderby가 표현식 트리 인 경우 어쨌든 실행을 위해 SQL로 변환됩니다.
OrderBy
이전 주문을 유지하지 마십시오 !!
여기 내가 찾은 흥미로운 것이 있습니다. 소스가 DataTable 인 경우 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 객체를 사용하여 컬렉션 쿼리 )이 솔루션을 얻었습니다.
myList.OrderByDescending(x => myPropertyInfo.GetValue(x, null)).ToList();
필자의 경우 "ColumnHeaderMouseClick"(WindowsForm)을 작업 중이므로 특정 열과 해당 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();
(열 이름이 객체 속성과 일치해야 함)
건배
많은 검색 후 이것이 나를 위해 일했습니다.
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);
}
대체 솔루션은 다음 클래스 / 인터페이스를 사용합니다. 그것은 역동적이지는 않지만 작동합니다.
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;
}
}
이 답변은 @John Sheehan-Runscope가 제공 한 솔루션에 대한 예제가 필요한 의견에 대한 답변입니다.
나머지 사람들에게 모범을 보여주십시오.
DAL (Data Access Layer)에서
IEnumerable 버전 :
public IEnumerable<Order> GetOrders()
{
// i use Dapper to return IEnumerable<T> using Query<T>
//.. do stuff
return orders // IEnumerable<Order>
}
IQueryable 버전
public IQueryable<Order> GetOrdersAsQuerable()
{
IEnumerable<Order> qry= GetOrders();
//use the built-in extension method AsQueryable in System.Linq namespace
return qry.AsQueryable();
}
이제 IQueryable 버전을 사용하여 바인딩 할 수 있습니다 (예 : Asp.net의 GridView 및 정렬 이점) (IEnumerable 버전을 사용하여 정렬 할 수 없음)
Dapper를 ORM으로 사용하고 IQueryable 버전을 빌드하고 asp.net의 GridView에서 정렬을 쉽게 사용했습니다.
먼저 Dynamic Tools-> NuGet Package Manager-> Package Manager Console 설치
install-package System.Linq.Dynamic
네임 스페이스 추가 using System.Linq.Dynamic;
이제 사용할 수 있습니다 OrderBy("Name, Age DESC")
이것을 사용할 수 있습니다 :
public List<Book> Books(string orderField, bool desc, int skip, int take)
{
var propertyInfo = typeof(Book).GetProperty(orderField);
return _context.Books
.Where(...)
.OrderBy(p => !desc ? propertyInfo.GetValue(p, null) : 0)
.ThenByDescending(p => desc ? propertyInfo.GetValue(p, null) : 0)
.Skip(skip)
.Take(take)
.ToList();
}