`Contains` on a JSON primitive collection compares JSON text, so `DateTime` and `TimeOnly` items never match

Aperta
#4,099 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@Mr444-dev ci sta già lavorando.

Dal 3/11/2026.

  • #4118 di @Mr444-dev — aperta

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
45/100
Tipo di issue
Bug
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
csharp, postgresql, sql
Ambito
database

Direzione di ricerca

Start from src/EFCore.PG/Query/Internal/NpgsqlQueryableMethodTranslatingExpressionVisitor.cs lines 756-766 where the JSON collection Contains is translated. Read the existing TODO and the JsonValueReaderWriter used for constants/parameters, and check how array unnesting is done elsewhere for typed comparison. Run the repro in the issue to see the mismatched SQL, then adjust the translation so the element type matches the stored JSON text.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

Environment
PostgreSQL 18.6 (postgres:18), session TimeZone Etc/UTC
.NET 10 runtime 10.0.11, Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, EF Core 10.0.4
.NET 11 runtime 11.0.0-rc.2, Npgsql.EntityFrameworkCore.PostgreSQL 11.0.0-rc.2, EF Core 11.0.0-rc.2
Also Npgsql 10.0.3 on both lines, macOS arm64

Both lines print the same output. main at 150011d has the same code.

LINQ Stored by EF .NET PostgreSQL SQL
r.Doc.Dates.Contains(ten) "2026-01-01T10:00:00Z" match no match (r."Doc" -> 'Dates') @> to_jsonb(@ten)
r.Doc.Times.Contains(new TimeOnly(10, 0)) "10:00:00.0000000" match no match (r."Doc" -> 'Times') @> to_jsonb(TIME '10:00:00')

to_jsonb() of the item gives "2026-01-01T10:00:00+00:00" and "10:00:00", not the text EF wrote into the document, and @> compares the strings.

Repro
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

await using var context = new TestContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var ten = new DateTime(2026, 1, 1, 10, 0, 0, DateTimeKind.Utc);
context.Rows.Add(new Row { Doc = new Doc { Dates = [ten], Times = [new TimeOnly(10, 0)] } });
await context.SaveChangesAsync();
var rows = await context.Rows.AsNoTracking().ToListAsync();

Console.WriteLine($"Doc.Dates.Contains(10:00 UTC): .NET {rows.Count(r => r.Doc.Dates.Contains(ten))}, database {await context.Rows.CountAsync(r => r.Doc.Dates.Contains(ten))}");
Console.WriteLine($"Doc.Times.Contains(10:00): .NET {rows.Count(r => r.Doc.Times.Contains(new TimeOnly(10, 0)))}, database {await context.Rows.CountAsync(r => r.Doc.Times.Contains(new TimeOnly(10, 0)))}");
Console.WriteLine($"Stored document: {await context.Database.SqlQueryRaw<string>("""SELECT "Doc"::text AS "Value" FROM "Rows" """).SingleAsync()}");

public class TestContext : DbContext
{
    public DbSet<Row> Rows => Set<Row>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseNpgsql(Environment.GetEnvironmentVariable("Test__Npgsql__DefaultConnection"))
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        => modelBuilder.Entity<Row>().ComplexProperty(r => r.Doc, d => d.ToJson());
}

public class Row
{
    public int Id { get; set; }
    public Doc Doc { get; set; } = new();
}

public class Doc
{
    public List<DateTime> Dates { get; set; } = [];
    public List<TimeOnly> Times { get; set; } = [];
}

Output, the same on both lines:

Doc.Dates.Contains(10:00 UTC): .NET 1, database 0
Doc.Times.Contains(10:00): .NET 1, database 0
Stored document: {"Dates": ["2026-01-01T10:00:00Z"], "Times": ["10:00:00.0000000"]}

The translation is NpgsqlQueryableMethodTranslatingExpressionVisitor.cs#L756-L766. Its TODO already mentions producing the JSON value on the client with the JsonValueReaderWriter for constants and parameters, which would fix it for those. For a column operand, the comparison would have to be typed (unnest the array and compare as the element type).

Lingua principale
C#
Stelle
1.8k
Fork
268
Merge medio
2h 35m
PR unite (30g)
136

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di npgsql/efcore.pg

Tutte le issue di npgsql/efcore.pg

Issue simili

Altre issue su C#

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.