-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
Bug description
This builds on the problem discovered in #37185. The solution there to go back to the "old" way with a single JSON parameter using EF.Parameter (or setting ParameterTranslationMode.Parameter) doesn't work if you have an IEnumerable with a nullable type as parameter.
We were hoping to use options.UseParameterizedCollectionMode(ParameterTranslationMode.Parameter) to go back to the old way for the whole project to get around #37185 to avoid going through and possibly updating every single query that uses a .Contains() to be sure.
This worked fine in .EFCore 9. So even if the other issue gets a fix this might cause further problems.
Your code
// PS: I love single file apps for this kind of thing
#:package Microsoft.EntityFrameworkCore.SqlServer@10.0.0
#:property PublishAot=false
using Microsoft.EntityFrameworkCore;
List<QueryObj> list = [];
for (int i = 0; i < 1500; i++)
{
list.Add(new QueryObj { OtherId = i.ToString() });
}
using var dbContext = new BloggingContext();
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var blogs = await dbContext.Set<Blog>()
.Where(b => EF.Parameter(list.Select(x => x.OtherId)).Contains(b.OtherId))
// Using the below line with ToList() works around the issue
//.Where(b => EF.Parameter(list.Select(x => x.OtherId).ToList()).Contains(b.OtherId))
.ToListAsync();
class BloggingContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=ParamBug;Trusted_Connection=True;")
.LogTo(Console.WriteLine);
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string? OtherId { get; set; }
}
public class QueryObj
{
public string? OtherId { get; set; }
}Stack traces
fail: 2025-11-19 09:03:33.217 CoreEventId.QueryIterationFailed[10100] (Microsoft.EntityFrameworkCore.Query)
An exception occurred while iterating over the results of a query for context type 'BloggingContext'.
System.Diagnostics.UnreachableException: Parameter 'Select' is not an IList.
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.TryMakeNonNullable(SelectExpression selectExpression, SelectExpression& rewrittenSelectExpression, Nullable`1& foundNull)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitIn(InExpression inExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlNullabilityProcessor.VisitIn(InExpression inExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression, Boolean visitProjection)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitExtension(Expression node)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlNullabilityProcessor.VisitExtension(Expression node)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Process(Expression queryExpression, ParametersCacheDecorator parametersDecorator)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlNullabilityProcessor.Process(Expression queryExpression, ParametersCacheDecorator parametersDecorator)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerParameterBasedSqlProcessor.ProcessSqlNullability(Expression selectExpression, ParametersCacheDecorator Decorator)
at Microsoft.EntityFrameworkCore.Query.RelationalParameterBasedSqlProcessor.Process(Expression queryExpression, ParametersCacheDecorator parametersDecorator)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerParameterBasedSqlProcessor.Process(Expression queryExpression, ParametersCacheDecorator parametersDecorator)
at Microsoft.EntityFrameworkCore.Query.RelationalParameterBasedSqlProcessor.Process(Expression queryExpression, Dictionary`2 parameters, Boolean& canCache)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalCommandCache.GetRelationalCommandTemplate(Dictionary`2 parameters)
at Microsoft.EntityFrameworkCore.Internal.RelationalCommandResolverExtensions.RentAndPopulateRelationalCommand(RelationalCommandResolver relationalCommandResolver, RelationalQueryContext queryContext)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
Verbose output
dbug: 2025-11-19 09:03:33.091 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query)
Compiling query expression:
'DbSet<Blog>()
.Where(b => EF.Parameter<IEnumerable<string>>(@Select)
.Contains(b.OtherId))'
dbug: 2025-11-19 09:03:33.201 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query)
Generated query execution expression:
'queryContext => SingleQueryingEnumerable.Create<Blog>(
relationalQueryContext: (RelationalQueryContext)queryContext,
relationalCommandResolver: parameters => [LIFTABLE Constant: RelationalCommandCache.QueryExpression(
Projection Mapping:
EmptyProjectionMember -> Dictionary<IPropertyBase, int> { [Property: Blog.BlogId (int) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: Blog.OtherId (string), 1] }
SELECT b.BlogId, b.OtherId
FROM Blogs AS b
WHERE b.OtherId IN (
SELECT s.value
FROM OPENJSON(@Select) WITH (value nvarchar(max) '') AS s)) | Resolver: c => new RelationalCommandCache(
c.Dependencies.MemoryCache,
c.RelationalDependencies.QuerySqlGeneratorFactory,
c.RelationalDependencies.RelationalParameterBasedSqlProcessorFactory,
Projection Mapping:
EmptyProjectionMember -> Dictionary<IPropertyBase, int> { [Property: Blog.BlogId (int) Required PK AfterSave:Throw ValueGenerated.OnAdd, 0], [Property: Blog.OtherId (string), 1] }
SELECT b.BlogId, b.OtherId
FROM Blogs AS b
WHERE b.OtherId IN (
SELECT s.value
FROM OPENJSON(@Select) WITH (value nvarchar(max) '') AS s),
False,
MultipleParameters
)].GetRelationalCommandTemplate(parameters),
readerColumns: null,
shaper: (queryContext, dataReader, resultContext, resultCoordinator) =>
{
Blog entity;
entity =
{
MaterializationContext materializationContext1;
IEntityType entityType1;
Blog instance1;
InternalEntityEntry entry1;
bool hasNullKey1;
materializationContext1 = new MaterializationContext(
[LIFTABLE Constant: ValueBuffer | Resolver: _ => (object)ValueBuffer.Empty],
queryContext.Context
);
instance1 = default(Blog);
entry1 = queryContext.TryGetEntry(
key: [LIFTABLE Constant: Key: Blog.BlogId PK | Resolver: c => c.Dependencies.Model.FindEntityType("Blog").FindPrimaryKey()],
keyValues: new object[]{ (object)dataReader.GetInt32(0) },
throwOnNullKey: True,
hasNullKey: hasNullKey1);
!(hasNullKey1) ? entry1 != default(InternalEntityEntry) ?
{
entityType1 = entry1.EntityType;
return instance1 = (Blog)entry1.Entity;
} :
{
ISnapshot shadowSnapshot1;
shadowSnapshot1 = [LIFTABLE Constant: Snapshot | Resolver: _ => Snapshot.Empty];
entityType1 = [LIFTABLE Constant: EntityType: Blog | Resolver: namelessParameter{0} => namelessParameter{0}.Dependencies.Model.FindEntityType("Blog")];
instance1 = switch (entityType1)
{
case [LIFTABLE Constant: EntityType: Blog | Resolver: namelessParameter{1} => namelessParameter{1}.Dependencies.Model.FindEntityType("Blog")]:
{
return
{
Blog instance;
instance = new Blog();
instance.<BlogId>k__BackingField = dataReader.GetInt32(0);
instance.<OtherId>k__BackingField = dataReader.IsDBNull(1) ? default(string) : dataReader.GetString(1);
(instance is IInjectableService) ? ((IInjectableService)instance).Injected(
context: materializationContext1.Context,
entity: instance,
queryTrackingBehavior: TrackAll,
structuralType: [LIFTABLE Constant: EntityType: Blog | Resolver: namelessParameter{2} => namelessParameter{2}.Dependencies.Model.FindEntityType("Blog")]) : default(void);
return instance;
}}
default:
default(Blog)
}
;
entry1 = entityType1 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
entityType: entityType1,
entity: instance1,
snapshot: shadowSnapshot1);
return instance1;
} : default(void);
return instance1;
};
return entity;
},
contextType: BloggingContext,
standAloneStateManager: False,
detailedErrorsEnabled: False,
threadSafetyChecksEnabled: True)'
EF Core version
10.0.0
Database provider
Microsoft.EntityFrameworkCore.SqlServer@
Target framework
.NET 10
Operating system
No response
IDE
No response