c# – 如何制作流式LINQ表达式,以提供已过滤的项目以及过滤的项目?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 如何制作流式LINQ表达式,以提供已过滤的项目以及过滤的项目?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3988字,纯文字阅读大概需要6分钟。
内容图文
我正在将Excel电子表格转换为“元素”列表(这是一个域名术语).在此转换期间,我需要跳过标题行并抛出无法转换的格式错误的行.
有趣的来了.我需要捕获那些格式错误的记录,以便我可以报告它们.我构建了一个疯狂的LINQ语句(如下).这些扩展方法隐藏了OpenXml库中类型的凌乱LINQ操作.
var elements = sheet
.Rows() <-- BEGIN sheet data transform
.SkipColumnHeaders()
.ToRowLookup()
.ToCellLookup()
.SkipEmptyRows() <-- END sheet data transform
.ToElements(strings) <-- BEGIN domain transform
.RemoveBadRecords(out discard)
.OrderByCompositeKey();
有趣的部分从ToElements开始,在那里我将行查找转换为我的域对象列表(详细信息:它称为ElementRow,后来转换为Element).只使用一个键(Excel行索引)创建错误记录,并且与真实元素相比是唯一可识别的.
public static IEnumerable<ElementRow> ToElements(this IEnumerable<KeyValuePair<UInt32Value, Cell[]>> map)
{
return map.Select(pair =>
{
try
{
return ElementRow.FromCells(pair.Key, pair.Value);
}
catch (Exception)
{
return ElementRow.BadRecord(pair.Key);
}
});
}
然后,我想删除那些不良记录(在过滤之前更容易收集所有这些记录).那个方法是RemoveBadRecords,就像这样开始……
public static IEnumerable<ElementRow> RemoveBadRecords(this IEnumerable<ElementRow> elements)
{
return elements.Where(el => el.FormatId != 0);
}
但是,我需要报告丢弃的元素!而且我不想通过报告来混淆我的变换扩展方法.所以,我去了out参数(考虑到在匿名块中使用out参数的the difficulties)
public static IEnumerable<ElementRow> RemoveBadRecords(this IEnumerable<ElementRow> elements, out List<ElementRow> discard)
{
var temp = new List<ElementRow>();
var filtered = elements.Where(el =>
{
if (el.FormatId == 0) temp.Add(el);
return el.FormatId != 0;
});
discard = temp;
return filtered;
}
哦,瞧!我以为我是铁杆,并且会一次性工作……
var discard = new List<ElementRow>();
var elements = data
/* snipped long LINQ statement */
.RemoveBadRecords(out discard)
/* snipped long LINQ statement */
discard.ForEach(el => failures.Add(el));
foreach(var el in elements)
{
/* do more work, maybe add more failures */
}
return new Result(elements, failures);
但是,当我循环播放它时,我的丢弃列表中没有任何内容!我逐步完成了代码并意识到我成功创建了一个完全流式LINQ语句.
>临时列表已创建
>已分配Where过滤器(但尚未运行)
>并且分配了丢弃清单
>然后返回流媒体的东西
迭代丢弃时,它不包含任何元素,因为元素尚未迭代.
有没有办法使用我构建的东西来解决这个问题?我是否必须在错误记录过滤器之前或期间强制重复数据?我错过了另一种建筑吗?
一些评论
Jon提到了任务/正在/正在发生.我只是没有等待它.如果我在元素迭代后检查丢弃的内容,它实际上已经满了!所以,我实际上没有任务分配问题.除非我接受Jon关于LINQ语句中哪些好/坏的建议.
解决方法:
When the statement was actually iterated, the Where clause ran and temp filled up, but discard was never assigned again!
它不需要再次分配 – 将填充将在调用代码中分配给丢弃的现有列表.
但是,我强烈建议不要采用这种方法.在这里使用out参数确实违背了LINQ的精神. (如果你两次迭代你的结果,你最终会得到一个包含所有坏元素两次的列表.Ick!)
我建议在删除坏记录之前实现查询 – 然后你可以运行单独的查询:
var allElements = sheet
.Rows()
.SkipColumnHeaders()
.ToRowLookup()
.ToCellLookup()
.SkipEmptyRows()
.ToElements(strings)
.ToList();
var goodElements = allElements.Where(el => el.FormatId != 0)
.OrderByCompositeKey();
var badElements = allElements.Where(el => el.FormatId == 0);
通过在List<>中实现查询,您只需按ToRowLookup,ToCellLookup等处理每一行.它确实意味着您需要有足够的内存来保存所有元素,当然.还有其他方法(例如对每个坏元素进行操作,同时对其进行过滤)但它们仍然可能最终变得相当脆弱.
编辑:Servy提到的另一个选择是使用ToLookup,它将实现并分组:
var lookup = sheet
.Rows()
.SkipColumnHeaders()
.ToRowLookup()
.ToCellLookup()
.SkipEmptyRows()
.ToElements(strings)
.OrderByCompositeKey()
.ToLookup(el => el.FormatId == 0);
然后你可以使用:
foreach (var goodElement in lookup[false])
{
...
}
和
foreach (var badElement in lookup[true])
{
...
}
请注意,这会对所有元素执行排序,无论好坏.另一种方法是从原始查询中删除顺序并使用:
foreach (var goodElement in lookup[false].OrderByCompositeKey())
{
...
}
我个人并不喜欢用真/假分组 – 感觉有点滥用通常意味着基于密钥的查找 – 但它肯定会起作用.
内容总结
以上是互联网集市为您收集整理的c# – 如何制作流式LINQ表达式,以提供已过滤的项目以及过滤的项目?全部内容,希望文章能够帮你解决c# – 如何制作流式LINQ表达式,以提供已过滤的项目以及过滤的项目?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。