Repository navigation
Description
After upgrading from Npgsql 8 to 9 I was running into this error wherever I'm trying to select tuples from my database context:
System.InvalidCastException: Reading as 'System.ValueTuple`2[[System.Guid, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[System.Int32, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]' is not supported for fields having DataTypeName 'record' ---> System.NotSupportedException: Could not read a PostgreSQL record. If you're attempting to read a record as a .NET tuple, call 'EnableRecordsAsTuples' on 'NpgsqlDataSourceBuilder' or NpgsqlConnection.GlobalTypeMapper (see https://www.npgsql.org/doc/types/basic.html and the 8.0 release notes for more details). If you're reading a record as a .NET object array using NpgsqlSlimDataSourceBuilder, call 'EnableRecords'.
This was despite calling .ConfigureDataSource(ds => ds.EnableRecordsAsTuples()) when setting up the context pool.
I managed to track it down to the fact that I am also calling MapEnum further down the configuration chain. If I remove the MapEnum calls, or move them so that those calls are made before the ConfigureDataSource call then the above error goes away. Here is a small webapi program that reproduces the problem:
Program.cs
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
public class TestEntity {
[Key]
public int Id { get; set; }
public required string First { get; set; }
public required string Second { get; set; }
}
public class AppDbContext(
DbContextOptions<AppDbContext> options) : DbContext(options) {
public required DbSet<TestEntity> TestEntities { get; set; }
}
[DbContext(typeof(AppDbContext))]
[Migration("00_InitialSchema")]
public class InitialSchema : Migration {
protected override void Up(MigrationBuilder migrationBuilder) {
migrationBuilder.AlterDatabase()
.Annotation("Npgsql:Enum:public.test_enum", "value_one,value_two");
migrationBuilder.CreateTable(
name: "TestEntities",
columns: table => new {
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy",
NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
First = table.Column<string>(type: "text", nullable: false),
Second = table.Column<string>(type: "text", nullable: false)
},
constraints: table => {
table.PrimaryKey("PK_TestEntities", x => x.Id);
});
}
}
public enum TestEnum {
ValueOne,
ValueTwo,
}
public class Program {
public static void Main(string[] args) {
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddDbContextPool<AppDbContext>(
static (provider, builder) => builder
.UseNpgsql(
"Server=127.0.0.1;User Id=cosmic;Password=password;Database=postgres",
static opt => opt
.ConfigureDataSource(static ds => ds
.EnableRecordsAsTuples())
// commenting out the following line or moving it
// before the ConfigureDataSource call fixes it
.MapEnum<TestEnum>("test_enum", "public", null)
));
var app = builder.Build();
using var scope = app.Services.CreateScope();
using var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Database.Migrate();
var newRow = new TestEntity {
First = "foo",
Second = "bar",
};
db.TestEntities.Add(newRow);
db.SaveChanges();
var tuple = db.Database.SqlQueryRaw<(string, string)>(@"
select row(""First"", ""Second"") as ""Value""
from ""TestEntities""").ToList();
}
}
Activity
roji commentedon Feb 28, 2025
Confirmed.
Cleaned-up repro
Preserve ConfigureDataSource() callback when applying other context o…
roji commentedon Mar 1, 2025
Thanks for the issue and repro @rudism, I've found the problem and am pushing a fix for 9.0.4
Preserve ConfigureDataSource() callback when applying other context o…
Preserve ConfigureDataSource() callback when applying other context o…
sguryev commentedon Jul 21, 2025
In case anybody will searching for
EnableDynamicJson()doesn't work orMigrationsAssembly()resetsConfigureDataSource(). Same issue here