我最近在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语句的结果吗?任何人都可以向我解释为什么我会看到这种行为吗?
答案 0 :(得分:6)
不,不应该。我的猜测是你遇到了枚举真的是空的情况,并通过检查计数> 0,First()
不会失败。
作为旁注,Any()
可能是更好的检查(取决于您的存储库的底层存储),它可能比Count()
更快:
if (traceJob != null && traceJob.Any())
{
traceJob.First().RunNow();
Console.WriteLine(traceJob.First().DisplayName + " Last Run Time: " + traceJob.First().LastRunTime);
}
答案 1 :(得分:4)
var traceJob =
(from jobDefinition in service.JobDefinitions
where jobDefinition.Id == traceGuid
select jobDefinition).SingleOrDefault();
您可以使用singleOrDefault检查单个结果。它将返回与where
条件匹配的结果,如果未找到匹配则返回null。如果找到多个匹配的查询,则会抛出异常。
这涵盖了您的tracejob == null
以及tracejob.count == 1
条件。
答案 2 :(得分:2)
我不知道您的“服务”的实际实现,但通常linq查询实际上仅在请求时填充其结果。所以Count()确实改变了traceJob的状态,很可能是填充内部集合。看起来First()不会填充内部集合,或者即使它通常也应该正确地执行它。
答案 3 :(得分: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);
}
答案 4 :(得分: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();