Open samuel-utbult-oborgen opened 9 months ago
Regardless of the actual issue above, be aware that this code executes a separate query for each value in the range, doing many sequential roundtrips, which is slow. Instead, consider just creating an array of your DateTimes and doing a single query with Contains over that array.
Regardless of the actual issue above, be aware that this code executes a separate query for each value in the range, doing many sequential roundtrips, which is slow. Instead, consider just creating an array of your DateTimes and doing a single query with Contains over that array.
The provided example is the most simple one I could come up with that showcases the bug. The production code where this bug was found is considerably more complicated and there is an actual reason why such an SQL parameter is useful in that case.
Debug query output for the first case, which ultimately returns one result. (Note that the database contains no data.)
dbug: 2/1/2024 10:48:54.969 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query)
Compiling query expression:
'DbSet<TestEntity>().FromSql(SELECT @date AS Date, __p_0)
.Union(DbSet<TestEntity>().FromSql(SELECT @date AS Date, __p_1)
)'
dbug: 2/1/2024 10:48:55.191 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query)
Generated query execution expression:
'queryContext => new SingleQueryingEnumerable<TestEntity>(
(RelationalQueryContext)queryContext,
RelationalCommandCache.QueryExpression(
Projection Mapping:
EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: TestEntity.Date (DateOnly) Required, 0] }
SELECT t0.Date
FROM (
SELECT t.Date
FROM SELECT @date AS Date
UNION
SELECT t1.Date
FROM SELECT @date AS Date
) AS t0
),
null,
Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, TestEntity>,
SomeDbContext,
False,
False,
True
)'
info: 2/1/2024 10:48:55.254 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (32ms) [Parameters=[date='01/01/2024' (DbType = Date)], CommandType='Text', CommandTimeout='30']
SELECT [t].[Date]
FROM (
SELECT @date AS Date
) AS [t]
UNION
SELECT [t1].[Date]
FROM (
SELECT @date AS Date
) AS [t1]
Debug query output for the second case, which ultimately returns two results. (Note that the database contains no data here either.)
dbug: 2/1/2024 10:44:51.612 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query)
Compiling query expression:
'DbSet<TestEntity>().FromSql(SELECT {0} AS Date, __p_0)
.Union(DbSet<TestEntity>().FromSql(SELECT {0} AS Date, __p_1)
)'
dbug: 2/1/2024 10:44:51.821 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query)
Generated query execution expression:
'queryContext => new SingleQueryingEnumerable<TestEntity>(
(RelationalQueryContext)queryContext,
RelationalCommandCache.QueryExpression(
Projection Mapping:
EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: TestEntity.Date (DateOnly) Required, 0] }
SELECT t0.Date
FROM (
SELECT t.Date
FROM SELECT {0} AS Date
UNION
SELECT t1.Date
FROM SELECT {0} AS Date
) AS t0
),
null,
Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, TestEntity>,
SomeDbContext,
False,
False,
True
)'
info: 2/1/2024 10:44:51.887 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (30ms) [Parameters=[p0='01/01/2024' (DbType = Date), p1='01/02/2024' (DbType = Date)], CommandType='Text', CommandTimeout='30']
SELECT [t].[Date]
FROM (
SELECT @p0 AS Date
) AS [t]
UNION
SELECT [t1].[Date]
FROM (
SELECT @p1 AS Date
) AS [t1]
Debug query output for the first case, which ultimately returns one result. (Note that the database contains no data.)
dbug: 2/1/2024 10:48:54.969 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query) Compiling query expression: 'DbSet<TestEntity>().FromSql(SELECT @date AS Date, __p_0) .Union(DbSet<TestEntity>().FromSql(SELECT @date AS Date, __p_1) )' dbug: 2/1/2024 10:48:55.191 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query) Generated query execution expression: 'queryContext => new SingleQueryingEnumerable<TestEntity>( (RelationalQueryContext)queryContext, RelationalCommandCache.QueryExpression( Projection Mapping: EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: TestEntity.Date (DateOnly) Required, 0] } SELECT t0.Date FROM ( SELECT t.Date FROM SELECT @date AS Date UNION SELECT t1.Date FROM SELECT @date AS Date ) AS t0 ), null, Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, TestEntity>, SomeDbContext, False, False, True )' info: 2/1/2024 10:48:55.254 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) Executed DbCommand (32ms) [Parameters=[date='01/01/2024' (DbType = Date)], CommandType='Text', CommandTimeout='30'] SELECT [t].[Date] FROM ( SELECT @date AS Date ) AS [t] UNION SELECT [t1].[Date] FROM ( SELECT @date AS Date ) AS [t1]
Debug query output for the second case, which ultimately returns two results. (Note that the database contains no data here either.)
dbug: 2/1/2024 10:44:51.612 CoreEventId.QueryCompilationStarting[10111] (Microsoft.EntityFrameworkCore.Query) Compiling query expression: 'DbSet<TestEntity>().FromSql(SELECT {0} AS Date, __p_0) .Union(DbSet<TestEntity>().FromSql(SELECT {0} AS Date, __p_1) )' dbug: 2/1/2024 10:44:51.821 CoreEventId.QueryExecutionPlanned[10107] (Microsoft.EntityFrameworkCore.Query) Generated query execution expression: 'queryContext => new SingleQueryingEnumerable<TestEntity>( (RelationalQueryContext)queryContext, RelationalCommandCache.QueryExpression( Projection Mapping: EmptyProjectionMember -> Dictionary<IProperty, int> { [Property: TestEntity.Date (DateOnly) Required, 0] } SELECT t0.Date FROM ( SELECT t.Date FROM SELECT {0} AS Date UNION SELECT t1.Date FROM SELECT {0} AS Date ) AS t0 ), null, Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, TestEntity>, SomeDbContext, False, False, True )' info: 2/1/2024 10:44:51.887 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) Executed DbCommand (30ms) [Parameters=[p0='01/01/2024' (DbType = Date), p1='01/02/2024' (DbType = Date)], CommandType='Text', CommandTimeout='30'] SELECT [t].[Date] FROM ( SELECT @p0 AS Date ) AS [t] UNION SELECT [t1].[Date] FROM ( SELECT @p1 AS Date ) AS [t1]
A clear observation to make in the debug output is that one parameter date
exists in the first query but two parameters p0
and p1
exists in the second query. This conforms to my speculation above. EF Core knows how to separate between {0}
yet it does not know how to separate between @date
. I hope this provides enough information for the developers of EF Core to know how this bug could be fixed.
I wrote two manual SQL queries with parameters with the same names but with different values like this:
I expected to get two results with 2024-01-01 and 2024-01-02 but got only the result 2024-01-01. However, this works as expected:
I have created an example project showcasing the bug: https://github.com/samuel-utbult-oborgen/ef-core-bug I suspect this could be because the variable "date" is created for the combined query and its value is set to 2024-01-01 while the value 2024-01-02 is ignored. The parameters should reasonably be isolated between the queries as they are specified for each query respectively.
EF Core version: 8.0.1 Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer) Target framework: (e.g. .NET 8.0) Operating system: macOS Sonoma 14.2.1