linq2db / linq2db.EntityFrameworkCore

Bring power of Linq To DB to Entity Framework Core projects
MIT License
462 stars 38 forks source link

enum doesn't work with npgsql on bulkcopy #59

Closed 0Lucifer0 closed 1 year ago

0Lucifer0 commented 4 years ago

when trying to bulk copy a list of entities containing Enum parameters (that are represented by enum in postgres) i'm getting this message. (here for the enum currency)

Npgsql.PostgresException (0x80004005): 42804: column "currency" is of type currency but expression is of type integer
   at void Npgsql.NpgsqlConnector+<>c__DisplayClass160_0+<<DoReadMessage>g__ReadMessageLong|0>d.MoveNext()
   at void Npgsql.NpgsqlConnector+<>c__DisplayClass160_0+<<DoReadMessage>g__ReadMessageLong|0>d.MoveNext()
   at async Task<bool> Npgsql.NpgsqlDataReader.NextResult(bool async, bool isConsuming)
   at bool Npgsql.NpgsqlDataReader.NextResult()
   at async ValueTask<NpgsqlDataReader> Npgsql.NpgsqlCommand.ExecuteReaderAsync(CommandBehavior behavior, bool async, CancellationToken cancellationToken)
   at async Task<int> Npgsql.NpgsqlCommand.ExecuteNonQuery(bool async, CancellationToken cancellationToken)
   at int Npgsql.NpgsqlCommand.ExecuteNonQuery()
   at int LinqToDB.Data.DbCommandProcessor.DbCommandProcessorExtensions.ExecuteNonQueryExt(IDbCommand cmd)
   at int LinqToDB.Data.DataConnection.ExecuteNonQuery(IDbCommand command)
   at int LinqToDB.Data.DataConnection.ExecuteNonQuery()
   at int LinqToDB.Data.CommandInfo.Execute()
   at int LinqToDB.Data.DataConnectionExtensions.Execute(DataConnection connection, string sql, DataParameter[] parameters)
   at bool LinqToDB.DataProvider.MultipleRowsHelper.Execute()
   at BulkCopyRowsCopied LinqToDB.DataProvider.BasicBulkCopy.MultipleRowsCopy1(MultipleRowsHelper helper, IEnumerable source)
   at BulkCopyRowsCopied LinqToDB.DataProvider.BasicBulkCopy.MultipleRowsCopy1<T>(ITable<T> table, BulkCopyOptions options, IEnumerable<T> source)
   at BulkCopyRowsCopied LinqToDB.DataProvider.PostgreSQL.PostgreSQLBulkCopy.MultipleRowsCopy<T>(ITable<T> table, BulkCopyOptions options, IEnumerable<T> source)
   at BulkCopyRowsCopied LinqToDB.DataProvider.BasicBulkCopy.BulkCopy<T>(BulkCopyType bulkCopyType, ITable<T> table, BulkCopyOptions options, IEnumerable<T> source)
   at BulkCopyRowsCopied LinqToDB.DataProvider.PostgreSQL.PostgreSQLDataProvider.BulkCopy<T>(ITable<T> table, BulkCopyOptions options, IEnumerable<T> source)
   at BulkCopyRowsCopied LinqToDB.Data.DataConnectionExtensions.BulkCopy<T>(DataConnection dataConnection, BulkCopyOptions options, IEnumerable<T> source)
   at BulkCopyRowsCopied LinqToDB.EntityFrameworkCore.LinqToDBForEFTools.BulkCopy<T>(DbContext context, BulkCopyOptions options, IEnumerable<T> source)
  Exception data:
    Severity: ERROR
    SqlState: 42804
    MessageText: column "currency" is of type currency but expression is of type integer
    Hint: You will need to rewrite or cast the expression.
    Position: 832
    File: parse_target.c
    Line: 591
    Routine: transformAssignedExpr
sdanyliv commented 4 years ago

It is more provider specific problem and i need more details.

0Lucifer0 commented 4 years ago

Planning to build a small proof project this weekend so it will help to answer all those questions 😀

sdanyliv commented 4 years ago

It will be very helpful and i'll prepare fix for that quickly because i don't know why conversions in this case was broken. Anyway i think workaround can be simple if you define converter Something like that:

MappingSchema.Default.SetConverter<currency, int>(c => (int)currency)
erwan-joly commented 4 years ago

here an example : https://github.com/erwan-joly/BulkCopyProofOfIssue tried MappingSchema.Default.SetConverter<Currency, int>(c => (int)c); and MappingSchema.Default.SetConverter<Currency, string>(c => nameof(c)); didn't change anything

sdanyliv commented 4 years ago

Conversion is simple:

 MappingSchema.Default.SetConverter<Currency, DataParameter>(c =>
                new DataParameter("", c.ToString().ToLower())
            );
erwan-joly commented 4 years ago

seems to works fine with a converter :) just had to make it to snake case instead of lower to work with all my enums

garkushin commented 3 years ago

for those who are still looking for how to do it, I got it like this.

public enum PriorityLevel {...}
public class DataContext : DbContext 
{
...
        static DataContext()
        {
            var nameTranslator = NpgsqlConnection.GlobalTypeMapper.DefaultNameTranslator;

            NpgsqlConnection.GlobalTypeMapper.MapEnum<PriorityLevel>();

            MappingSchema.Default.SetDefaultFromEnumType(typeof(EdgeType), typeof(string));
            MappingSchema.Default.SetConverter<EdgeType, string>(c => nameTranslator.TranslateMemberName(c.ToString()));
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.HasPostgresEnum<PriorityLevel>();
        }
...
}
MaceWindu commented 1 year ago

Closing as it looks like all questions resolved.

Some extra notes: