efcore icon indicating copy to clipboard operation
efcore copied to clipboard

Json: error when trying to materialize json entity with nullable property that is null in the json string - should materialize the property as null instead

Open r-Larch opened this issue 1 year ago • 2 comments

Maybe a regression of https://github.com/dotnet/efcore/issues/29219

Materializing a JSON entity with null property results in an error even if the target model allows null. Expected to materialize the property as null instead.

var error2 = await db.LawCategories
    .AsNoTracking()
    .Where(_ => _.Id == 2)
    .Select(_ => new ModelDto(_.Id, _.Meta.Release))
    .FirstOrDefaultAsync(token); // error: System.InvalidOperationException: Entity LawRelease is required but the JSON element containing it is null.
    
public record ModelDto(int Id, LawRelease? Release);
System.InvalidOperationException: Entity LawRelease is required but the JSON element containing it is null.
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.MaterializeJsonEntity[TEntity](QueryContext queryContext, Object[] keyPropertyValues, JsonReaderData jsonReaderData, Boolean nullable, Func`4 shaper)
   at lambda_method697(Closure, QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator)
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken)

Entity Definition:

public class LawCategory {
    public int Id { get; set; }
    // ... props
    public LawCategoryMeta Meta { get; set; } = null!;

    // ... navigation
}

public class LawCategoryMeta {
    public LawRelease? Release { get; set; }
}

public class LawCategoryConfiguration : IEntityTypeConfiguration<LawCategory> {
    public void Configure(EntityTypeBuilder<LawCategory> builder) {
        builder.OwnsOne(_ => _.Meta, _ => {
            _.ToJson();
            _.OwnsOne(_ => _.Release);
        });
        // ... more
    }
}

Data

Id Meta Note
2 {"Release": null} error in all cases
19 {"Release": {"Date": "2022-10-10", "Number": 149, "Source": "Lgs."}} works in all cases

Example Code to show error

var works = await db.LawCategories
    .AsNoTracking()
    .Where(_ => _.Id == 19)
    .Select(_ => _.Meta.Release)
    .FirstOrDefaultAsync(token); // works

var error1 = await db.LawCategories
    .AsNoTracking()
    .Where(_ => _.Id == 2)
    .Select(_ => _.Meta.Release)
    .FirstOrDefaultAsync(token); // error: System.InvalidOperationException: Entity LawRelease is required but the JSON element containing it is null.
    
var error2 = await db.LawCategories
    .AsNoTracking()
    .Where(_ => _.Id == 2)
    .Select(_ => new ModelDto(_.Id, _.Meta.Release))
    .FirstOrDefaultAsync(token); // error: System.InvalidOperationException: Entity LawRelease is required but the JSON element containing it is null.
    
public record ModelDto(int Id, LawRelease? Release);

Provider and version information

EF Core version: Microsoft.EntityFrameworkCore v8.0.7 Database provider: Npgsql.EntityFrameworkCore.PostgreSQL v8.0.4 Target framework: .NET 8.0 Operating system: Windows IDE: Microsoft Visual Studio Community 2022 (64-bit) Version 17.10.4

r-Larch avatar Jul 26 '24 12:07 r-Larch

Entity OrganizationData is required but the JSON element containing it is null

I also encountered this problem today

public OrganizationData? Organization { get; set; }

{
  "User": {
    "Gender": 1,
    "IsInJob": false,
    "UserName": "xiaodong",
    "TravelLevel": "ygzj-1",
    "OrganizationCode": "XXB",
    "SettlementEntityCode": "xxB"
  },
  "Organization": null,
  "SettlementEntity": null
}

Normal operation: var sss = db.TImportData.AsNoTracking().ToList();


There is a bug: var sss1 = db.TImportData.AsNoTracking().Select(t => t.Data).ToList();

dashiell-zhang avatar Aug 21 '24 02:08 dashiell-zhang

@roji From what we have observed so far, if you query directly against the table, there is no problem, but if you query against a new dto, there will be a problem.

dashiell-zhang avatar Aug 21 '24 03:08 dashiell-zhang

The problem is we were incorrectly computing the nullability of json reference - using navigation.ForeignKey.IsRequired rather than navigation.ForeignKey.IsRequiredDependent

maumar avatar Nov 04 '24 23:11 maumar

https://github.com/dotnet/efcore/issues/35412 This problem seems to still exist

dashiell-zhang avatar Jan 06 '25 04:01 dashiell-zhang

@dashiell-zhang this issue has been fixed in the upcoming EF10 - we didn't fix it in time for EF9.

maumar avatar Jan 07 '25 00:01 maumar

@maumar Any plan to backport it in EF9 ? It is a blocking point for me.

boukenka avatar Feb 06 '25 15:02 boukenka

@boukenka at the moment we don't plan to port this to EF9, reason being that it's not a regression (scenario also fails on EF8) and relatively few people have hit the issue so far.

maumar avatar Feb 10 '25 22:02 maumar

We would also appreciate this being backported to EF9 instead of having to wait for EF10

dheardal avatar Feb 11 '25 09:02 dheardal

Also facing this problem. The workaround for my use case isn't great. Would also really appreciate a patch into 9 @maumar. Thanks

eaton-sam avatar Apr 04 '25 13:04 eaton-sam

A Backport to EFCore 9 would also be appreciated.

c5racing avatar May 01 '25 17:05 c5racing