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

Query: element access on a collection of primitives parameter throws bad exception when run on sql server that doesn't support JSON #33621

Open
maumar opened this issue Apr 27, 2024 · 1 comment

Comments

@maumar
Copy link
Contributor

maumar commented Apr 27, 2024

Scenario:

        string[] values = new[] { "one", "two", "three", "four" };

        return AssertQuery(
            async,
            ss => from e in ss.Set<PrimitiveCollectionsEntity>()
                  let value = e.Id > 0 && e.Id < 4 ? values[e.Id] : "zero"
                  select new { e.Id, value });

when ran in the "legacy" mode, we throw

Unable to cast object of type 'System.String[]' to type 'System.Linq.IQueryable`1[System.String]'.

Here is the shaper that we generate:

(queryContext, dataReader, resultContext, resultCoordinator) => 
{
    int? value1;
    bool? value2;
    IQueryable<string> value3;
    int? value4;
    value1 = (int?)dataReader.GetInt32(0);
    value2 = dataReader.IsDBNull(1) ? default(bool?) : (bool?)dataReader.GetBoolean(1);
    value3 = dataReader.IsDBNull(2) ? default(IQueryable<string>) : (IQueryable<string>)(IEnumerable<string>)new JsonCollectionOfReferencesReaderWriter<string[], string>(JsonStringReaderWriter.Instance).FromJsonString(
        json: (string)dataReader.GetFieldValue<object>(2), 
        existingObject: null);
    value4 = (int?)dataReader.GetInt32(0);
    return new { 
        Id = (int)value1, 
        value = value2 == True ? value3
            .ElementAt((int)value4) : "zero"
     };
}

and here is the sql:

exec sp_executesql N'SELECT [p].[Id], CASE
    WHEN [p].[Id] > 0 AND [p].[Id] < 4 THEN CAST(1 AS bit)
    ELSE CAST(0 AS bit)
END, @__values_0
FROM [PrimitiveCollectionsEntity] AS [p]',N'@__values_0 nvarchar(4000)',@__values_0=N'["one","two","three","four"]'

values parameter is array of strings, and we do read it as IEnumerable initially, but the ElementAt method that we use to get the element we need is on IQueryable, so we try convert to IQueryable and ultimately fail.

In preprocessing we normalize indexer over parameter array into:
[ParameterQueryRootExpression].AsQueryable().ElementAt(...), then nav expansion gobbles up AsQueryable call (because in normal mode this all will get translated, so AsQueryable is not necessary). In legacy mode we end up client-evaling the whole thing, but we don't restore AsQueryable or apply original indexer or Enumerable.ElementAt.

@maumar
Copy link
Contributor Author

maumar commented Apr 27, 2024

FYI @roji
This might go away if/when we get rid of nav expansion, also low priority bug, only happens in legacy mode and test case seems uncommon

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

2 participants