Linq查询当找不到结果时,First()似乎返回null

时间:2014-12-11 18:02:09

标签: sql-server linq linq-to-sql outer-join linqpad

我收到了以下内容(这个问题已经简化了):

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表没有所有学生的记录。难民价值适当地返回价值或者" IT WAS NULL"当Student_Program_Participation_Values中缺少记录时。但是,难民专栏会返回" NOT NULL!"或者"首先返回NULL!"。

我的问题是,为什么"首先返回NULL!"因为在我使用Linq的经验中,在一个空集上调用First()应该抛出一个异常,但是在这个查询中它似乎做了一些完全不同的事情。请注意,refg.Value在数据库中永远不会为空(它是有效值,或者没有记录)。

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

澄清一下,这里有一些示例输出:

StudentId   Refugees          Refugee
22122       True              NOT NULL!
2332        IT WAS NULL       First Returned NULL!

在上述情况下,当难民返回" IT是否为空时" Student_Program_Participation_Values表中没有记录,所以我希望First()抛出异常,但是它是null,因此难民显示"首先返回NULL!"。

有什么想法吗?

更新:Enigmativity推动我朝着正确的方向前进,指出我是第一个()时遇到了First()调用,而First()根本不是一个函数调用,而只是简单地翻译成& #34; 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),因为整个事情都是基于外部联接,整个查询只是简单地合并为“第一次返回NULL!”#。

因此,在我看来,Linq To SQL(以及LINQ to Entities)与在列表等上调用相同命名的方法非常不同,这本身就是一种认知错误。

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

1 个答案:

答案 0 :(得分:0)

如果没有您的数据库,我无法测试此代码,但无论如何都要尝试,看看它是否有效。

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有时可能会略微偏离,因此您无法获得预期的结果。这就像将英语翻译成法语一样 - 您可能无法获得正确的翻译。