efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Nullable object must have a value thrown for a query with DefaultIfEmpty()

Open Luigi6821 opened this issue 1 year ago • 22 comments

Hi, I am getting error "Nullable object must have a value" using the following scenario:

var categories = from c in dbContext.Set<AMOS_ADDRESSCATEGORY>().AsNoTracking()
                                 select new
                                 {
                                     Id = c.ADDRESSCATEGORYID,
                                     Name = c.DESCRIPTION
                                 };

var addresses = (from address in dbContext.Set<AMOS_ADDRESS>().AsNoTracking()
                                 join c in categories on address.ADDRESSCATEGORYID equals c.Id into aC
                                 from category in aC.DefaultIfEmpty()
                                 select new
                                 {
                                     Id = address.ADDRESSID,
                                     Category = category
                                 }).ToArray();

The above scenarion works pefectly using EF Standard and works in EF Core if I change to:

var addresses = (from address in dbContext.Set<AMOS_ADDRESS>().AsNoTracking()
                                 **join c in dbContext.Set<AMOS_ADDRESSCATEGORY>().AsNoTracking()** 
                                on address.ADDRESSCATEGORYID equals c.ADDRESSCATEGORYID into aC
                                 from category in aC.DefaultIfEmpty()
                                 select new
                                 {
                                     Id = address.ADDRESSID,
                                     Category = category
                                 }).ToArray();

Can you help me please? Thanks in advance Luigi

EF Core version: 8.0.0-preview.4.23259.3 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: NET 6.0 Operating system: IDE: Visual Studio 2022 17.4

Luigi6821 avatar May 17 '23 16:05 Luigi6821

This issue is lacking enough information for us to be able to fully understand what is happening. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

ajcvickers avatar May 17 '23 19:05 ajcvickers

Hi @ajcvickers Please find attached a project for reproducing issue.

ReproducibleNullObject.zip

Luigi6821 avatar May 17 '23 23:05 Luigi6821

Note for triage: we don't handle assigning null to the anonymous type after calling DefaultIfEmpty, but it seems like this should work, and it works in LINQ-to-Objects.

ajcvickers avatar May 18 '23 14:05 ajcvickers

Hi @ajcvickers I believe it has same issue with non anonymous type. Example:

var categories = from c in dbContext.Set<AMOS_ADDRESSCATEGORY>().AsNoTracking()
                         select new Category
                         {
                             Id = c.ADDRESSCATEGORYID,
                             Name = c.DESCRIPTION
                         };

 var addresses = (from address in dbContext.Set<AMOS_ADDRESS>().AsNoTracking()
                                 join c in categories
                                on address.ADDRESSCATEGORYID equals c.Id into aC
                                 from category in aC.DefaultIfEmpty()
                                 select new
                                 {
                                     Id = address.ADDRESSID,
                                     Category = category
                                 }).ToArray();

Luigi6821 avatar May 19 '23 07:05 Luigi6821

Hi, Has been solved in version 8 ? I tested and seems still present.. Thanks in advance

Luigi6821 avatar Nov 15 '23 11:11 Luigi6821

This issue is in the Backlog milestone. This means that it is not fixed in EF Core 8.0. We will re-assess the backlog and consider this item in the upcoming planning. However, keep in mind that there are many other high priority features with which it will be competing for resources. Make sure to vote (👍) for this issue if it is important to you.

ajcvickers avatar Nov 15 '23 11:11 ajcvickers

Hi, thanks for prompt reply. I believe that this issue should be with high priority because fails on a simple left join operation. Anyway thanks for suggestion

Luigi6821 avatar Nov 15 '23 11:11 Luigi6821

I have the same exception using a simple group by statement, I think the root cause might be the same. You can run this in roslyn pad to reproduce.

#r "nuget: Microsoft.EntityFrameworkCore.Sqlite,8.0.0"
#r "nuget: Microsoft.EntityFrameworkCore,8.0.0"

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

await using var ctx = new Context();
await ctx.Database.EnsureDeletedAsync();
await ctx.Database.EnsureCreatedAsync();

ctx.Steps.Add(new Step
{
    Id = 5
});
await ctx.SaveChangesAsync();

