Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Regression when using ImmutableArray's Contains, while Array's Contains method still works. #35102

Open
fschwiet opened this issue Nov 14, 2024 · 1 comment

Comments

@fschwiet
Copy link

File a bug

I ran into a regression going from 8.0.1 to 9.0.0 using the Sqlite provider. I have some code which is checking if the string value of a column (column name Value) is one of a number of values (using dbKeys.Contains). dbKeys was an ImmutableArray, I found if I used an Array instead it start to work. But I don't see any reason it shouldn't keep working with an ImmutableArray.

Include your code

The following code using ToImmutableArray stopped working when going from 8.0.1 to 9.0.0:

		var dbKeys = dbTags.Select(t => t.Value).Distinct().ToImmutableArray();

		var existingEntities = await context
			.Tags.Where(t => dbKeys.Contains(t.Value))
			.ToArrayAsync(cancellationToken);

But it works if I use ToArray():

		var dbKeys = dbTags.Select(t => t.Value).Distinct().ToArray();

		var existingEntities = await context
			.Tags.Where(t => dbKeys.Contains(t.Value))
			.ToArrayAsync(cancellationToken);

Include stack traces

Unhandled exception. System.ArgumentException: Expression of type 'System.Collections.Immutable.ImmutableArray`1[System.String]' cannot be used for parameter of type 'System.Collecti
ons.Generic.IEnumerable`1[System.String]' of method 'System.Linq.IQueryable`1[System.String] AsQueryable[String](System.Collections.Generic.IEnumerable`1[System.String])' (Parameter 'arg0')
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.TryConvertCollectionContainsToQueryableContains(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryableMethodNormalizingExpressionVisitor.Normalize(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.NormalizeQueryableMethod(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.RelationalQueryTranslationPreprocessor.NormalizeQueryableMethod(Expression expression)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutorExpression[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass11_0`1.<ExecuteCore>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteCore[TResult](Expression query, Boolean async, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToArrayAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)

Include provider and version information

EF Core version: regressed with version 9.0.0, worked for version 8.0.1
Database provider: Microsoft.EntityFrameworkCore.Sqlite regressed with version 9.0.0, worked for version 8.0.1
Target framework: regression seen with net9.0, worked with net8.0
Operating system: Windows 11 Home
IDE: JetBrains Rider 2024.2.5

@roji
Copy link
Member

roji commented Nov 14, 2024

Confirmed regression from 8.0 to 9.0; not specific to SQLite.

await using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

ImmutableArray<string> tags = ["foo", "bar"];
// List<string> tags = ["foo", "bar"]; // This works

var existingEntities = await context
    .Tags.Where(t => tags.Contains(t.Value))
    .ToArrayAsync();

public class BlogContext : DbContext
{
    public DbSet<Tag> Tags { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer("Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0;Encrypt=false")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
}

public class Tag
{
    public int Id { get; set; }
    public string Value { get; set; }
}

@cincuranet assigning to you as you worked in this area in 9.0, IIRC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants