6

我最近一直在使用 LINQ,感谢一些 StackOverflowers 的帮助,我能够让这个语句起作用:

var traceJob =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

if (traceJob != null && traceJob.Count() == 1)
{
 traceJob.First().RunNow();
 Console.WriteLine(traceJob.First().DisplayName + "  Last Run Time: " + traceJob.First().LastRunTime);
}

但是,我很困惑,因为使它起作用的部分是if(traceJob.Count() ==1). 如果我删除该部分,那么我会收到一条ObjectNullRef错误消息,指出枚举traceJob没有产生任何结果。

现在,据我所知,if检查计数的语句实际上不应该改变 Linq 语句的结果,对吧?谁能向我解释为什么我会看到这种行为?

4

5 回答 5

6

不,不应该。我的猜测是您遇到了枚举确实为空的情况,并且通过检查计数 > 0,First()不会失败。

作为旁注,Any()这里可能是一个更好的检查,因为(取决于存储库的底层存储)它可能比Count()

if (traceJob != null && traceJob.Any())
{
 traceJob.First().RunNow();
 Console.WriteLine(traceJob.First().DisplayName + "  Last Run Time: " + traceJob.First().LastRunTime);
}
于 2012-11-26T16:57:11.347 回答
4
var traceJob =
    (from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition).SingleOrDefault();

您可以使用 singleOrDefault 检查单个结果。它将返回与where条件匹配的结果,如果没有找到匹配项,则返回 null。如果为您的查询找到多个匹配项,则会引发异常。

这涵盖了您tracejob == null以及tracejob.count == 1条件。

MSDN 文章

于 2012-11-26T16:58:51.663 回答
2

我不知道您的“服务”的实际实现,但通常 linq 查询实际上仅在请求时才会填充它们的结果。所以 Count() 确实改变了 traceJob 的状态,很可能填充了内部集合。看起来 First() 没有填充内部集合,或者没有正确执行它,即使它通常应该这样做。

于 2012-11-26T16:56:06.810 回答
2

关于https://stackoverflow.com/a/1745716/1289709
我认为FirstOrDefault在这里使用更合理。

每当您使用 SingleOrDefault 时,您都清楚地声明该查询最多只能产生一个结果。另一方面,当使用 FirstOrDefault 时,查询可以返回任意数量的结果,但您声明您只想要第一个结果。

所以把你的代码改成这样:

var traceJob = (from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition).FirstOrDefault();

    if (traceJob != null)
    {
       traceJob.RunNow();
       Console.WriteLine(traceJob.DisplayName + "  Last Run Time: " + traceJob.LastRunTime);
    }
于 2012-11-26T17:04:57.743 回答
2

我认为您的问题出在调用它们时如何执行 linq 语句,而不是从声明它们的位置。

所以你的陈述:

var traceJob =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

这在功能上大致相当于:

IEnumerable<JobDefinition> GetJobDefinitions(YourService service, Guid traceGuid) 
{
    foreach(var jobDefinition in service.JobDefinitions) 
        if(jobDefinition.Id == traceGuid) 
            yield return jobDefinition;
}

所以当你打电话时,traceJob.Count()你做的相当于打电话GetJobDefinitions(service, traceGuid).Count(),每次你打电话时,traceJob.First()你都会再次进入循环。

service.JobDefinitions如果可以一遍又一遍地调用,这可能不是问题。但是,如果结果随时间变化(例如,如果在您执行时添加了作业)或者如果后续运行有不同的结果,您就会遇到问题。

在任何情况下,最好只执行一次循环:

var traceJobs =
    from jobDefinition in service.JobDefinitions
    where jobDefinition.Id == traceGuid
    select jobDefinition;

// This is where the loop above actually executes - if it's empty it will return null
var firstJob = traceJobs.FirstorDefault();

if(firstJob != null)
{
    firstJob.RunNow();
    Console.WriteLine(firstJob.DisplayName + "  Last Run Time: " + firstJob.LastRunTime);
}

或者,您可以通过将循环转换为列表或数组来强制执行循环:

var traceJobsExecuted = traceJobs.ToList();
于 2012-11-26T17:09:39.093 回答