我需要处理来自数据库的多个项目,并将JSON字符串中的处理状态发送回调用者。我写了这样的东西(简化):
string ProcessAll()
{
return ConvertToJsonString(
database.
Get<XAccount>().
Where(a => a.Enabled).
Select(a => new
{
Id = a.Id,
Success = HeavyProcessing(a)
}).
ToArray());
}
bool HeavyProcessing(XAccount account)
{
try
{
.... up to 10-20 HTTP requests here ......
return true;
}
catch (Exception ex)
{
LogException(ex);
return false;
}
}
HeavyProcessing
非常复杂:在内部执行大量HTTP请求。可以在Select
中调用它吗?如果不是,我怎样才能优雅地重新设计ProcessAll
?
我的另一个想法是创建IEnumerable<XAccountStatus> HeavyProcessAll(IEnumerable<XAccount> accounts)
方法,并在其中使用return yield
。在重型方法中使用yield
是否可以?
答案 0 :(得分:4)
它不会产生太大的影响,因为无论如何,您在致电ToArray
之后立即致电Select
,因此您需要等待所有待处理的项目继续执行。
您可以考虑使用并行执行,使用PLINQ,同时对多个项目执行这些繁重的操作。 AsParallel
应该成功:
string ProcessAll()
{
return ConvertToJsonString(
database.
Get<XAccount>().
Where(a => a.Enabled).
AsParallel().
Select(a => new
{
Id = a.Id,
Success = HeavyProcessing(a)
}).
ToArray());
}
答案 1 :(得分:0)
我认为这是一个非常明智的问题。我不相信LINQ to Objects指定了确切的执行模式。没有正式保证您的选择器只能按顺序每个项目调用一次。如果您只依赖于文档所做的正式保证,那么您必须假设您的“重量级”#34;选择器功能可能会多次运行或无序运行。
现在显然这不是Enumerable.Select
所做的。几乎只有一个合理的实现可能:
foreach (var item in items) yield return selector(item);
我愿意在我的项目中依赖这个事实,我向其他人推荐这个事实。
鉴于我们现在拥有(或假设)LINQ如何运行查询的可执行模型,我们可以看到使用LINQ运行重选择器确实是安全的。
可能的复杂情况是延迟评估,但您已通过使用ToArray
实现序列来排除这种可能性。
还要考虑LINQ带来的代码质量的好处。
使用问题中提出的yield return
编写自定义函数基本上会产生同样的效果。如果您将处理代码内联到上面给出的代码段中(用它替换selector
),您将得到您的建议。没必要那样做。
答案 2 :(得分:-1)
您在谈论实体框架吗?我很确定它不会这样。 EF不知道如何将HeavyProcessing
转换为SQL查询,并且您将在运行时获得异常。因此,您必须先从数据库中获取所有XAccount
个项目,然后在每个项目上应用HeavyProcessing
。