Skip to content
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

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

Closed
vipwan opened this issue Sep 28, 2022 · 8 comments
Assignees
Labels
area-json closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@vipwan
Copy link

vipwan commented Sep 28, 2022

File a bug

  • 当查询一组数据如果改类型包含一个JSON列,当该JSON列的特定属性不存在时 查询结果绑定到实体会报错 !

image

Include your code

    public class Author
    {
        public string? UserName { get; set; }

        public string? Email { get; set; }

        public string? Address { get; set; }

    }

OnModelCreating :

    modelBuilder.Entity<Blog>()
        .OwnsMany(b => b.Versions, builder =>
        {
            builder.ToJson("Versions");
        })
        .OwnsMany(b => b.Comments, builder =>
        {
            builder.ToJson();
        })
        .OwnsOne(b => b.Author, bulider =>
        {
            bulider.ToJson();
        });
var item = await _dbContext.Blogs.FirstOrDefaultAsync(x => x.Id == 1);   //Error! 

var item2 = await _dbContext.Blogs.FirstOrDefaultAsync(x => x.Id == 2);   //Success ! 

Include stack traces

Id=1 的这行数据是历史数据 插入时 JSON对象并不包含 Address 字段, 所以当更新实体对象后 (Author对象新增 Address 字段 ),查询这条记录抛出如下错误:

  System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
     at lambda_method697(Closure, QueryContext, Object[], JsonElement)
     at lambda_method696(Closure, QueryContext, DbDataReader, ResultContext, SplitQueryResultCoordinator)
     at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`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)

我认为Address 属性没有序列化到JSON和 {Address:null ...} 应该是等价的 而不应该抛出异常!

Include provider and version information

EF Core version: 7.0.0-rc.1.22426.7
Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (e.g. .NET 6.0)

@vipwan
Copy link
Author

vipwan commented Sep 28, 2022

#29199 The exceptions are similar, but the trigger points are different

@maumar
Copy link
Contributor

maumar commented Sep 28, 2022

translation:

When querying a set of data, if the type contains a JSON column, when the specific attribute of the JSON column does not exist, the query result is bound to the entity and an error will be reported

(,,,)

The row of data with Id=1 is historical data. The JSON object does not contain the Address field when it is inserted, so when the entity object is updated (the Address field is added to the Author object), the following error is thrown when querying this record:

(...)

I think the Address property is not serialized to JSON and {Address:null ...} should be equivalent and should not throw an exception!

@alienwareone
Copy link

I think the Address property is not serialized to JSON and {Address:null ...} should be equivalent and should not throw an exception!

👍

That's what I would expect when migrating from a Value Conversion based json column.

@vipwan
Copy link
Author

vipwan commented Sep 28, 2022

I think the Address property is not serialized to JSON and {Address:null ...} should be equivalent and should not throw an exception!

👍

That's what I would expect when migrating from a Value Conversion based json column.

It is enough to use Conversion to access the serialized JSON string, but EF7 supports JSON operations at the database level, such as retrieving the Address field through Linq->SQL, and even using SQL's built-in JSON function.

In addition used Conversion that bulider. ToJson(); Isn't that redundant...

smitpatel added a commit that referenced this issue Sep 29, 2022
@maumar maumar changed the title EFCore 7.0.0-rc.1.22426.7 查询含Json列数据解析的问题 Json: error when trying to materialize json entity with nullable property that is missing in the json string - should materialize the property as null instead Oct 1, 2022
maumar added a commit that referenced this issue Oct 1, 2022
#29214 - Json column: constructor with optional enum-parameter throws ArgumentException

When producing shaper code for json nullable properties with converters we check if the converter handles null, if not we need to peek into the json value and only pass it thru converter if it's not null, and return null otherwise

#29217 - Json: nullable enums on entities mapped to json don't use enum-to-int converter, rather than string, like for non-nullable enums

In convention code unwrap nullable type when testing for enum properties that we then apply converter to string

#29225 - Json: projecting nullable scalar produces debug.assert in SqlExpression ctor

Unwrap nullable type from property before passing it as type to SqlExpression ctor

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

Modify ExtractJsonProperty method so that it used TryGetProperty when trying to extract nullable properties.

Fixes #29214
Fixes #29217
Fixes #29225
Fixes #29219
maumar added a commit that referenced this issue Oct 1, 2022
#29214 - Json column: constructor with optional enum-parameter throws ArgumentException

When producing shaper code for json nullable properties with converters we check if the converter handles null, if not we need to peek into the json value and only pass it thru converter if it's not null, and return null otherwise

#29217 - Json: nullable enums on entities mapped to json don't use enum-to-int converter, rather than string, like for non-nullable enums

In convention code unwrap nullable type when testing for enum properties that we then apply converter to string

#29225 - Json: projecting nullable scalar produces debug.assert in SqlExpression ctor

Unwrap nullable type from property before passing it as type to SqlExpression ctor

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

Modify ExtractJsonProperty method so that it used TryGetProperty when trying to extract nullable properties.

Fixes #29214
Fixes #29217
Fixes #29225
Fixes #29219
@maumar maumar self-assigned this Oct 1, 2022
@maumar maumar added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 1, 2022
@maumar maumar added this to the 7.0.0 milestone Oct 1, 2022
maumar added a commit that referenced this issue Oct 3, 2022
#29214 - Json column: constructor with optional enum-parameter throws ArgumentException

When producing shaper code for json nullable properties with converters we check if the converter handles null, if not we need to peek into the json value and only pass it thru converter if it's not null, and return null otherwise

#29217 - Json: nullable enums on entities mapped to json don't use enum-to-int converter, rather than string, like for non-nullable enums

In convention code unwrap nullable type when testing for enum properties that we then apply converter to string

#29225 - Json: projecting nullable scalar produces debug.assert in SqlExpression ctor

Unwrap nullable type from property before passing it as type to SqlExpression ctor

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

Modify ExtractJsonProperty method so that it used TryGetProperty when trying to extract nullable properties.

Fixes #29214
Fixes #29217
Fixes #29225
Fixes #29219
maumar added a commit that referenced this issue Oct 4, 2022
#29214 - Json column: constructor with optional enum-parameter throws ArgumentException

When producing shaper code for json nullable properties with converters we check if the converter handles null, if not we need to peek into the json value and only pass it thru converter if it's not null, and return null otherwise

#29217 - Json: nullable enums on entities mapped to json don't use enum-to-int converter, rather than string, like for non-nullable enums

In convention code unwrap nullable type when testing for enum properties that we then apply converter to string

#29225 - Json: projecting nullable scalar produces debug.assert in SqlExpression ctor

Unwrap nullable type from property before passing it as type to SqlExpression ctor

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

Modify ExtractJsonProperty method so that it used TryGetProperty when trying to extract nullable properties.

Fixes #29214
Fixes #29217
Fixes #29225
Fixes #29219
@maumar
Copy link
Contributor

maumar commented Oct 4, 2022

fixed in d4c22b2

@imranmomin
Copy link

imranmomin commented Dec 18, 2022

@maumar Thanks for fixing the above issue. Unfortunately, I'm facing a similar issue when an object is missing from the JSON document. Lets say the Address was not a string and is an actual class representing different fields of an address.

public class Contact
{
    public int Id{ get; set; }
    public ContactDetails? Contact { get; set; }
}

public class ContactDetails
{
    public string? Email { get; set; }
    public string? Phone { get; set; }
    public Address? Address { get; set; }
}

public class Address
{
    public string? Street { get; set; }
    public string? City { get; set; }
    public string? PostCode { get; set; }
    public string? State { get; set; }
    public string? Country { get; set; }
}

Configuration

public class ContactConfigurations : IEntityTypeConfiguration<Contact>
{

    public void Configure(EntityTypeBuilder<Contact> builder)
    {
        builder.ToTable("tbl_contacts", "member");
        builder.HasKey(x => x.Id);

        builder.Property(x => x.Id)
            .HasColumnName("id")
            .IsRequired();
           
        builder.OwnsOne(x => x.Contact, b =>
        {
            b.ToJson("contact_details");
            b.OwnsOne(x => x.Address);
        });
    }
}

Data

id contact_details
1 { "Email": "me@example.com", "Phone": "123-456-5555" }
2 { "Address": null, "Email": "me@example.com", "Phone": "123-456-5555" }
3 { "Address": {}, "Email": "me@example.com", "Phone": "123-456-5555" }
4 { "Address": { "Country": "Canada" }, "Email": "me@example.com", "Phone": "123-456-5555" }
var contact1 = await _dbContext.Contacts.SingleOrDefaultAsync(x => x.id == 1);  // error with the below exception
var contact2 = await _dbContext.Contacts.SingleOrDefaultAsync(x => x.id == 2);  // success
var contact3 = await _dbContext.Contacts.SingleOrDefaultAsync(x => x.id == 3);  // success
var contact4 = await _dbContext.Contacts.SingleOrDefaultAsync(x => x.id == 3);  // success

Exception

System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
   at System.Text.Json.JsonElement.GetProperty(String propertyName)
   at lambda_method40(Closure, QueryContext, Object[], JsonElement)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.IncludeJsonEntityReference[TIncludingEntity,TIncludedEntity](QueryContext queryContext, Nullable`1 jsonElement, Object[] keyPropertyValue
s, TIncludingEntity entity, Func`4 innerShaper, Action`2 fixup)
   at lambda_method39(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)

EF Core version: 7.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7)

@rvplauborg
Copy link

rvplauborg commented Jan 11, 2023

@imranmomin Have you found out anything regarding that or created a new separate issue given this one is closed? We just stumbled into that unexpected behavior as well.
EDIT: I now created a new issue for this.

@imranmomin
Copy link

@rvplauborg I have not created any separate issue for it. Thanks for doing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-json closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants