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

Ouverte
#4,099 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 1 jour

@Mr444-dev y travaille déjà.

Depuis le 3/11/2026.

  • #4118 par @Mr444-dev — ouverte

Évaluation

Difficulté
4/5
Temps estimé
3-5 jours
Accessibilité débutants
45/100
Type d'issue
Bug
Clarté
Plutôt claire
Activité
Active
Stack technique
csharp, postgresql, sql
Domaine
database

Piste de recherche

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.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

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

Langage dominant
C#
Étoiles
1.8k
Forks
268
Merge moyen
2 h 35 min
PR mergées (30 j)
136

Préparer son environnement

Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de npgsql/efcore.pg

Toutes les issues de npgsql/efcore.pg

Issues similaires

Plus d'issues C#

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.