Description
Description
According to C# null semantics, given a bool? A
, the expressions !(A == true)
and A != true
should be equivalent. When used in a filter, both expressions should translate to SQL that includes rows where A
is null
. Previous versions of EF correctly reflected this behaviour. However, in v9, !(A == true)
is incorrectly translated to SQL that excludes rows where A
is null
, while A != true
is translated correctly.
This seems to apply to both the SQL Server and Sqlite providers.
Steps to reproduce
using Microsoft.EntityFrameworkCore;
var db = new MyContext();
var notBlocked = db.Set<User>().Where(x => !(x.IsBlocked == true)).ToQueryString();
var notBlocked2 = db.Set<User>().Where(x => x.IsBlocked != true).ToQueryString();
Console.WriteLine($"""
Query 1:
{notBlocked}
Query 2:
{notBlocked2}
""");
class MyContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlite();
}
class User
{
public int Id { get; set; }
public bool? IsBlocked { get; set; }
}
Expected Output
Using version 8.0.12 of EF and the Sqlite provider:
Query 1:
SELECT "u"."Id", "u"."IsBlocked"
FROM "Users" AS "u"
WHERE "u"."IsBlocked" = 0 OR "u"."IsBlocked" IS NULL
Query 2:
SELECT "u"."Id", "u"."IsBlocked"
FROM "Users" AS "u"
WHERE "u"."IsBlocked" <> 1 OR "u"."IsBlocked" IS NULL
Version 7.0.20 produces virtually the same output, only wrapping the IS NULL comparison in parentheses.
Actual Output
Using version 9.0.1 of EF and the Sqlite provider:
Query 1:
SELECT "u"."Id", "u"."IsBlocked"
FROM "Users" AS "u"
WHERE "u"."IsBlocked" = 0
Query 2:
SELECT "u"."Id", "u"."IsBlocked"
FROM "Users" AS "u"
WHERE "u"."IsBlocked" = 0 OR "u"."IsBlocked" IS NULL
Environment
EF Core version: 9.0.1
Database provider: Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Sqlite
Target framework: .NET 9.0
Operating system: Windows 11
IDE: Visual Studio 2022 17.12.3
Activity