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

DbFunction definition returning nullable value type causes incorrect SQL to get generated #29585

Closed
andreikarkkanen opened this issue Nov 16, 2022 · 7 comments · Fixed by #29588
Assignees
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@andreikarkkanen
Copy link

I have AsQueryable extension method that solves SQL Server cache pollution problem using STRING_SPLIT approach. The following invocation has different results depending of TargetFramework

image

For EFCore 6.0.11 it produces
exec sp_executesql N'SELECT [c].[CustomerId] FROM [Customers] AS [c] WHERE EXISTS ( SELECT 1 FROM STRING_SPLIT(@__source_1, @__separator_2) AS [s] WHERE CONVERT(UNIQUEIDENTIFIER, [s].[Value]) = [c].[CustomerId])',N'@__source_1 nvarchar(4000),@__separator_2 nvarchar(4000)',@__source_1=N'009cf046-a859-4849-8de7-9854bd00f7de,b38c95f2-e5aa-4926-a315-a23ce6b6578c',@__separator_2=N','

and for EFCore 7.0.0 it has
SELECT [c].[CustomerId] FROM [Customers] AS [c] WHERE 0 = 1

The full code of the program is in the attachment

ConsoleApp1.zip

@roji
Copy link
Member

roji commented Nov 16, 2022

The problem is with the DbFunction configuration of the ToGuid function:

modelBuilder.HasDbFunction(type.GetMethod(nameof(ToGuid))!)
    .HasTranslation(t => new SqlFunctionExpression(
        functionName: "CONVERT",
        arguments: new[] { new SqlFragmentExpression("UNIQUEIDENTIFIER"), t[0] },
        nullable: true,
        argumentsPropagateNullability: new[] { false, true },
        typeof(Guid?), <-- Change to Guid
        typeMapping: null))
    .IsBuiltIn();

Although the function may return null, in EF's SQL tree, the CLR types are never nullable (since nullability is handled differently on the SQL side). Changing the function definition to return Guid instead of Guid? generates the correct SQL.

Note that we have a debug assertion against this, but of course it doesn't occur in Release. We should add model validation against DbFunctions with nullable value return types.

For reference, the reason this started failing in 7.0 is f1bcf13, which changed the Contains(foo) to be rewritten to Any(x => object.Equals(x, foo), as opposed to the previous Any(x => x == foo). The 6.0 equality gets translated in RelationalSqlTranslatingExpressionVisitor.VisitBinary, which doesn't care about the nullability of the two sides. But the 7.0 object.Equals gets to EqualsTranslator, which returns false since the left and right side have different nullability.

@roji roji changed the title EFCore 7.0 causes WHERE 0 = 1 generating DbFunction definition returning nullable value type causes incorrect SQL to get generated Nov 16, 2022
roji added a commit to roji/efcore that referenced this issue Nov 16, 2022
@andreikarkkanen
Copy link
Author

Thank you so much, I wouldn't have figured it out on my own.

@roji roji added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. area-query labels Nov 16, 2022
@roji roji added this to the 8.0.0 milestone Nov 16, 2022
@ajcvickers ajcvickers modified the milestones: 8.0.0, 8.0.0-preview1 Jan 29, 2023
@ajcvickers ajcvickers modified the milestones: 8.0.0-preview1, 8.0.0 Nov 14, 2023
@RichardD2
Copy link

This is now throwing an exception in EF Core 8.0 which wasn't present in EF Core 7. However, it doesn't seem to be documented as a "breaking change", which it technically is.

@ajcvickers
Copy link
Member

@RichardD2 When something was not working correctly in a previous version and we fix that, as was the case here, we generally don't consider that a breaking change since your code wasn't working correctly before.

@RichardD2
Copy link

@ajcvickers In my case, I was using TRY_PARSE, and it was working in EF7 - at least in my tests.

It was even mostly working for other people:
NBS.EntityFrameworkCore.SqlServer.TryParse

The only issue reported with EF7 was a missing AS in the generated query for a very complex projection, which I have yet to be able to reproduce.

Changing that to throw an exception at runtime certainly seems like a breaking change to me. :)

RichardD2 added a commit to RichardD2/NBS.EntityFrameworkCore.SqlServer.TryParse that referenced this issue Nov 17, 2023
@ajcvickers ajcvickers removed this from the 8.0.0 milestone Nov 20, 2023
@ajcvickers ajcvickers reopened this Nov 20, 2023
@ajcvickers
Copy link
Member

Note for triage: make sure we are not being overly aggressive validating this.

@ajcvickers
Copy link
Member

@RichardD2 I checked with the team, and while this may have been working for you in some cases, that was accidental, and the code should be changed going forward.

@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Dec 5, 2023
@ajcvickers ajcvickers added this to the 8.0.0 milestone Dec 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants