【问题标题】:LINQ reusing Select statement with anonymous grouping typesLINQ 重用具有匿名分组类型的 Select 语句
【发布时间】:2021-06-22 14:50:41
【问题描述】:

我正在尝试找到一种方法来删除以 LINQ 选择语句为中心的应用程序中的重复代码。假设我们有现有的表格行,需要针对不同的报告要求进行汇总和分组,所有原始数据仅按天分组,需要按周/月和其他属性分组。

DataRow 是一个示例对象,需要将其分组并转换为对象 ReportTableRow(请注意,这是一个大大简化的对象,但实际对象具有更多属性,因此变得更加突出)。

    public class DataRow
    {
        public DateTime Date { get; set; }
        public string AccountNumber { get; set; }
        public string MachineNumber { get; set; }
        public int TEST { get; set; }
    }


    public class ReportTableRow
    {
        public int WeekNumber { get; set; }
        public int Month { get; set; }
       
        public string AccountNumber{ get; set; }
        public string MachineNumber { get; set; }
        
        public int TEST { get; set; }
        public string TEST_TRAFFICLIGHT { get; set; }       
    }

然后我们创建一个 DataRows 列表:

List<DataRow> reportTable = new List<DataRow>()
{
    new DataRow()
    {
        Date = new DateTime(2021, 06, 14),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 15),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 1
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 15),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 6
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 16),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 4
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 17),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 18),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 7
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 19),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 20),
        AccountNumber = "11111111",
        MachineNumber = "00AB2021",
        TEST = 11
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 14),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 15),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 1
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 15),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 6
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 16),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 4
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 17),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 18),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 7
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 19),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 2
    },
    new DataRow()
    {
        Date = new DateTime(2021, 06, 20),
        AccountNumber = "22222222",
        MachineNumber = "11BC2021",
        TEST = 11
    }
};

因此,如果我们需要“按周”或“按月”分组的数据,那么我们需要在报告中分别返回 WeekNumber 或 Month 编号,分组看起来像这样,其中 GetTrafficLight 方法返回一个字符串值基于TEST总和的值:

switch (aggregate.ToUpper())
{
    case "BY WEEK":
        reportTable = reportTable
                                .GroupBy(x => new { x.AccountNumber, WeekNumber = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(x.Date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday) })
                                .Select(x => new ReportTableRow
                                {
                                    WeekNumber = x.Key.WeekNumber,
                                    Month = x.Max(y => y.Date).Month,
                                    MachineNumber = x.FirstOrDefault().MachineNumber,
                                    TEST = x.Sum(y => y.TEST),
                                    TEST_TRAFFICLIGHT = GetTrafficLight(x.Sum(y => y.TEST)
                                })
                                .ToList();
        break;
    case "BY MONTH":
        reportTable = reportTable
                                .GroupBy(x => new { x.AccountNumber, x.Date.Month })
                                .Select(x => new ReportTableRow
                                {
                                    WeekNumber = CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(x.Max(y => y.Date), CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday),
                                    Month = x.Key.Month,
                                    
                                    MachineNumber = x.FirstOrDefault().MachineNumber,
                                    TEST = x.Sum(y => y.TEST),
                                    TEST_TRAFFICLIGHT = GetTrafficLight(x.Sum(y => y.TEST)
                                })
                                .ToList();
        break;
}

问题是,无论如何要删除“选择”代码并将其传递给可以接受匿名分组的方法或对象,以便可以多次重复使用。将匿名分组更改为包含两个属性的编译时对象意味着然后在数据集中返回重复项,是否找到一种方法来删除编译时分组成员中的重复项可能有助于解决?

请注意,已创建代码来提出这个问题。

【问题讨论】:

  • 我之前做过类似的事情,和这个 SO 帖子的答案非常相似:stackoverflow.com/a/16517768/1188197
  • 我去看看,谢谢!
  • 看起来是这样的,但似乎很简单,将一个字符串值选择为另一个字符串值,像“Sum”这样的计算以及传入一个外部方法(如“GetTrafficLight”)呢?

标签: c# linq


【解决方案1】:

这是可选的,但我要做的第一件事是将“聚合”放入布尔变量中:

bool byWeek = aggregate.ToUpper() == "BY WEEK";

接下来我要做的是捕获并驯服“GetWeekOfYear”逻辑:

int getFirstMonday (DateTime date) => 
    CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(
        date, 
        CalendarWeekRule.FirstFourDayWeek, 
        DayOfWeek.Monday
    ); 

做这些事情使得将条件逻辑放入 GroupBySelect 方法中变得更加可行,这样您就不必将整个事情做两次:

List<ReportTableRow> results = 
    reportTable
    .GroupBy(x => new { 
        x.AccountNumber, 
        TimeBin = byWeek ? getFirstMonday(x.Date): x.Date.Month
    })
    .Select(x => new ReportTableRow {
        WeekNumber = byWeek ? x.Key.TimeBin : getFirstMonday(x.Max(y => y.Date)),
        Month = byWeek ? x.Max(y => y.Date).Month : x.Key.TimeBin,
        MachineNumber = x.FirstOrDefault().MachineNumber,
        TEST = x.Sum(y => y.TEST),
        TEST_TRAFFICLIGHT = GetTrafficLight(x.Sum(y => y.TEST))
    })
    .ToList();

我应该注意,您的示例数据使得无论“聚合”设置如何,结果都是相同的。但是如果你稍微改变一下,比如改变一些条目的日期,你会在聚合中得到不同的结果。至少对于我所做的更改,我的代码在切换“聚合”时重复了代码的行为。

【讨论】:

  • 我将尝试从中获得我想要的东西并回复您,感谢您的回复!
猜你喜欢
  • 1970-01-01
  • 2012-07-19
  • 2017-03-26
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 1970-01-01
  • 1970-01-01
  • 2021-08-21
相关资源
最近更新 更多