The translation pipeline assumes that SQL expressions are pure, which is not true in general (for example EF.Functions.Random()).
Under this assumption, it sometimes duplicates sub-expressions which can lead to inconsistent results, exceptions and (a minor issue, but in some cases still relevant) degraded performance.
See #32519 for a related issue that is specific to the translation performed by the SQL Server provider.
An example program that showcases the bug is:
using System;
using System.Data;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using var db = new BloggingContext();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
var urls = db.Blogs
.Select(x => (EF.Functions.Random() > 0.5 ? null : x.Url).Contains("5"))
.ToList();
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options
.LogTo(Console.WriteLine, Microsoft.Extensions.Logging.LogLevel.Information)
.UseSqlite($"Data Source=test.db");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
for (var i = 1; i < 100; i++) {
modelBuilder.Entity<Blog>().HasData(new Blog { BlogId = i, Url = $"url/{i}" });
}
}
}
public class Blog
{
public int BlogId { get; set; }
public required string Url { get; set; }
}
Exception
The program terminates with the following exception:
fail: 05/23/2024 04:25:52.836 CoreEventId.QueryIterationFailed[10100] (Microsoft.EntityFrameworkCore.Query)
An exception occurred while iterating over the results of a query for context type 'BloggingContext'.
System.InvalidOperationException: Nullable object must have a value.
at lambda_method19(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
Unhandled exception. System.InvalidOperationException: Nullable object must have a value.
at lambda_method19(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Program.<Main>$(String[] args)
because it is running the query
SELECT CASE
WHEN abs(random() / 9.2233720368547799E+18) > 0.5 THEN NULL
ELSE "b"."Url"
END IS NOT NULL AND instr(CASE
WHEN abs(random() / 9.2233720368547799E+18) > 0.5 THEN NULL
ELSE "b"."Url"
END, '5') > 0
FROM "Blogs" AS "b"
The two random() calls are evaluated independently, hence it is possible for this query to return NULL values (which IIUC the shaper/materializer rightly does not expect).
Include provider and version information
EF Core version: 8.0.5
Database provider: Microsoft.EntityFrameworkCore.Sqlite
Target framework: .NET 8.0
Operating system: Linux (/WSL)
IDE: Visual Studio Code 1.89.1
The translation pipeline assumes that SQL expressions are pure, which is not true in general (for example
EF.Functions.Random()).Under this assumption, it sometimes duplicates sub-expressions which can lead to inconsistent results, exceptions and (a minor issue, but in some cases still relevant) degraded performance.
See #32519 for a related issue that is specific to the translation performed by the SQL Server provider.
An example program that showcases the bug is:
Exception
The program terminates with the following exception:
because it is running the query
The two
random()calls are evaluated independently, hence it is possible for this query to returnNULLvalues (which IIUC the shaper/materializer rightly does not expect).Include provider and version information
EF Core version: 8.0.5
Database provider: Microsoft.EntityFrameworkCore.Sqlite
Target framework: .NET 8.0
Operating system: Linux (/WSL)
IDE: Visual Studio Code 1.89.1