await ctx.Steps
    .GroupBy(e => new
    {
        e.Planned
    }).ToListAsync();

public class Context : DbContext
{
    public DbSet<Step> Steps { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlite(Environment.ExpandEnvironmentVariables("DataSource=%USERPROFILE%/Desktop/test_db.sqlite"))
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();
}

public class Step
{
    [Key]
    public int Id { get; set; }
    public DateTime? Planned { get; set; }
}

UliPlabst avatar Dec 20 '23 10:12 UliPlabst

Hi, any news on the above? Thanks in advance

Luigi6821 avatar Feb 14 '24 15:02 Luigi6821

This issue is in the Backlog milestone. This means that it is not planned for the next release (EF Core 8.0). We will re-assess the backlog following the this release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources. Make sure to vote (👍) for this issue if it is important to you.

roji avatar Feb 14 '24 17:02 roji

I will "vote" it for sure but let me say that this issue is really a bug for anyone want to outer join tables.

Regards Luigi

Luigi6821 avatar Feb 15 '24 09:02 Luigi6821

We're also experienced the issue on a piece of code very similar to @Luigi6821 , I'll keep an eye on this issue. Can anybody explain how can we vote this one up?

adrius avatar Feb 23 '24 14:02 adrius

Same issue here, how can we vote?

geraparra avatar Feb 27 '24 14:02 geraparra

@geraparra vote (thumbs up 👍) in the top comment of the issue.

roji avatar Feb 27 '24 14:02 roji

Hi, I am testing version 9 preview 9.0.0-preview.3.24172.4 and still the error is present. Honestly I am buffled on how this "pillar" functionality is not working and how any other can use EF core without having it fixed.

Luigi6821 avatar Jun 29 '24 08:06 Luigi6821

Yes, this issue is still in the backlog milestone - we haven't yet fixed it. Ultimately, it has only 10 upvotes, meaning that very few users run into it, and therefore it's not at the top of our priority list.

roji avatar Jun 29 '24 08:06 roji

Yes, this issue is still in the backlog milestone - we haven't yet fixed it. Ultimately, it has only 10 upvotes, meaning that very few users run into it, and therefore it's not at the top of our priority list.

I can't believe in users that do not use outer JOIN. There is some workaround?

Luigi6821 avatar Jun 29 '24 10:06 Luigi6821

This issue has been present since EF Core 5+until now

This is a breaking change when upgrading from EFCore 3.1 to EFCore 5, but it is not mentioned in the Microsoft documentation, causing widespread errors in our production environment.

@ajcvickers @roji Can you fix it ?

shaofing avatar Jul 16 '24 10:07 shaofing

        static void Main(string[] args)
        {
            //Table AppointmentOrder only has 1 record, while table AppointmentDetail has no data.
            var connectionString = $"Data Source=(localdb)\\ProjectModels;Initial Catalog=eftest;Integrated Security=True;Connect Timeout=30;Encrypt=False;Trust Server Certificate=False;Application Intent=ReadWrite;Multi Subnet Failover=False";
            var builder = new DbContextOptionsBuilder<SqliteContext>();
            builder.UseLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()));
            builder.UseSqlServer(connectionString);
            var dbContext = new SqliteContext(builder.Options);
            var query = from a in dbContext.AppointmentOrder
                         join cc in dbContext.AppointmentDetail on a.AppointmentId equals cc.AppId into ccQuery
                         from c in ccQuery.DefaultIfEmpty()
                         select new
                         {
                             a.AppointmentId,
                             c.DetailCount
                         };
            var list = query.ToList();
            Console.ReadLine();
        }
        
        
    public class AppointmentOrder
    {
        public int AppointmentId { get; set; }
        public string AppointmentNo { get; set; }
    }
    public class AppointmentDetail
    {
        public int AppId { get; set; }
        public int DetailCount { get; set; }
    }
        

image

shaofing avatar Jul 17 '24 07:07 shaofing

I am using ((int?)c.DetailCount).GetValueOrDefault() instead of it

But we need to make changes one by one

image

shaofing avatar Jul 18 '24 09:07 shaofing