Skip to content

Enum with custom "upper-case" names are treated as int for searching with "contains" #3433

@Zwergenpunk

Description

@Zwergenpunk

Hey,

I found problem with the 9.x release.

We have a "PascalCase" naming convention for tables and enums. (yeah probably not good, but we have it)
When i set a cusom name that has no upper case letter it works.

When I search for models with some enums:

List<MyEnum> listOfEnums = [MyEnum.value1];
var result = await context.Set<MyEntity>().AsQueryable().Where(item => listOfEnums.Contains(item.MyEnum)).ToListAsync();

I receive an error:

Unhandled exception. Npgsql.PostgresException (0x80004005): 42883: Operator existiert nicht: doller_test."MyEnum" = integer

POSITION: 89
   at Npgsql.Internal.NpgsqlConnector.ReadMessageLong(Boolean async, DataRowLoadingMode dataRowLoadingMode, Boolean readingNotifications, Boolean isReadingPrependedMessage)
   at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource<TResult>.GetResult(Int16 token)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlDataReader.NextResult(Boolean async, Boolean isConsuming, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteReader(Boolean async, CommandBehavior behavior, CancellationToken cancellationToken)
   at Npgsql.NpgsqlCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in C:\Users\MicFiller\Documents\Test\MariaDbTest\PostgresTest\Program.cs:line 51
   at Program.<Main>(String[] args)
  Exception data:
    Severity: FEHLER
    SqlState: 42883
    MessageText: Operator existiert nicht: doller_test."MyEnum" = integer
    Hint: Kein Operator stimmt mit dem angegebenen Namen und den Argumenttypen überein. Sie müssen möglicherweise ausdrückliche Typumwandlungen hinzufügen.
    Position: 89
    File: d:\pginstaller_13.auto\postgres.windows-x64\src\backend\parser\parse_oper.c
    Line: 731
    Routine: op_error

The problem is that the generated sql somehow assumes that the parameter is an int:

      Failed executing DbCommand (72ms) [Parameters=[@__listOfEnums_0={ '0' } (DbType = Object)], CommandType='Text', CommandTimeout='30']
      SELECT m."Id", m."MyEnum", m."Name"
      FROM doller_test."MyEntity" AS m
      WHERE m."MyEnum" = ANY (@__listOfEnums_0)

__listOfEnums_0 should be "value1"

Setup to reproduce:

public class MyDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(
            "Host=localhost;Database=postgres;Username=postgres;Password=postgres",
            option => { RegisterEnum<MyEnum>(option); });
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("doller_test");

        modelBuilder.Entity<MyEntity>(_ => { });

        base.OnModelCreating(modelBuilder);
    }

    private static void RegisterEnum<TEnum>(NpgsqlDbContextOptionsBuilder optionsBuilder)
        where TEnum : struct, Enum
    {
        // works
        // optionsBuilder.MapEnum<TEnum>("enum", schemaName: "doller_test");

        // does not work
        optionsBuilder.MapEnum<TEnum>(typeof(TEnum).Name, schemaName: "doller_test");
    }
}

public class MyEntity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public MyEnum MyEnum { get; set; }
}

public enum MyEnum
{
    value1 = 0,
    value2 = 1
}

// Program.cs
var context = new MyDbContext();

await context.Database.MigrateAsync();

var postgresConnection = (NpgsqlConnection)context.Database.GetDbConnection();
if (postgresConnection.State != ConnectionState.Open)
{
    postgresConnection.Open();
}

postgresConnection.ReloadTypes(); 

List<MyEnum> listOfEnums = [MyEnum.value1];
var result = await context.Set<MyEntity>().AsQueryable().Where(item => listOfEnums.Contains(item.MyEnum)).ToListAsync();

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions