在Select中调用重方法是否可以?

时间:2015-01-02 21:45:06

标签: c#

我需要处理来自数据库的多个项目,并将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是否可以?

3 个答案:

答案 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