Почему порядок запросов LET имеет значение в этом запросе Entity Framework?

Запрос для сетки в поддерживаемом Entity Framework.NET-приложении .NET, над которым я работаю, давал ошибку 500 (The cast to value type 'System.Int32' failed because the materialized value is null. Either the result type generic parameter or the query must use a nullable type.), когда у объекта строки сетки имелись нулевые дочерние элементы в определенном - много отношений. Нуль возвращался к несвязаному целочисленному свойству. Непосредственно, обращая порядок двух независимых операторов Let в выражении Linq, ошибка исчезла.

То есть, если есть только один виджет (ID: 1, CreatedOn: некоторое datetime), у которого нет баров и один Foo (fValue: 96)

from w in Widgets.OrderBy(w => w.CreatedOn)
let foo = w.Foos.FirstOrDefault()
let bar = w.Bars.FirstOrDefault()
select new { w.WidgetID, foo.fValue }

или

from w in Widgets
let bar = w.Bars.FirstOrDefault()
let foo = w.Foos.FirstOrDefault()
orderby w.CreatedOn
select new { w.WidgetID, foo.fValue }

дает {WidgetID: 1, fValue: 96}, как ожидалось, но

from w in Widgets.OrderBy(w => w.CreatedOn)
let bar = w.Bars.FirstOrDefault()
let foo = w.Foos.FirstOrDefault()
select new { w.WidgetID, foo.fValue }

возвращается с {WidgetID: 1, fValue: NULL}, который, конечно, сбой, потому что Foo.fValue является целым числом.

Все три выражения генерируют несколько разные SQL-запросы в Entity Framework, которые я ожидал бы - выражение с ошибкой содержит предложение

... 
(SELECT TOP (1) 
    [Extent7].[fValue] AS [fValue]
    FROM   (SELECT TOP (1) [Extent6].[BarID] AS [BarID]
        FROM [dbo].[Bars] AS [Extent6]
        WHERE [Extent1].[WidgetID] = [Extent6].[bWidgetID] ) AS [Limit5]
    CROSS JOIN [dbo].[Foos] AS [Extent7]
    WHERE [Extent1].[WidgetID] = [Extent7].[fWidgetID]) AS [C1]
...

который, я считаю, является виновником (0 баров пересекаются с результатами 1 Foo = 0). Поэтому я понимаю "как" ошибки; то, что меня заводит, заключается в том, что я понятия не имею, почему порядок LET или я OrderBy с вызовом метода Linq против выражения Linq должен иметь значение.

Здесь приведенная схема/данные таблицы, если вы хотите поэкспериментировать:

create table Widgets (
    WidgetID int not null primary key,
    CreatedOn datetime not null
)    
insert Widgets values (1, '1995-02-03')

create table Foos (
    FooID int not null primary key,
    fWidgetID int not null references Widgets (WidgetID),
    fValue int not null
)
insert Foos values (7, 1, 96)

create table Bars (
    BarID int not null primary key,
    bWidgetID int not null references Widgets (WidgetID),
    bValue int not null
)

Можете ли вы объяснить, почему эти 3 выражения не логически эквивалентны в Entity Framework?

Ответ 1

Я считаю, что это ошибка, связанная с этой проблемой для Entity Framework: https://entityframework.codeplex.com/workitem/1196. В соответствии с этой проблемой при использовании order by, let и FirstOrDefault дерево запросов скомпилируется с ошибкой SQL-запроса.

К сожалению, проблема почти двух лет, поэтому эта ошибка может не быть приоритетом для команды EF. Возможно, он будет исправлен в EF7!