[解決済み] IEnumerable<T> / IQueryable<T>の動的なLINQ OrderBy
2022-03-14 22:41:47
質問
の中に例がありました。
VS2008 の例
を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;
}
編集部:これを混ぜるなら、もっと楽しくなります。
dynamic
- ただし
dynamic
はLINQ-to-Objectにしか適用されない(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);
}
}
}
関連
-
[解決済み] 保護レベルによりアクセス不能になりました。
-
[解決済み】リソースの読み込みに失敗した:ステータス500(内部サーバーエラー)のサーバーの応答)
-
[解決済み] LINQで複数の "order by "を使用する
-
[解決済み] LINQのGroup by
-
[解決済み] IEnumerable<T> と IQueryable<T> を比較する。
-
[解決済み] DataTableに対するLINQクエリ
-
[解決済み] IEnumerable<T>のforeachのLINQでの等価性
-
[解決済み] LINQ Orderby Descending Query(LINQ降順クエリ
-
[解決済み】LinqでIQueryableを使用する。
-
[解決済み】IEnumerable vs List - What to Use? どのように動作するのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】WebForms UnobtrusiveValidationModeは、jqueryのScriptResourceMappingを必要とする
-
[解決済み】パディングが無効で、削除できない?
-
[解決済み】Sequence contains no matching element(シーケンスにマッチする要素がない
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み】Unity 「関連するスクリプトを読み込むことができません」「Win32Exception: システムは指定されたファイルを見つけることができません"
-
[解決済み】"指定されたパスのフォーマットはサポートされていません。"
-
[解決済み】HRESULTからの例外:0x800A03ECエラー
-
[解決済み】C#のequal to演算子でtextとvarcharのデータ型は互換性がない
-
[解決済み】WSACancelBlockingCallの例外について
-
[解決済み】「namespace」なのに「type」のように使われる。