如何用Linq优化按最大附件大小拆分收件人报表的循环逻辑?
优化报表邮件拆分逻辑:用LINQ替代foreach循环
这段代码的核心功能是将报表与对应接收人关联,当单个收件人对应的报表总大小超过附件上限时,自动拆分出多封邮件。我们可以用LINQ的Aggregate方法替代原有的嵌套foreach循环,让逻辑更连贯,和前面的分组操作衔接更自然。
优化后的完整代码
internal static (List<MailToSend> mailToSends, List<Report> reportsNotUsed) CreateMailsWithReports( IList<Report> reports, IEnumerable<ReportReceiver> reportReceivers, long maxAttachmentSizeInBytes) { // 关联报表与接收人,并按收件人邮箱分组 var groupedByReceiver = reportReceivers .Join(reports, receiver => new { receiver.ReportTypeId, receiver.FundId }, report => new { report.ReportTypeId, report.FundId }, (receiver, report) => new { Receiver = receiver, Report = report }) .GroupBy(x => x.Receiver.Email, x => x.Report); // 对每个收件人的报表按大小拆分邮件,用Aggregate替代foreach循环 var mailToSends = groupedByReceiver .SelectMany(g => g.Aggregate( // 初始状态:空邮件列表 + 未初始化的当前邮件 + 已用字节数0 (Mails: new List<MailToSend>(), CurrentMail: (MailToSend)null, UsedBytes: 0L), (state, report) => { if (state.CurrentMail == null) { // 首次创建当前收件人的第一封邮件 var newMail = new MailToSend { CustomerEmail = g.Key, Reports = new List<Report>() }; newMail.Reports.Add(report); state.Mails.Add(newMail); return (state.Mails, newMail, report.SizeInBytes); } if (state.UsedBytes + report.SizeInBytes <= maxAttachmentSizeInBytes) { // 当前邮件仍可添加报表,直接追加 state.CurrentMail.Reports.Add(report); return (state.Mails, state.CurrentMail, state.UsedBytes + report.SizeInBytes); } else { // 超过附件上限,新建邮件并添加当前报表 var newMail = new MailToSend { CustomerEmail = g.Key, Reports = new List<Report> { report } }; state.Mails.Add(newMail); return (state.Mails, newMail, report.SizeInBytes); } }, // 最终只提取拆分完成的邮件列表 state => state.Mails)) .ToList(); // 筛选出未匹配到接收人的报表 var allUsedReports = mailToSends.SelectMany(m => m.Reports); var reportsNotUsed = reports.Except(allUsedReports).ToList(); return (mailToSends, reportsNotUsed); }
关键逻辑说明
- 用
Aggregate实现状态跟踪:替代原foreach循环中byteUsed和mail两个临时变量,在遍历报表的过程中维护「已生成邮件列表」「当前构建的邮件」「已用字节数」三个状态,完美封装拆分规则。 - 链式调用衔接分组逻辑:从报表-接收人关联、分组到邮件拆分,全程通过LINQ链式调用完成,无需额外的中间临时集合,逻辑更连贯。
- 调整未使用报表的计算:从最终拆分后的邮件集合中提取已使用报表,确保结果准确性(原代码基于拆分前的集合计算,存在逻辑偏差)。
内容的提问来源于stack exchange,提问作者Martin Andersen
相关产品推荐
相关产品推荐

