当未找到结果时,其中 First() 似乎返回 null 的 Linq 查询

Posted

技术标签:

【中文标题】当未找到结果时,其中 First() 似乎返回 null 的 Linq 查询【英文标题】:Linq query where First() appears to return null when results are not found 【发布时间】:2015-02-10 07:49:07 【问题描述】:

我收到了以下信息(已针对问题进行了简化):

int programId = 3;
int refugeeId = 5;

var q = (  from st in Students
           join cr in Class_Rosters on st.StudentId equals cr.StudentId
           join pp in Student_Program_Part on cr.StudentId equals pp.StudentId
           from refg in (Student_Program_Participation_Values
                         .Where(rudf => rudf.ProgramParticipationId == pp.ProgramParticipationId 
                                     && rudf.UDFId == refugeeId)).DefaultIfEmpty()
          where cr.ClassId == 22898 
             && pp.ProgramId == programId
         select new
         
             StudentId = st.StudentId,

              Refugees = refg.Value ?? "IT WAS NULL",
              Refugee = Student_Program_Participation_Values
                       .Where(rudf => rudf.ProgramParticipationId == pp.ProgramParticipationId 
                                   && rudf.RefugeeId == refugeeId)
                       .Select(rudf => (rudf.Value == null ? "IT WAS NULL" : "NOT NULL!"))
                       .First() ?? "First Returned NULL!",
          );
q.Dump();

在上述查询中,Student_Program_Participation_Values 表没有所有学生的记录。当 Student_Program_Participation_Values 中缺少记录时,难民值会正确返回值或“IT WAS NULL”。但是,难民列返回“NOT NULL!”或“第一次返回 NULL!”。

我的问题是,为什么“First Returned NULL!”此后,根据我对 Linq 的经验,在空集上调用 First() 应该会引发异常,但在此查询中,它似乎在做一些完全不同的事情。请注意,数据库中的 refg.Value 永远不会为空(它要么是有效值,要么没有记录)。

另请注意,这是 Linq to SQL,我们正在 Linqpad 中运行此查询。

为了澄清,这里是一些示例输出:

StudentId 难民 难民 22122 真不为空! 2332 它是 NULL 首先返回 NULL!

在上面,当 Refugees 返回“IT WAS NULL”时,Student_Program_Participation_Values 表中没有记录,所以我希望 First() 抛出异常,但它是 null,所以 Refugee 显示“First Returned NULL!”。

有什么想法吗?

更新:神秘性将我推向正确的方向,指出当我是 IQueryable 时,我被困在 First() 调用上,First() 根本不是函数调用,而是简单地翻译成“TOP 1” “在查询中。当我查看 LINQPad 中生成的 SQL 时,这一点很明显。下面是生成的 SQL 的重要部分,它清楚地说明了正在发生的事情和原因。我不会粘贴整个内容,因为它非常庞大且与讨论无关。

...
COALESCE((
    SELECT TOP (1) [t12].[value]
    FROM (
        SELECT 
            (CASE 
                WHEN 0 = 1 THEN 'IT WAS NULL'
                ELSE CONVERT(NVarChar(11), 'NOT NULL!')
             END) AS [value], [t11].[ProgramParticipationId], [t11].[UDFId]
        FROM [p_Student_Program_Participation_UDF_Values] AS [t11]
        ) AS [t12]
    WHERE ([t12].[ProgramParticipationId] = [t3].[ProgramParticipationId]) AND ([t12].[UDFId] = @p8)
), 'First Returned NULL!') AS [value3]
...

所以,在这里您可以清楚地看到 Linq 将 First() 转换为 TOP (1) 并且还确定“IT WAS NULL”永远不会发生(因此 0 = 1),因为整个事情都是基于外部join 并且整个查询简单地合并为“First Returned NULL!”。

因此,我认为 Linq To SQL(以及就此而言的 LINQ to Entities)与在列表等上调用同名方法非常不同,这完全是我的一个认知错误。

我希望我的错误对其他人有用。

【问题讨论】:

【参考方案1】:

如果没有您的数据库,我无法测试此代码,但还是尝试一下,看看它是否有效。

var q =
(
    from st in Students
    join cr in Class_Rosters on st.StudentId equals cr.StudentId
    where cr.ClassId == 22898
    join pp in Student_Program_Part on cr.StudentId equals pp.StudentId
    where pp.ProgramId == programId
    select new
    
        StudentId = st.StudentId,
        refg =
            Student_Program_Participation_Values
                .Where(rudf =>
                    rudf.ProgramParticipationId == pp.ProgramParticipationId
                        && rudf.UDFId == refugeeId)
                .ToArray()
    
).ToArray();

var q2 =
    from x in q
    from refg in x.refg.DefaultIfEmpty()
    select new
    
        StudentId = x.StudentId,
        Refugees = refg.Value ?? "IT WAS NULL",
        Refugee = refg
            .Select(rudf => (rudf.Value == null ? "IT WAS NULL" : "NOT NULL!"))
            .First() ?? "First Returned NULL!",
    ;

q2.Dump();

基本上,这个想法是从数据库中干净地捕获记录,将它们放入内存,然后执行所有null 的工作。如果这可行,那是因为未能将 LINQ 转换为相同的 SQL。翻译后的 SQL 有时会有些偏差,因此您无法获得预期的结果。这就像将英语翻译成法语 - 您可能无法得到正确的翻译。

【讨论】:

Enigmativity,感谢您的回复,但我并不是真的在寻找替代解决方案。在我原来的解决方案中,暴露为“难民”的外部连接工作正常。如果不清楚,我很抱歉,但我想了解的问题是:在我的示例中,为什么 First() 调用没有引发异常,即使显然 Select 必须不返回任何结果。 @ScottGartner - 我认为您需要了解代码正在被转换为 SQL,并且没有实际调用 .First()。因此,为什么没有例外。 我不敢相信在提出答案之前我没有查看 SQL。当然,你是对的,这是我在 First() 调用上卡住的感知问题。谢谢,我会用我发现的内容更新我的答案。

以上是关于当未找到结果时,其中 First() 似乎返回 null 的 Linq 查询的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript对象函数和`this`,当未绑定并在表达式/括号中返回时

仅当未附加性能分析器时 UI 线程锁定

仅当未使用 @azure/msal-react 进行身份验证时才重定向 onLoad

为啥当未接受的套接字发送消息时,套接字不会抛出错误?

当未存储返回值时,std::async 不会产生新线程

C++ std::set emplace 返回值 first second