Skip to content

Instantly share code, notes, and snippets.

@aiwas
Last active August 3, 2023 12:32
Show Gist options
  • Save aiwas/0820994fa9420b085a854c666dc32e55 to your computer and use it in GitHub Desktop.
Save aiwas/0820994fa9420b085a854c666dc32e55 to your computer and use it in GitHub Desktop.
OrderByWithString
using System;
using System.Linq;
using System.Linq.Expressions;
namespace Net.Elendia.Sample.Linq;
public static class LinqExtensions {
/// <summary>
/// ORDER BY 句文字列を使ってソートします。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <param name="orderByQuery">ORDER BY 句文字列</param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
/// <remarks>
/// <code>Hoge,Fuga DESC,Piyo,Puyo DESC</code>
/// のような文字列を受け取り、
/// <code>OrderBy(x => x.Hoge).ThenByDescending(x => x.Fuga).ThenBy(x => x.Piyo).ThenByDescending(x => x.Puyo)</code>
/// のようなメソッド呼び出しに変換します。
/// </remarks>
public static IQueryable<T> OrderByWithString<T>(this IQueryable<T> source, string orderByQuery) {
IQueryable<T> result = source;
// ORDER BY 句文字列を列ごとに分解
var orderByList = orderByQuery.Split(',')
.Select(a => {
string[] pair = a.Trim().Split(' ');
string name = pair[0];
bool isAsc = (pair.Length == 1) || (pair[1] != "DESC");
return new { Name = name, IsAsc = isAsc };
})
.ToList();
if (orderByList.Count == 0) {
throw new ArgumentException("無効な ORDER BY 句が指定されました。");
}
var type = typeof(T);
var parameter = Expression.Parameter(type, "x");
for (int i = 0; i < orderByList.Count; i++) {
// メソッド名を決定
string methodName = ((i == 0), orderByList[i].IsAsc) switch {
(true, true) => "OrderBy",
(true, false) => "OrderByDescending",
(false, true) => "ThenBy",
(false, false) => "ThenByDescending",
};
// ラムダ式を構築
var propertyInfo = type.GetProperty(orderByList[i].Name)
?? throw new ArgumentException("無効な ORDER BY 句が指定されました。");
var propertyAccess = Expression.MakeMemberAccess(parameter, propertyInfo);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(
typeof(Queryable),
methodName,
new[] { type, propertyInfo.PropertyType },
result.Expression,
Expression.Quote(orderByExpression)
);
// 連結
result = result.Provider.CreateQuery<T>(resultExpression);
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment