Skip to content

Allow inline primitive collections with parameters, translating to VALUES #30732

@roji

Description

@roji

In the following query:

public virtual Task Inline_collection_Contains_with_all_parameters(bool async)
{
    var (i, j) = (2, 999);

    return AssertQuery(
        async,
        ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new[] { i, j }.Contains(c.Id)),
        entryCount: 1);
}

ParameterExtractingEV client-evaluates the NewArrayExpression (since it contains no server-correlated data), and transforms the entire thing into a single parameter. This causes us to send the following SQL on SQL Server:

@__p_0='[2,999]' (Size = 4000)

SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE EXISTS (
    SELECT 1
    FROM OpenJson(@__p_0) AS [p0] -- Not good
    WHERE CAST([p0].[value] AS int) = [p].[Id])

Ideally, this LINQ query would be translated to an IN expression with two parameters instead:

WHERE [p.Id] IN (@i, @j)

This would reproduce the constant array in the original LINQ query, and would give the database the opportunity to optimize for the fact that there are two parameters; it would very likely run faster than the generic OpenJson version, which has the same SQL regardless of the number of parameters.

The main problem here is the change to refrain client-evaluating the NewArrayExpression even though it contains no database-correlated things. Allowing arbitrary expressions in the inline expression (not just constants and parameters) is tracked by #30734.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions