dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.79k stars 3.19k forks source link

Expressions without explicit type causes filter to be eliminated/false #35059

Open cincuranet opened 1 week ago

cincuranet commented 1 week ago
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;

using var db = new MyContext();
var parameter = Expression.Parameter(typeof(Havit), "o");
var equal = Expression.Equal(Expression.Property(parameter, "Deleted"), Expression.Constant(null));
var equal2 = Expression.Equal(Expression.Property(parameter, "Deleted"), Expression.Constant(null, typeof(DateTime?)));
var expr = Expression.Lambda<Func<Havit, bool>>(equal, parameter);
var query = db.Set<Havit>().Where(x => x.Deleted == null).Where(x => x.Deleted == null).ToQueryString();
var query2 = db.Set<Havit>().Where(expr).Where(x => x.Deleted == null).ToQueryString();
Console.WriteLine(query);
Console.WriteLine();
Console.WriteLine(query2);

class MyContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Havit>();
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite();
    }
}

class Havit
{
    public int Id { get; set; }
    public DateTime? Deleted { get; set; }
}

The first query properly results in this SQL.

SELECT "h"."Id", "h"."Deleted"
FROM "Havit" AS "h"
WHERE "h"."Deleted" IS NULL

While the second results in this unexpected SQL.

SELECT "h"."Id", "h"."Deleted"
FROM "Havit" AS "h"
WHERE 0

The culprit seems to be that the expression var equal = Expression.Equal(Expression.Property(parameter, "Deleted"), Expression.Constant(null)); does not contain the type information like the var equal2 = Expression.Equal(Expression.Property(parameter, "Deleted"), Expression.Constant(null, typeof(DateTime?))); which works fine.

Include provider and version information

EF Core version: 8.0.10 and 9.0 RC2 Database provider: unrelated Target framework: .NET 8.0

cc @hakenr

hakenr commented 1 week ago

Just to make this ticket easier to find for others facing the same issue, using UseSqlServer() this results in query with WHERE 0 = 1:

SELECT [h].[Id], [h].[Deleted]
FROM [Havit] AS [h]
WHERE 0 = 1
cincuranet commented 1 week ago

Because the manually constructed expression differs (it is missing the type) from what compiler produces for x => x.Deleted == null - and because the "fix" is easy and straightforward - we don't consider this to be a high priority. That said, we'll put it into backlog and consider it for some future release, because especially for the Expression.Constant(null) specifically it seems to be something nice to have.