EF6 - 使用可为空属性(外键,TPH)时的无效 SQL 查询

Posted

技术标签:

【中文标题】EF6 - 使用可为空属性(外键,TPH)时的无效 SQL 查询【英文标题】:EF6 - invalid SQL query when using nullable property (foreign key, TPH) 【发布时间】:2014-08-16 11:11:12 【问题描述】:

Entity Framework 为以下 LINQ 查询生成无效的 SQL 查询:

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

我的 ParentId 属性是可为空的 int,同一个表的外键(Id 属性)。我的结果是一个空数组,但它不应该。我使用其他表使用了类似的查询(检查可空属性没有值),它工作得很好。在这种情况下,不同之处在于 ParentId 是外键,而数据库表使用 TPH。这是一个错误还是我犯了一些错误?为什么 EF 完全忽略可为空的属性?我的配置和EF生成的SQL(命名只是举例,例如“汽车”):

上下文配置:

// 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 获取所有实体(ParentId 被忽略):

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);

它会起作用的。

【讨论】:

以上是关于EF6 - 使用可为空属性(外键,TPH)时的无效 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

使用 Database.Create 的对象名称“dbo.__MigrationHistory”无效;传入连接字符串时的EF6.02

SQL 在 Where 子句中处理可为空的外键

EF6:导航属性外键到外键

EF6(代码优先)单个外键属性上的多个导航属性

输入数据集可为空的日期值存在问题

为可空外键创建导航属性