Reputation: 3555
I have the following code, and trying to avoid code duplication What I'd like to do is have a Custom Extension method say called "SelectGrouped"
that would accept a "Shift" object, and return the anonymous type (not sure if that possible)
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();
What I'd like to achieve is to have it look like so
var groupedResultCurrentShift =
currentShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);
var groupedResultPreviousShift =
previousShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);
Upvotes: 0
Views: 94
Reputation: 113242
var groupedResultCurrentShift =
currentShift.SelectGrouped(t=>t.Medium, t.MeasurementTime, t.VolumenInTanks);
Makes no sense, because the t
of the second and third clause isn't defined, and VolumenInTanks
relates to a property that you haven't created yet.
We also have a problem, in that the Select
in your query relates to an anonymous object defined earlier in the same scope. This prevents us from defining a method that lets us pass in a lambda defining what should be selected.
However:
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();
is equivalent to;
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();
Now, this still isn't quite there, but let's consider that we get the same information from:
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();
So. If you'd be willing to have to do item.Key.Medium
rather than item.Medium
, then we're in business:
We need a return type, so we'll create one:
public class GroupedCount<T>
{
public<T> Key{get;set;}
public int Count{get;set;}//bit more of a general-purpose name than VolumeInTanks
}
Now, create our method:
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();
}
We can now call:
currentShift.ToGroupedCounts(
f => TimeSpan.Parse(f.MeasurementTime.MeasurementTime) == ShiftEnd,
f => new { f.Medium, f.MeasurementTime },
s => s.Filing
);
Since we want this to be general purpose, and ToList()
shouldn't be called unless it's definitely needed in the specific case, it makes more sense to return an 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() });
}
Upvotes: 0
Reputation: 71563
Unfortunately, what you want can't be done in its current form.
First off, the variable t
only has value in the lambda. The commas separate parameters, and so t
loses its scope for the other parameters.
Second and more importantly, anonymous types are locally scoped by definition, because you have to use "var" to imply the type of a variable that holds them. You can't pass them in as parameters nor return them as return values (well, technically you can using dynamic
, but let's not go there, please).
If you were to define a named type that could be used to hold the final form of the projected data, this would work:
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();
Upvotes: 2