Skip to content

WriteJson: Object reference not set to an instance of an object. #35530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
douglasg14b opened this issue Jan 25, 2025 · 6 comments
Closed

WriteJson: Object reference not set to an instance of an object. #35530

douglasg14b opened this issue Jan 25, 2025 · 6 comments

Comments

@douglasg14b
Copy link

douglasg14b commented Jan 25, 2025

Bug description

Small model, trying to add a new entity. EF Core odly enough throws a null ref exception.

The last stack frame is at:

            if (value is not null)
            {
                var jsonValueReaderWriter = property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter;
                Check.DebugAssert(jsonValueReaderWriter is not null, "Missing JsonValueReaderWriter on JSON property");
                jsonValueReaderWriter.ToJson(writer, value);
            }

The result of property.GetJsonValueReaderWriter() ?? property.GetTypeMapping().JsonValueReaderWriter; is null for a dictionary?

Image

Your code

builder.Services.AddDbContext<AppDbContext>(options =>
            options.UseNpgsql(appConfig.ConnectionString)
        );



var entity = new ReqResEntity(enrichedData);
db.ReqResData.Add(entity);
await db.SaveChangesAsync();





public class AppDbContext : DbContext
{
    public DbSet<ReqResEntity> ReqResData { get; set; }

    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<ReqResEntity>()
            .OwnsOne(x => x.RequestUrl, model => model.ToJson())
            .OwnsOne(x => x.SourceUrl, model => model.ToJson())
            .OwnsOne(x => x.Response, model => model.ToJson())
            .OwnsOne(x => x.Request, model => model.ToJson());
    }
}




public class ReqResEntity
{
    public int Id { get; set; }

    public DateTimeOffset TimeStamp { get; set; }

    public UrlDetailsEntity SourceUrl { get; set; }
    public UrlDetailsEntity RequestUrl { get; set; }
    public RequestEntity? Request { get; set; }
    public ResponseEntity? Response { get; set; }

    public string Agent { get; set; }
    public string Protocol { get; set; }
    public string HttpMethod { get; set; }
    public string? StatusCode { get; set; }


    private ReqResEntity() { }

    public ReqResEntity(EnrichedReqResData data)
    {
        TimeStamp = data.TimeStamp;
        SourceUrl = new UrlDetailsEntity(data.SourceUrl);
        RequestUrl = new UrlDetailsEntity(data.RequestUrl);
        Request = new RequestEntity(data.Request);
        Response = data.Response != null ? new ResponseEntity(data.Response) : null;
        Agent = data.Agent;
        Protocol = data.Protocol;
        HttpMethod = data.Request.HttpMethod;
        StatusCode = data.Response?.StatusCode.ToString() ?? null;
    }
}

public class UrlDetailsEntity
{
    public string Url { get; set; }
    public string Fqdn { get; set; }
    public string Tld { get; set; }
    public string Domain { get; set; }
    public string SubDomain { get; set; }

    public string Path { get; set; }
    public string Query { get; set; }

    private UrlDetailsEntity() { }

    public UrlDetailsEntity(UrlDetails urlDetails)
    {
        Url = urlDetails.Url;
        Fqdn = urlDetails.Fqdn;
        Tld = urlDetails.Tld;
        Domain = urlDetails.Domain;
        SubDomain = urlDetails.SubDomain;
        Path = urlDetails.Path;
        Query = urlDetails.Query;
    }
}

public class RequestEntity
{
    public DateTimeOffset TimeStamp { get; set; }
    public Dictionary<string, string> Headers { get; set; }
    public bool IsComplete { get; set; }
    public string? ContentType { get; set; }

    public int BodySize { get; set; }

    private RequestEntity() { }

    public RequestEntity(EnrichedRequest request)
    {
        TimeStamp = request.TimeStamp;
        Headers = request.Headers;
        IsComplete = request.IsComplete;
        ContentType = request.ContentType;

        BodySize = Encoding.Unicode.GetByteCount(request.Body);
    }
}

public class ResponseEntity
{
    public DateTimeOffset TimeStamp { get; set; }
    public string Reason { get; set; }
    public Dictionary<string, string> Headers { get; set; }
    public bool IsComplete { get; set; }
    public string? ContentType { get; set; }

    public int BodySize { get; set; }

    private ResponseEntity() { }

    public ResponseEntity(EnrichedResponse response)
    {
        TimeStamp = response.TimeStamp;
        Reason = response.Reason;
        Headers = response.Headers;
        IsComplete = response.IsComplete;
        ContentType = response.ContentType;

        BodySize = Encoding.Unicode.GetByteCount(response.Body);
    }
}

