OrderByのセレクタを外出ししたい
外部からのパラメータでリストの表示順位を変えたいなんてのは比較的ありがちなんだけど、EF+LINQでやろうとしてもいまいち方法がわからん。
ケース
素直にやるとこうなる。
public class ViewModel{ public string SearchText {get;set;} public int? age {get;set;} public int Order{get;set;} public int OrderDirection{get;set;} } public class Person{ public int id {get;set;} public string Name {get;set;} public string FamilyName{get;set;} public int age{get;set;} } public IEnumerable<Person> search(ViewModel model){ using (Entity entity = new Entity()){ IQueryable<Person> query = entity.Persons ; if (!string.IsNullOrEmpty(model.SearchText)) query = query.Where(p=>p.Name==SearchText || p.FamilyName==SearchText); if (model.age.HasValue) query = query.Where(p=>p.age==model.age.Value); if (model.OrderDirection==0){ query = model.Order==0? query.OrderBy(p=>p.Name) : query.OrderBy(p=>p.id); }else{ query = model.Order==0? query.OrderByDescending(p=>p.Name) : query.OrderByDescending(p=>p.id); } return query.Select(p=>p); } }
「model.Orderによって表示順を、model.OrderDirectionによって昇順降順を切り替えたい」ってのの実現に
if (model.OrderDirection==0){ query = model.Order==0? query.OrderBy(p=>p.Name) : query.OrderBy(p=>p.id); }else{ query = model.Order==0? query.OrderByDescending(p=>p.Name) : query.OrderByDescending(p=>p.id); }
ってなるのがダサすぎる。
Orderbyのラムダを外出しにしてOrderDirectionによる分岐1個か、さらにそれも無くした形にしたい。
OrderByってどんな形になってたか
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )Enumerable.OrderBy(TSource, TKey) メソッド (IEnumerable(TSource), Func(TSource, TKey)) (System.Linq)
thisがIEnumerable
IOrderedEnumerable
TKeyはKeySelectorにかかってるんだったか。
ということは
Func<Person,int> f=(p)=>{return p.id;} query = query.OrderBy(f);
的な感じか。確かにコンパイルは通る。
型違いのデリゲートってどうするんだ?
上の例だとOrderByで切り替えたい並び替えは string(person.Name)とint(person.id)なので、
Func<Person,int> f=(p)=>{return p.id;} Func<Person,string> f2=(p)=>{return p.Name;} query = query.OrderBy(model.Order==0?f:f2);
なんて甘い話は当然エラーになると。
'System.Func<Person,int>' と 'System.Func<Person,string>' の間に暗黙的な変換がないため、条件式の型がわかりません。
へちょん。
天才あらわる
what you are looking for is:
Expression<Func<Products, dynamic>>;try create a structure/class to hold both the Expression and if it is ascending or descending.
c# - Create an OrderBy Expression for LINQ/Lamda - Stack Overflow
dynamic!その手があったか!
Expression<Func<Person,dynamic>> f; if (model.Order==0){ f = p=> new{p.id}; }else{ f = p=> new{p.Name}; } query = query.OrderBy(f);
コンパイルも実行も普通にできてしまった。
すげぇ。
参考
- asp.net mvc - LINQ - Dynamic expression to OrderBy - Stack Overflow
- [http://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet:title=c# - Dynamic LINQ OrderBy on IEnumerable
- Stack Overflow] - dynamic (C# によるプログラミング入門)