Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to debug and fix 'Nullable object must have a value' within Entity Framework Core?

I'm doing a projection in this method:

public async Task<TradeDetail> Get(int tradeId)
{
    var firstOrDefaultAsync = await context.EvgTGTrade
        .Where(x => x.IdTrade == tradeId)
        .Select(trade => new TradeDetail
        {
            Id = trade.IdTrade,
            Code = trade.CdTrade,
            Description = trade.DeTrade,
            Comments = trade.DeComentarios,
            TypeId = trade.IdTipoTrade,
            BrokerId = trade.EvgTGBrokerTrade.FirstOrDefault().IdBroker,
            BillingCycleId = trade.IdCicloFacturacion,
            CounterpartId = trade.IdSujeto,
            StartDate = trade.FhIni,
            EndDate = trade.FhFin,
            SignatureDate = trade.FhFirma,
            Positions = trade.EvgTGPosicion.Select(pos => new Position
            {
                Amount = pos.Cantidad,
                Code = pos.CdPosicion,
                Description = pos.DePosicion,
                LogisticElement = pos.IdElemLogNavigation.DeElemLog,
                StartDate = pos.FhIni,
                EndDate = pos.FhFin,
                FormulaId = pos.IdFormula,
                Operations = pos.EvgTGOperacionCv.Select(op =>
                    new Operation()
                    {
                        TypeId = op.IdTipoOperacion,
                        Fee = op.Fee,
                        Date = op.FhOperacionCv,
                        Price = op.NmPrecio ?? 0,
                        Quantity = op.NmCantidadKwh ?? 0,
                    }),
            })
        })
        .FirstOrDefaultAsync();
    return firstOrDefaultAsync;
}

I'm getting an exception at the first line saying

Microsoft.EntityFrameworkCore.Query: Error: An exception occurred while iterating over the results of a query for context type 'SampleApp.EF.MyDbContext'. System.InvalidOperationException: Nullable object must have a value.

I've inspected the exception and the stack trace and it doesn't say anything about which is the problematic object. No clue of what's wrong.

So, is there a method to know which property generates the conflict? I'm sure it's a property-column mismatch, but what if you have a a lot of properties? It's quite painful!

Update

I've already fixed the problem. It was due to an attempt of assigning null to a property in my model that was non-nullable.

That said, this questions isn't about this concrete fix, but about finding a general way to debug this kind of situation (this exception) that are quite common. IMHO, the exception message is too vague. It doesn't provide any useful information of the conflicting property.

like image 671
SuperJMN Avatar asked Jan 22 '20 16:01

SuperJMN


2 Answers

There can be two answers.

Firstly, this may be probably because your query is returning NULL and the model is not accepting null.

Second fix may be, if you are using anonymous types, then try typecasting the query result into nullable type.

e.g.

Id = (int?) op.Id,

how to find which property is returning NULL?

You can enable SQL Profiler and check the SQL query which is getting executed. Copy the query into SSMS and see which values are NULL.
This may be helpful for you to decide out if you really need nullable typecast OR you want to change the query itself.

There is an issue on GitHub on this page.

Update: 05-July-2021: How to find which property is returning NULL ?

You can use simple logging feature (introduced in EF core 5.0) by calling EnableDetailedErrors method. Below code example shows how to configure Simple Logging for showing logs on Console and it also enables detailed errors. If you do not want to show logs on console, then you can pass an action delegate in LogTo call, which accepts a string as parameter. This delegate can then write logs to any destination of your choice.

Refer documentation for more details.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .LogTo(Console.WriteLine)
        .EnableDetailedErrors();
like image 80
Manoj Choudhari Avatar answered Sep 21 '22 01:09

Manoj Choudhari


I want to add to @Manoj-Choudhari's answer which helped me track down an issue which started after I upgraded from EF Core 3.x to EF Core 5.x.

I had a query like this: (Item[1] -> Categories[0..n] -> Parent[1], keys are ints)

from item in ctx.Items
from ixc in item.Categories.DefaultIfEmpty() /* Left join */
let parent = ixc.Parent
select new {
    itemId = item.Id,
    parentId = parent.Id
}

This fails with the same "Nullable object must have a value" error if the navigation to "parent" ends up null. What I observed different in behavior between EFC3 and EFC5 is the parent.Id part of the projection used to be automatically inferred as int? because it seemed to understand parent could be null. Seems you have to be more explicit with EF Core 5 and actually cast the mapped types to the nullable type manually.

from item in ctx.Items
from ixc in item.Categories.DefaultIfEmpty() /* Left join */
let parent = ixc.Parent
select new {
    itemId = item.Id,
    parentId = (int?)parent.Id
}

The old way was probably more of an exploit, and its better to be specific now.

Edit: The EF Core team is aware of this behavior: https://github.com/dotnet/efcore/issues/22517

like image 39
K0D4 Avatar answered Sep 21 '22 01:09

K0D4