EF6 — недопустимый SQL-запрос при использовании свойства с нулевым значением (внешний ключ, TPH)

#c# #entity-framework

#c# #entity-framework

Вопрос:

Entity Framework генерирует недопустимый SQL-запрос для следующего запроса LINQ:

 Car[] results = this.DbContext.Cars
    .Where(x => !x.ParentId.HasValue) // also for x.ParentId == null
    .ToArray();
 

Моим свойством ParentID является nullable int, внешний ключ к той же таблице (свойство Id). Мои результаты представляют собой пустой массив, но этого не должно быть. Я использовал аналогичный запрос (проверяя, что свойство nullable не имеет значения), используя другие таблицы, и он работал просто отлично. В этом случае разница в том, что ParentID является внешним ключом, а таблица базы данных использует TPH. Это ошибка или я допустил какие-то ошибки? Почему EF вообще игнорирует свойство nullable? Моя конфигурация и сгенерированный SQL с помощью EF (именование только для примера, например, «Car»):

Конфигурация контекста:

 // TPH (Table per Hierarchy)
modelBuilder.Entity<Car>()
    .Map<CarA>(x => x.Requires("type").HasValue(1))
    .Map<CarB>(x => x.Requires("type").HasValue(2))
    .Map<CarC>(x => x.Requires("type").HasValue(3))
    .Map<CarD>(x => x.Requires("type").HasValue(4));

// parent child relationship
modelBuilder.Entity<Car>()
    .HasMany(x => x.Children)
    .WithRequired()
    .HasForeignKey(child => child.ParentId);
 

Свойства моего класса:

 [Column("parent_id")]
public int? ParentId { get; set; }

public virtual List<Car> Children { get; set; }
 

для:

 Car[] results = this.DbContext.Cars
    .Where(x => !x.ParentId.HasValue)
    .ToArray();
 

Я получаю пустой результат с сгенерированным SQL:

 SELECT 
    CAST(NULL AS int) AS [C1], 
    CAST(NULL AS int) AS [C2], 
    ...
    ...
    ...
    CAST(NULL AS decimal(18,2)) AS [C20], 
    CAST(NULL AS datetime2) AS [C21]
    FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
    WHERE 1 = 0
 

Но это должно быть:

 ...
...
WHERE [Extent1].[car] IS NULL
 

для:

 var results = this.DbContext.Cars
    .Where(x => x.ParentId.HasValue)
    .ToArray();
 

Я получаю все объекты (родительский идентификатор игнорируется) с сгенерированным SQL:

 SELECT 
    [Extent1].[type] AS [type], 
    [Extent1].[id] AS [id], 
    [Extent1].[parent_id] AS [parent_id], 
    [Extent1].[name] AS [name], 
    ...
    ...
    ...
    FROM [dbo].[car] AS [Extent1]
    WHERE [Extent1].[type] IN (1,2,3,4)
 

Протестировано на EF6 6.0.2 / 6.1.1 и MS SQL Server.

Ответ №1:

Вау… это забавное поведение, но решение довольно простое. У вас есть свойство внешнего ключа с нулевым значением, но вы определили связь как требуется. Просто измените

 modelBuilder.Entity<Car>()
    .HasMany(x => x.Children)
    .WithRequired()
    .HasForeignKey(child => child.ParentId);
 

Для

 modelBuilder.Entity<Car>()
    .HasMany(x => x.Children)
    .WithOptional()
    .HasForeignKey(child => child.ParentId);
 

И это будет работать.