Linq扩展返回分组结果

时间:2012-08-17 20:20:37

标签: c# .net linq

我有以下代码,并尝试避免代码重复 我想做的是有一个自定义扩展方法称为“SelectGrouped”

接受“Shift”对象,并返回匿名类型(不确定是否可能)

var groupedFillingsCurrentShift = currentShift.FilingMeasurements
                    .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd)
                    .GroupBy(f => new { f.Medium, f.MeasurementTime })
                    .Select(t => new { t.Key.Medium, t.Key.MeasurementTime, VolumeInTanks = t.Sum(s => s.Filing) })
                    .ToList();


if (previousShift != null)
{
    var groupedFillingsPreviousShift = previousShift.FilingMeasurements
        .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == previousShift.ShiftEnd)
        .GroupBy(f => new { f.Medium, f.MeasurementTime })
        .Select(t => new { t.Key.Medium, t.Key.MeasurementTime, VolumeInTanks = t.Sum(s => s.Filing) })
        .ToList();

我想要达到的目的是让它看起来像这样

var groupedResultCurrentShift = 
   currentShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);

var groupedResultPreviousShift = 
   previousShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);

2 个答案:

答案 0 :(得分:2)

不幸的是,你想要的东西不能以现在的形式完成。

首先,变量t仅在lambda中具有值。逗号分隔参数,因此t会丢失其他参数的范围。

其次,更重要的是,匿名类型按定义在本地范围内,因为您必须使用“var”来暗示保存它们的变量的类型。您不能将它们作为参数传递,也不能将它们作为返回值返回(从技术上讲,您可以使用dynamic,但是不要去那里,)。

如果您要定义一个可用于保存投影数据的最终形式的命名类型,这将起作用:

internal class VolumeData
{
   public string Medium;
   public DateTime MeasurementTime;
   public decimal VolumeInTanks;
}

public static List<VolumeData> GetVolumeData(this Shift shift)
{
   return shift.FilingMeasurements
               .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd)
               .GroupBy(f => new { f.Medium, f.MeasurementTime })
               .Select(t => new VolumeData{ 
                                Medium = t.Key.Medium, 
                                MeasurementTime = t.Key.MeasurementTime, 
                                VolumeInTanks = t.Sum(s => s.Filing) })
               .ToList();
}

...

var groupedFillingsCurrentShift = currentShift.GetVolumeData();


if (previousShift != null)
    var groupedFillingsPreviousShift = previousShift.GetVolumeData();

答案 1 :(得分:0)

var groupedResultCurrentShift = 
 currentShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);

没有意义,因为第二个和第三个子句的t未定义,VolumenInTanks与尚未创建的属性相关。

我们也遇到了一个问题,因为查询中的Select与之前在同一范围内定义的匿名对象有关。这阻止我们定义一个允许我们传入lambda的方法,该lambda定义了应该选择的内容。

然而:

currentShift.FilingMeasurements
                .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd)
                .GroupBy(f => new { f.Medium, f.MeasurementTime })
                .Select(t => new { t.Key.Medium, t.Key.MeasurementTime, VolumeInTanks = t.Sum(s => s.Filing) })
                .ToList();

相当于;

currentShift.FilingMeasurements
                .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd)
                .GroupBy(f => new { f.Medium, f.MeasurementTime }, s => s.Filing)
                .Select(t => new { t.Key.Medium, t.Key.MeasurementTime, VolumeInTanks = t.Sum() })
                .ToList();

现在,这仍然不存在,但让我们考虑从以下方面获得相同的信息:

currentShift.FilingMeasurements
                .Where(f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd)
                .GroupBy(f => new { f.Medium, f.MeasurementTime }, s => s.Filing)
                .Select(t => new { t.Key, VolumeInTanks = t.Sum() })
                .ToList();

因此。如果您愿意item.Key.Medium而不是item.Medium,那么我们就会开展业务:

我们需要一个返回类型,因此我们将创建一个:

public class GroupedCount<T>
{
  public<T> Key{get;set;}
  public int Count{get;set;}//bit more of a general-purpose name than VolumeInTanks
}

现在,创建我们的方法:

public static List<GroupedCount<TKey>> ToGroupedCounts<TSource, TKey>(this IQueryable<TSource> source, Func<TSource, bool> pred, Func<TSource, TKey> keyGen, Func<TSource, int> tallyGen)
{
    currentShift.FilingMeasurements
                .Where(pred)
                .GroupBy(keyGen, tallyGen)
                .Select(t => new GroupedCount<TKey>{ Key = t.Key, Count = t.Sum() })
                .ToList();
}

我们现在可以致电:

currentShift.ToGroupedCounts(
  f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd,
  f => new { f.Medium, f.MeasurementTime },
  s => s.Filing
);

由于我们希望这是通用的,并且除非在特定情况下确实需要ToList(),否则不应该调用public static IQueryable<GroupedCount<TKey>> ToGroupedCounts<TSource, TKey>(this IQueryable<TSource> source, Func<TSource, bool> pred, Func<TSource, TKey> keyGen, Func<TSource, int> tallyGen) { currentShift.FilingMeasurements .Where(pred) .GroupBy(keyGen, tallyGen) .Select(t => new GroupedCount<TKey>{ Key = t.Key, Count = t.Sum() }); } ,返回IQueryable更有意义:

{{1}}