一直想抽个时间记录下平时项目中遇到的问题及自己的解决方案,慢慢地发现项目中遇到的问题单单通过百度或者CSDN已经解决不了。因为百度搜到的资料很少,当然也是因为项目中的前端框架本身也不是很常见,再者CSDN中的博客大多数都是复制粘贴过来,一部分问题还需要通过官方文档解决,但苦于是英文的,阅读起来不是很友好。于是就决定自己写好每一篇博客,不管能不能解决读者的问题,更好的也是一种记录。当然能帮助到同僚的解决也是非常荣幸。

  • C#中Linq表达式使用场景(聚类,投影写法)

该项目主要解决石油产量的分析工作,数据库中存了石油产量的日和月数据,其中月数据中包含了年数据的字段和值,因此按日产量、月产量、年产量分析计算不存在什么问题。但项目中需求累计周产量、旬产量、季产量进行分析,这样的需求怎么解决呢?

一般来说有两种解决方案:

  1. 将日产量数据获取出来,然后按照日期进行排序,然后对于日产量进行foreach循环,周产量每七天相加可得,但旬产量分为上旬、中旬、下旬,这样对于上旬、中旬来说都是10天,下旬就是每个月的天数减去20天,按照这样的方式进行循环相加,这样做的弊端就是代码不好写且不易控制,代码可读性不佳。笔者并不推荐这种做法,当然大神除外哈。这样就催生了第二种解决方案,使用linq表达式可以很好的将其解决。
  2. 使用Linq表达式

假如日数据已经按生产日期进行排序之后获得了日数据list——WellsProdsDay.那么我们首先通过GroupBy()方法进行对周进行分组,代码为:

this.WellsProdsDay.GroupBy(x => x.ProdDate.AddDays(-(int)x.ProdDate.DayOfWeek))

prodDate即为每日的生产日期,类型为DateTime类型,于是我们对于周产量的字段写一个bean类,名为WellProdWeekDescriptor,假如这个bean类里定义了周产油量、周产水量(在此只举这两个字段为例)于是可以利用Select方法进行向新表投影,于是代码为:

  1. var wellsProdsWeek = this.WellsProdsDay.GroupBy(x => x.ProdDate.AddDays(-(int)x.ProdDate.DayOfWeek)).Select(item => new WellProdWeekDescriptor
  2. //周产油
  3. WeekOil = item.Sum(y => y.DailyOil),
  4. //周产水
  5. WeekWater = item.Sum(y => y.DailyWater)
  6. }).ToList();

在select方法中提供了Sum()即为求和,Average()方法求平均值,这些可以自己试用,在此只提供Linq写法,最后ToList()方法将新表进行封装,其返回值为List

,但为了方便写,C#提供了var替代了具体的返回值。

打开网易新闻 查看更多图片

同样地,旬产量的分析稍微有一点不同,那就是区分出每个旬的天数,当然一个GroupBy()是不够的,但是我已经写好了获取旬天数的工具方法,在此先展示出旬的工具方法代码:

  1. /// 获取指定年,指定月, 指定旬的开始日期
  2. /// 年
  3. /// 月
  4. /// 日
  5. /// 日期
  6. public static DateTime GetStartOfTen(int year, int month, int ten)
  7. {
  8. if (!IsValidYear(year))
  9. {
  10. throw new ArgumentOutOfRangeException("Valid values are 0-9999, and your transfer value is" + year);
  11. }
  12. if (!IsValidMonth(month))
  13. {
  14. throw new ArgumentOutOfRangeException("Valid values are 1-12, and your transfer value is" + month);
  15. }
  16. if (!IsValidTen(ten))
  17. {
  18. throw new ArgumentOutOfRangeException("Valid values are 1-3, and your transfer value is" + ten);
  19. }
  20. if (ten == 1)
  21. {
  22. return new DateTime(year, month, 1, 0, 0, 0, 0);
  23. }
  24. else if (ten == 2)
  25. {
  26. return new DateTime(year, month, 11, 0, 0, 0, 0);
  27. }
  28. else
  29. {
  30. return new DateTime(year, month, 21, 0, 0, 0, 0);
  31. }
  32. }

使用GroupBy()方法对日产量进行分组,代码为:

var wellsProdXun = this.WellsProdsDay.GroupBy(x => GetStartOfTen(x.ProdDate))

其中表达式中调用了GetStartOfTen()方法,根据日期自动分出上旬,中旬、下旬的组别。然后通过Select()方法进行投影,旬产量类名为WellProdXunDescriptor。代码为:

  1. var wellsProdXun = this.WellsProdsDay.GroupBy(x => GetStartOfTen(x.ProdDate)).Select(item => new WellProdXunDescriptor
  2. //旬产油
  3. XunOil = item.Sum(y => y.DailyOil),
  4. //旬产水
  5. XunWater = item.Sum(y => y.DailyWater)
  6. }).ToList();

打开网易新闻 查看更多图片

对于季产量,假如月数据已经按生产日期进行排序之后获得了月数据list——WellMonProdData.那么我们首先通过GroupBy()方法进行对季进行分组,代码为:

var wellSeasonProdData = this.WellMonProdData.GroupBy(x => x.ProdDate.AddMonths(0 - (x.ProdDate.Month - 1) % 3).AddDays(1 - x.ProdDate.Day))

通过Select()方法进行投影,季产量类名为WellProdSeasonDescriptor,代码为:

  1. var wellSeasonProdData = this.WellMonProdData.GroupBy(x => x.ProdDate.AddMonths(0 - (x.ProdDate.Month - 1) % 3).AddDays(1 - x.ProdDate.Day)).Select(item => new WellProdSeasonDescriptor
  2. //季产油
  3. SeasonOil = item.Sum(y => y.MonthlyOil),
  4. //季产水
  5. SeasonWater = item.Sum(y => y.MonthlyWater)
  6. }).ToList();

打开网易新闻 查看更多图片

年产量没有直接拿出数据库的字段值,同样也是使用分组的方式,这里把代码附上(原理都一样):

  1. var wellYearProdData = this.WellMonProdData.GroupBy(x => x.ProdDate.AddMonths(-(x.ProdDate.Month - 1)).AddDays(1 - x.ProdDate.Day)).Select(item => new WellProdYearDescriptor
  2. //年产油
  3. YearOil = item.Sum(y => y.MonthlyOil),
  4. //年产水
  5. YearWater = item.Sum(y => y.MonthlyWater)
  6. }).ToList();

当然Liuq表达式的强大之处并不局限于此,这只是解决了一个问题的需求,之后有很多地方都用到了Linq表达式的写法,基本很少使用foreach遍历的写法,代码冗长且不易阅读。今天就先分享到这里,接下来还会给大家更新其他的linq表达式以及前端工具包DevExpress的相关用法,如果您点进来请给笔者一个赞或者评论以示鼓励,你们的鼓励是我更新下去的动力。