Skip to content

MapEnum interferes with previous ConfigureDataSource call #3478

@rudism

Description

@rudism

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();
  }
}

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions