Contains over an object[]/List<object> parameter against a column with an element value converter throws at query compilation:
System.ArgumentException: Expression of type 'System.Object' cannot be used for parameter of type 'System.Int32' (Parameter 'arg0')
at System.Linq.Expressions.Expression.Invoke(Expression expression, Expression arg0)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.ValueConversion.NpgsqlArrayConverter`3.ArrayConversionExpression[TInput,TOutput,TConcreteOutput](LambdaExpression elementConversionExpression)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.ValueConversion.NpgsqlArrayConverter`3..ctor(ValueConverter elementConverter)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Constructor(...)
(TargetInvocationException wrapping the ArgumentException when the converter is constructed reflectively.)
Repro
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
var ids = new object[] { 1, 2 }; // also List<object> { 1, 2 }
using var ctx = new Ctx();
ctx.Blogs.Where(b => ids.Contains(b.Id)).ToQueryString();
sealed class Blog { public int Id { get; set; } }
sealed class Ctx : DbContext
{
public DbSet<Blog> Blogs => Set<Blog>();
protected override void OnConfiguring(DbContextOptionsBuilder b)
=> b.UseNpgsql("Host=localhost;Database=x;Username=u");
protected override void OnModelCreating(ModelBuilder b)
=> b.Entity<Blog>().Property(x => x.Id)
.HasConversion(new ValueConverter<int, long>(v => v, v => checked((int)v)))
.HasColumnType("bigint");
}
Results with 10.0.3:
object[] TargetInvocationException: ArgumentException: Expression of type 'System.Object' cannot be used for parameter of type 'System.Int32' (Parameter 'arg0')
List<object> TargetInvocationException: ArgumentException: ...
int[] OK
List<int> OK
So only collections whose element type (object) differs from the converter's model type (int) fail; typed collections work.
Analysis
NpgsqlArrayTypeMapping.CreateParameters sees the element mapping's converter (int → long) and builds:
NpgsqlArrayConverter<object[], object[], long[]>(elementConverter: ValueConverter<int, long>)
NpgsqlArrayConverter.ArrayConversionExpression then does, in the indexer/for-loop path:
Invoke(elementConversionExpression, indexer(counter))
where indexer(counter) has the collection's element type (object), while the lambda's parameter is the converter's model type (int) — so Expression.Invoke rejects it. The nullable guard above only handles Nullable<T> input/output element types; the object (non-nullable, non-matching) case falls through. It would need to Convert/Unbox the element to elementConversionExpression.Parameters[0].Type before invoking (and the model-type validation in the ctor would need to tolerate the wider collection element type).
This looks like the same family as #2189 / #3050, but for an element type that is wider than the converter's model type rather than nullable.
Environment
Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 (also reproduces on 9.x per the closely-related reports)
- EF Core 10.0.12,
net10.0
Workaround: use a typed collection (int[] / List<int>).
(Found while building an EF Core provider for Aurora DSQL, which widens int identity keys to bigint with exactly this converter; the four failing tests are upstream of that provider.)
Containsover anobject[]/List<object>parameter against a column with an element value converter throws at query compilation:(TargetInvocationException wrapping the ArgumentException when the converter is constructed reflectively.)
Repro
Results with 10.0.3:
So only collections whose element type (
object) differs from the converter's model type (int) fail; typed collections work.Analysis
NpgsqlArrayTypeMapping.CreateParameterssees the element mapping's converter (int → long) and builds:NpgsqlArrayConverter.ArrayConversionExpressionthen does, in the indexer/for-loop path:where
indexer(counter)has the collection's element type (object), while the lambda's parameter is the converter's model type (int) — soExpression.Invokerejects it. The nullable guard above only handlesNullable<T>input/output element types; theobject(non-nullable, non-matching) case falls through. It would need toConvert/Unboxthe element toelementConversionExpression.Parameters[0].Typebefore invoking (and the model-type validation in the ctor would need to tolerate the wider collection element type).This looks like the same family as #2189 / #3050, but for an element type that is wider than the converter's model type rather than nullable.
Environment
Npgsql.EntityFrameworkCore.PostgreSQL10.0.3 (also reproduces on 9.x per the closely-related reports)net10.0Workaround: use a typed collection (
int[]/List<int>).(Found while building an EF Core provider for Aurora DSQL, which widens
intidentity keys tobigintwith exactly this converter; the four failing tests are upstream of that provider.)