Stack traces

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.WriteJson(Utf8JsonWriter writer, Object navigationValue, IUpdateEntry parentEntry, IEntityType entityType, Nullable`1 ordinal, Boolean isCollection, Boolean isTopLevel)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<GenerateColumnModifications>g__HandleJson|41_4(List`1 columnModifications, <>c__DisplayClass41_0&)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.GenerateColumnModifications()
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<>c.<get_ColumnModifications>b__33_0(ModificationCommand command)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.get_ColumnModifications()
   at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlUpdateSqlGenerator.AppendInsertOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, Int32 commandPosition, Boolean overridingSystemValue, Boolean& requiresTransaction)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlUpdateSqlGenerator.AppendInsertOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, Int32 commandPosition, Boolean& requiresTransaction)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.AddCommand(IReadOnlyModificationCommand modificationCommand)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.TryAddCommand(IReadOnlyModificationCommand modificationCommand)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.CreateCommandBatches(IEnumerable`1 commandSet, Boolean moreCommandSets, Boolean assertColumnModification, ParameterNameGenerator parameterNameGenerator)+MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList`1 entries, IUpdateAdapter updateAdapter)+MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Webbler.Web.WebApp.<>c.<<Build>b__0_1>d.MoveNext()

Verbose output


EF Core version

9.0.1

Database provider

Npgsql.EntityFrameworkCore.PostgreSQL

Target framework

.NET 9

Operating system

Windows 10

IDE

Rider

@roji
Copy link
Member

roji commented Jan 27, 2025

@douglasg14b can you please take the time to submit a runnable, minimal code sample (please always do that)? The above contains references to types which aren't included (e.g. EnrichedReqResData). You're effectively asking us to guess the missing parts of your code sample.

@cincuranet
Copy link
Contributor

No repro was provided (yet). Closing. Will reopen if repro is provided.

@cincuranet cincuranet closed this as not planned Won't fix, can't repro, duplicate, stale Feb 3, 2025
@chklauser
Copy link

I think I have a reproduction here: https://github.com/chklauser/ReproNreJsonElement/tree/a8dbeb960a6b3fcbd4702a4bedc7b48669823488

Any hints on how to work around this issue until a fix is available would be most welcome 😄

@cincuranet / @roji would this be sufficient to re-open the ticket?

@cincuranet cincuranet reopened this Mar 13, 2025
@cincuranet
Copy link
Contributor

Yep. That works.

@cincuranet
Copy link
Contributor

Minimal repro:

using System.Text.Json;
using Microsoft.EntityFrameworkCore;

await using var db = new MyDbContext();
db.Database.EnsureCreated();
db.RootEntities.Add(new()
{
    Owned = new()
    {
        Untyped = JsonSerializer.SerializeToElement(new { x = 5, y = 7 })
    }
});
await db.SaveChangesAsync();

class RootEntity
{
    public long Id { get; set; }
    public OwnedEntity? Owned { get; set; }
}

class OwnedEntity
{
    public JsonElement? Untyped { get; set; }
}

class MyDbContext : DbContext
{
    public DbSet<RootEntity> RootEntities { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=test.db");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RootEntity>().OwnsOne(r => r.Owned,
            o => o.ToJson());
    }
}

Exception:

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.WriteJson(Utf8JsonWriter writer, Object navigationValue, IUpdateEntry parentEntry, IEntityType entityType, Nullable`1 ordinal, Boolean isCollection, Boolean isTopLevel)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<GenerateColumnModifications>g__HandleJson|41_4(List`1 columnModifications, <>c__DisplayClass41_0&)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.GenerateColumnModifications()
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.<>c.<get_ColumnModifications>b__33_0(ModificationCommand command)
   at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
   at Microsoft.EntityFrameworkCore.Update.ModificationCommand.get_ColumnModifications()
   at Microsoft.EntityFrameworkCore.Update.UpdateSqlGenerator.AppendInsertReturningOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, Int32 commandPosition, Boolean& requiresTransaction)
   at Microsoft.EntityFrameworkCore.Sqlite.Update.Internal.SqliteUpdateSqlGenerator.AppendInsertOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, Int32 commandPosition, Boolean& requiresTransaction)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.AddCommand(IReadOnlyModificationCommand modificationCommand)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.TryAddCommand(IReadOnlyModificationCommand modificationCommand)
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.CreateCommandBatches(IEnumerable`1 commandSet, Boolean moreCommandSets, Boolean assertColumnModification, ParameterNameGenerator parameterNameGenerator)+MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.CommandBatchPreparer.BatchCommands(IList`1 entries, IUpdateAdapter updateAdapter)+MoveNext()
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable`1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

@maumar
Copy link
Contributor

maumar commented Apr 2, 2025

dupe of #32192

now we throw much better exception:

The property 'OwnedEntity.Untyped' could not be mapped because it is of type 'JsonElement?', which is not a supported primitive type or a valid entity type. Either explicitly map this property, or ignore it using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

@maumar maumar closed this as completed Apr 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants