C# 用于返回分组结果的Linq扩展

C# 用于返回分组结果的Linq扩展,c#,.net,linq,C#,.net,Linq,我有以下代码,并试图避免代码重复 我想做的是有一个自定义的扩展方法,比如“SelectGrouped” 它将接受一个“Shift”对象,并返回匿名类型(不确定是否可能) 我想要达到的是让它看起来像这样 var groupedResultCurrentShift = currentShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks); var groupedResultPreviousShift

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

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

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

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

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

不幸的是,您想要的东西不能以当前的形式完成

首先,变量
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();
内部类卷数据
{
公共字符串媒体;
公共日期时间度量时间;
公共十进制卷索引;
}
公共静态列表GetVolumeData(此班次)
{
返回移位。归档测量
其中(f=>TimeSpan.Parse(f.MeasurementTime.MeasurementTime)==ShiftEnd)
.GroupBy(f=>new{f.Medium,f.MeasurementTime})
.Select(t=>newvolumedata{
中等=t.Key.Medium,
MeasurementTime=t.Key.MeasurementTime,
VolumeInTanks=t.Sum(s=>s.Filing)})
.ToList();
}
...
var groupedFillingsCurrentShift=currentShift.GetVolumeData();
if(上一个移位!=null)
var groupedFillingsPreviousShift=previousShift.GetVolumeData();
没有意义,因为第二个和第三个子句的
t
没有定义,并且
volumeInTanks
与您尚未创建的属性相关

我们还有一个问题,查询中的
Select
与前面在同一范围内定义的匿名对象相关。这会阻止我们定义一个方法,该方法允许我们传入定义应该选择什么的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
}
由于我们希望它是通用的,除非在特定情况下确实需要,否则不应调用
ToList()
,因此返回IQueryable更有意义:

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() });
}
public static IQueryable to groupedcounts(此IQueryable源、Func pred、Func keyGen、Func tallyGen)
{
电流移位。锉刀测量
.Where(pred)
.GroupBy(凯根、塔利根)
.Select(t=>newgroupedcount{Key=t.Key,Count=t.Sum()});
}

Thanx。这也是我的怀疑,我需要使用dynamic来实现这一点。(或硬键入一个类)。很好:)不太确定您对该键做了什么。这就是与计数一起出现的键类型,因此在调用的情况下,它是一个具有
Medium
MeasurementTime
属性的匿名对象。它是基于第二个参数创建的(第一个参数过滤掉那些你不想要的,第三个表示你要求和的属性)。
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
);
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() });
}