Skip to content

Incorrect COALESCE when using DefaultIfEmpty over nullable value types #37178

@neoGeneva

Description

@neoGeneva

Bug description

When joining to a subquery using the ".Where(...).DefaultIfEmpty() " syntax EF Core adds COALESCE(..., '0001-01-01T00:00:00.0000000') even though the value should remain null, and the value is out of range if the date type is datetime.

In the example code provided the output result is:

SELECT [p].[product_id], [p3].[first_time_stocked] AS [error], [p1].[first_time_stocked] AS [okay]
FROM [product] AS [p]
OUTER APPLY (
    SELECT COALESCE(MIN([p0].[date_arrived]), '0001-01-01T00:00:00.0000000') AS [first_time_stocked]
    FROM [product_stock_item] AS [p0]
    GROUP BY [p0].[product_id]
    HAVING [p0].[product_id] = [p].[product_id]
) AS [p1]
LEFT JOIN (
    SELECT [p2].[product_id], MIN([p2].[date_arrived]) AS [first_time_stocked]
    FROM [product_stock_item] AS [p2]
    GROUP BY [p2].[product_id]
) AS [p3] ON [p].[product_id] = [p3].[product_id]`

Your code

#nullable disable

using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore;

var options = new DbContextOptionsBuilder<BugDbContext>()
   .UseSqlServer()
   .Options;

using var _db = new BugDbContext(options);

var stockItems =
    from g in _db.product_stock_item
    group g by g.product_id into x
    select new
    {
        product_id = x.Key,
        first_time_stocked = x.Min(y => y.date_arrived)
    };

var items =
    from p in _db.product
    from stock_item_error in stockItems.Where(si => si.product_id == p.product_id).DefaultIfEmpty()
    join x in stockItems on p.product_id equals x.product_id into xg
    from stock_item_okay in xg.DefaultIfEmpty()
    select new
    {
        p.product_id,
        error = stock_item_okay.first_time_stocked,
        okay = stock_item_error.first_time_stocked
    };

Console.WriteLine(items.ToQueryString());

public class BugDbContext : DbContext
{
    public BugDbContext(DbContextOptions<BugDbContext> options)
        : base(options)
    { }

    public DbSet<product> product { get; set; }
    public DbSet<product_stock_item> product_stock_item { get; set; }
}

public class product
{
    [Key]
    public int product_id { get; set; }
}

public class product_stock_item
{
    [Key]
    public int product_stock_item_id { get; set; }
    public int product_id { get; set; }
    public DateTime? date_arrived { get; set; }
}

Stack traces

Microsoft.Data.SqlClient.SqlException: The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
   at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, SqlCommand command, Boolean callerHasConnectionLock, Boolean asyncClose)
   at Microsoft.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at Microsoft.Data.SqlClient.SqlDataReader.TryHasMoreRows(Boolean& moreRows)
   at Microsoft.Data.SqlClient.SqlDataReader.TryReadInternal(Boolean setTimeout, Boolean& more)
   at Microsoft.Data.SqlClient.SqlDataReader.ReadAsyncExecute(Task task, Object state)
   at Microsoft.Data.SqlClient.SqlDataReader.InvokeAsyncCall[T](SqlDataReaderBaseAsyncCallContext`1 context)
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
``

Verbose output


EF Core version

10.0.0

Database provider

Microsoft.EntityFrameworkCore.SqlServer

Target framework

.NET 10

Operating system

Windows 11

IDE

VS Code 1.106.0

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions