2011-08-18 16 views
5

Uygulamam sıklıkla bir tabloyu gruplandırmalı, ardından satırı o grup için maksimum değere döndürmelidir. Bu LINQ yapmak oldukça kolaydır:LINQ - Satırı her satır için maksimum değerle elde etmek için bir uzantı yöntemi yazmak

myTable.GroupBy(r => r.FieldToGroupBy) 
.Select(r => r.Max(s => s.FieldToMaximize)) 
.Join(
    myTable, 
    r => r, 
    r => r.FieldToMaximize, 
    (o, i) => i) 

Şimdi kendi yöntemiyle içine bu soyut istediğinizi varsayalım. , Bu yüzden yeniden çalıştı maxKeySelector bir ifade (böylece r üzerinde diyemezsin, ve hatta Max geçemeyecek. Ne yazık ki bu derleme değil

public static IQueryable<TSource> 
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, TGroupKey>> groupKeySelector, 
    Expression<Func<TSource, TMaxKey>> maxKeySelector) 
    where TMaxKey : IComparable 
{ 
    return source 
     .GroupBy(groupKeySelector) 
     .Join(
      source, 
      g => g.Max(maxKeySelector), 
      r => maxKeySelector(r), 
       (o, i) => i); 
} 

: Bunu yazarken çalıştı maxKeySelector bir işlev yerine bir ifade verme.

public static IQueryable<TSource> 
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, TGroupKey>> groupKeySelector, 
    Func<TSource, TMaxKey> maxKeySelector) 
    where TMaxKey : IComparable 
{ 
    return source 
     .GroupBy(groupKeySelector) 
     .Join(
      source, 
      g => g.Max(maxKeySelector), 
      r => maxKeySelector(r), 
       (o, i) => i); 
} 

Şimdi bu derler Ama zamanında başarısız: Ben gerekir: "sorgu operatörü 'Max' için kullanılan desteklenmeyen aşırı" Bu şaşırıp budur MaxKeySelector'ı Max() içine geçirmek için doğru yolu bulun.

Herhangi bir öneriniz var mı? LINQ to SQL'i bir fark yaratıyor gibi görünüyor. Her şeyden

cevap

4

Birincisi, LINQ düşündüğünden daha ne yapmaya çalışıyoruz hatta kolay olduğunu işaret etmek istiyorum:

myTable.GroupBy(r => r.FieldToGroupBy) 
    .Select(g => g.OrderByDescending(r => r.FieldToMaximize).FirstOrDefault()) 

... hayatlarımızı yapmalıdır hangi bir ikinci bölümü için biraz daha kolay:

public static IQueryable<TSource> 
SelectMax<TSource, TGroupKey, TMaxKey>(
    this IQueryable<TSource> source, 
    Expression<Func<TSource, TGroupKey>> groupKeySelector, 
    Expression<Func<TSource, TMaxKey>> maxKeySelector) 
    where TMaxKey : IComparable 
{ 
    return source 
     .GroupBy(groupKeySelector) 
     .Select(g => g.AsQueryable().OrderBy(maxKeySelector).FirstOrDefault()); 
} 

anahtarı grubun bir IQueryable yaparak, yerine Func s almaktan daha gerçek ifadeleri alabilir LINQ yöntemlerin yeni bir dizi açmak olmasıdır. Bu, çoğu standart LINQ sağlayıcıları ile uyumlu olmalıdır.

+0

Bu kesinlikle daha özlü, ancak maalesef işe yaramıyor. Daha önce olduğu gibi hemen hemen aynı hatayı alıyorum: "Sorgulama operatörü 'OrderBy' için desteklenmeyen aşırı yük". – ctkrohn

+0

@ctkrohn: Hangi LINQ sağlayıcısı kullanıyorsunuz? LINQ to Entities ... ve LINQ to Objects için benim için gayet iyi çalışıyor. – StriplingWarrior

+0

Bu bir LINQ to SQL. – ctkrohn

4

Çok ilginç. Bazen "dinamik", daha fazla geliştirme ve çalışma zamanı yürütme işleminde, değerinden (IMHO) daha fazla size mal olabilir.

public static IQueryable<Item> _GroupMaxs(this IQueryable<Item> list) 
{ 
    return list.GroupBy(x => x.Family) 
     .Select(g => g.OrderByDescending(x => x.Value).First()); 
} 

Ve burada en dinamik yaklaşım: Yine de, burada en kolay Gördüğünüz gibi

public static IQueryable<T> _GroupMaxs<T, TGroupCol, TValueCol> 
    (this IQueryable<T> list, string groupColName, string valueColName) 
{ 
    // (x => x.groupColName) 
    var _GroupByPropInfo = typeof(T).GetProperty(groupColName); 
    var _GroupByParameter = Expression.Parameter(typeof(T), "x"); 
    var _GroupByProperty = Expression 
      .Property(_GroupByParameter, _GroupByPropInfo); 
    var _GroupByLambda = Expression.Lambda<Func<T, TGroupCol>> 
     (_GroupByProperty, new ParameterExpression[] { _GroupByParameter }); 

    // (x => x.valueColName) 
    var _SelectParameter = Expression.Parameter(typeof(T), "x"); 
    var _SelectProperty = Expression 
      .Property(_SelectParameter, valueColName); 
    var _SelectLambda = Expression.Lambda<Func<T, TValueCol>> 
     (_SelectProperty, new ParameterExpression[] { _SelectParameter }); 

    // return list.GroupBy(x => x.groupColName) 
    // .Select(g => g.OrderByDescending(x => x.valueColName).First()); 
    return list.GroupBy(_GroupByLambda) 
     .Select(g => g.OrderByDescending(_SelectLambda.Compile()).First()); 
} 

, ben bir alt çizgi ile benim Uzatma Yöntemleri önce. Elbette bunu yapmak zorunda değilsiniz. Sadece genel fikri al ve onunla koş.

Böyle çağırır:

public class Item 
{ 
    public string Family { get; set; } 
    public int Value { get; set; } 
} 

foreach (Item item in _List 
     .AsQueryable()._GroupMaxs<Item, String, int>("Family", "Value")) 
    Console.WriteLine("{0}:{1}", item.Family, item.Value); 

İyi şanslar!

+0

Bu akıllıca ve yardımcı görünüyor, ama muhtemelen amaçlarımın gereğinden fazla olduğunu. Başka bir yöntemi hesaba katmam gerekecek olursa bu tekniği işaretleyeceğim, ancak şimdilik kodu sadece satırda yazmak daha kolay olacaktır. – ctkrohn

+0

@ctkrohn, welp, sorunuzda böyle bir yöntem sorduğunuz için utanıyorum. Eğer seçiyor olsaydım, onu da yazabilirdim. Ama asıl sorununun bu daha zarif bir cevabı görmüyorum. C'est la vie, sanırım. –

+0

+1, sağlamak için çok tembel olduğum kodları yazmak için zaman ayırdığınız için. – StriplingWarrior

İlgili konular