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

Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying #30993

Closed
maumar opened this issue May 30, 2023 · 1 comment · Fixed by #31160
Assignees
Labels
area-json area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@maumar
Copy link
Contributor

maumar commented May 30, 2023

The way we materialize json entities is different than regular/non-json owned entities. For json we prune the entire include tree and materialize related json entities as part of it's parent entity materialization (recursively). This may lead to a problem in tracking query. We query for entity with nested json structure, it gets properly materialized and stored in change tracker. Then we modify the nested json entity (e.g. add element to nested json collection) and re-query. In the materializer we try to get entity from change tracker - we find it and return it. However that short-circuits materializing of the nested owned json entities and therefore we don't see the updates.

In regular queries we preserve the entire IncludeExpression structure (rather than prune it), so we go through materialization of all the entities, regardless if we find some of them in the change tracker or not.

To fix this we should rewrite the json entity materializer logic for successfully finding entity entry for the given key - instead of simply returning the entity from the entry, we need to also generate code to build all the navigations like we do for NoTracking/first query.

In case of utf8jsonreader we can use the "simplified" pattern (like we could use for entities with parameterless ctors) - we already have the entity instance, so don't need to cache all the navigations and can do the fixup between parent and children in place.

@maumar
Copy link
Contributor Author

maumar commented May 30, 2023

(QueryContext queryContext, DbDataReader dataReader, ResultContext resultContext, SingleQueryResultCoordinator resultCoordinator) => 
{
    JsonEntityCustomNaming namelessParameter{0};
    Stream namelessParameter{1};
    JsonReaderData namelessParameter{2};
    Utf8JsonReaderManager namelessParameter{3};
    object[] namelessParameter{4};
    Stream namelessParameter{5};
    JsonReaderData namelessParameter{6};
    Utf8JsonReaderManager namelessParameter{7};
    object[] namelessParameter{8};
    Stream namelessParameter{9};
    JsonReaderData namelessParameter{10};
    Utf8JsonReaderManager namelessParameter{11};
    object[] namelessParameter{12};
    List<JsonOwnedCustomNameBranch> namelessParameter{13};
    namelessParameter{0} = 
    {
        MaterializationContext materializationContext1;
        IEntityType entityType1;
        JsonEntityCustomNaming instance1;
        InternalEntityEntry entry1;
        bool hasNullKey1;
        materializationContext1 = new MaterializationContext(
            ValueBuffer, 
            queryContext.Context
        );
        instance1 = null;
        entry1 = queryContext.TryGetEntry(
            key: Key: JsonEntityCustomNaming.Id PK, 
            keyValues: new object[]{ (object)dataReader.GetInt32(0) }, 
            throwOnNullKey: True, 
            hasNullKey: hasNullKey1);
        !(hasNullKey1) ? entry1 != default(InternalEntityEntry) ? 
        {
            entityType1 = entry1.EntityType;
            return instance1 = (JsonEntityCustomNaming)entry1.Entity;
        } : 
        {
            ValueBuffer shadowValueBuffer1;
            shadowValueBuffer1 = ValueBuffer;
            entityType1 = EntityType: JsonEntityCustomNaming;
            instance1 = switch (entityType1)
            {
                case EntityType: JsonEntityCustomNaming: 
                    {
                        return 
                        {
                            JsonEntityCustomNaming instance;
                            instance = new JsonEntityCustomNaming();
                            instance.<Id>k__BackingField = dataReader.GetInt32(0);
                            instance.<Title>k__BackingField = dataReader.IsDBNull(1) ? default(string) : (string)dataReader.GetFieldValue<object>(1);
                            (instance is IInjectableService) ? ((IInjectableService)instance).Injected(
                                context: materializationContext1.Context, 
                                entity: instance, 
                                bindingInfo: ParameterBindingInfo) : default(void);
                            return instance;
                        }}
                default: 
                    null
            }
            ;
            entry1 = entityType1 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                entityType: entityType1, 
                entity: instance1, 
                valueBuffer: shadowValueBuffer1);
            return instance1;
        } : default(void);
        return instance1;
    };
    namelessParameter{1} = dataReader.IsDBNull(2) ? default(MemoryStream) : new MemoryStream(Encoding.UTF8.GetBytes((string)dataReader.GetFieldValue<object>(2)));
    namelessParameter{2} = new JsonReaderData(namelessParameter{1});
    namelessParameter{3} = new Utf8JsonReaderManager(namelessParameter{2});
    namelessParameter{3}.MoveNext();
    namelessParameter{3}.CaptureState();
    namelessParameter{4} = new object[]{ (object)dataReader.GetInt32(0) };
    namelessParameter{5} = dataReader.IsDBNull(3) ? default(MemoryStream) : new MemoryStream(Encoding.UTF8.GetBytes((string)dataReader.GetFieldValue<object>(3)));
    namelessParameter{6} = new JsonReaderData(namelessParameter{5});
    namelessParameter{7} = new Utf8JsonReaderManager(namelessParameter{6});
    namelessParameter{7}.MoveNext();
    namelessParameter{7}.CaptureState();
    namelessParameter{8} = new object[]{ (object)dataReader.GetInt32(0) };
    namelessParameter{9} = dataReader.IsDBNull(4) ? default(MemoryStream) : new MemoryStream(Encoding.UTF8.GetBytes((string)dataReader.GetFieldValue<object>(4)));
    namelessParameter{10} = new JsonReaderData(namelessParameter{9});
    namelessParameter{11} = new Utf8JsonReaderManager(namelessParameter{10});
    namelessParameter{11}.MoveNext();
    namelessParameter{11}.CaptureState();
    namelessParameter{12} = new object[]{ (object)dataReader.GetInt32(0) };
    ShaperProcessingExpressionVisitor.IncludeJsonEntityCollection2<JsonEntityCustomNaming, JsonOwnedCustomNameRoot>(
        queryContext: queryContext, 
        keyPropertyValues: namelessParameter{4}, 
        jsonReaderData: namelessParameter{2}, 
        entity: namelessParameter{0}, 
        innerShaper: (QueryContext queryContext, object[] namelessParameter{14}, JsonReaderData namelessParameter{15}) => 
        {
            JsonOwnedCustomNameRoot namelessParameter{16};
            return namelessParameter{16} = 
            {
                MaterializationContext materializationContext2;
                IEntityType entityType2;
                JsonOwnedCustomNameRoot instance2;
                InternalEntityEntry entry2;
                bool hasNullKey2;
                materializationContext2 = new MaterializationContext(
                    ValueBuffer, 
                    queryContext.Context
                );
                instance2 = null;
                entry2 = queryContext.TryGetEntry(
                    key: Key: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.JsonEntityCustomNamingId, JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.Id PK, 
                    keyValues: new object[]
                    { 
                        namelessParameter{14}[0], 
                        namelessParameter{14}[1] 
                    }, 
                    throwOnNullKey: False, 
                    hasNullKey: hasNullKey2);
                !(hasNullKey2) ? entry2 != default(InternalEntityEntry) ? 
                {
                    entityType2 = entry2.EntityType;
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
					
                    return instance2 = (JsonOwnedCustomNameRoot)entry2.Entity;
                } : 
                {
                    ValueBuffer shadowValueBuffer2;
                    shadowValueBuffer2 = ValueBuffer;
                    entityType2 = EntityType: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot CLR Type: JsonOwnedCustomNameRoot Owned;
                    instance2 = 
                    {
                        JsonOwnedCustomNameRoot instance;
                        Utf8JsonReaderManager namelessParameter{17};
                        JsonTokenType tokenType;
                        JsonEnum namelessParameter{18};
                        string namelessParameter{19};
                        int namelessParameter{20};
                        List<JsonOwnedCustomNameBranch> namelessParameter{21};
                        JsonOwnedCustomNameBranch namelessParameter{22};
                        shadowValueBuffer2 = new ValueBuffer(new object[]
                        { 
                            namelessParameter{14}[0], 
                            namelessParameter{14}[1] 
                        });
                        namelessParameter{17} = new Utf8JsonReaderManager(namelessParameter{15});
                        tokenType = namelessParameter{17}.TokenType();
                        Loop(Break: done Continue: )
                        {
                            {
                                tokenType = namelessParameter{17}.MoveNext();
                                tokenType != EndObject ? switch (tokenType)
                                {
                                    case PropertyName: 
                                        namelessParameter{17}.ValueTextEquals(CustomEnum.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{17}.MoveNext();
                                            namelessParameter{18} = (JsonEnum)namelessParameter{17}.CurrentReader.GetInt32();
                                        } : namelessParameter{17}.ValueTextEquals(CustomName.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{17}.MoveNext();
                                            namelessParameter{19} = namelessParameter{17}.CurrentReader.GetString();
                                        } : namelessParameter{17}.ValueTextEquals(CustomNumber.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{17}.MoveNext();
                                            namelessParameter{20} = namelessParameter{17}.CurrentReader.GetInt32();
                                        } : namelessParameter{17}.ValueTextEquals(CustomOwnedCollectionBranch.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{17}.MoveNext();
                                            namelessParameter{17}.CaptureState();
                                            namelessParameter{21} = ShaperProcessingExpressionVisitor.MaterializeJsonEntityCollection2<JsonOwnedCustomNameBranch, List<JsonOwnedCustomNameBranch>>(
                                                queryContext: queryContext, 
                                                keyPropertyValues: namelessParameter{14}, 
                                                jsonReaderData: namelessParameter{15}, 
                                                navigation: Navigation: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch (List<JsonOwnedCustomNameBranch>) Collection ToDependent JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch, 
                                                innerShaper: (QueryContext queryContext, object[] namelessParameter{23}, JsonReaderData namelessParameter{24}) => 
                                                {
                                                    JsonOwnedCustomNameBranch namelessParameter{25};
                                                    return namelessParameter{25} = 
                                                    {
                                                        MaterializationContext materializationContext3;
                                                        IEntityType entityType3;
                                                        JsonOwnedCustomNameBranch instance3;
                                                        InternalEntityEntry entry3;
                                                        bool hasNullKey3;
                                                        materializationContext3 = new MaterializationContext(
                                                            ValueBuffer, 
                                                            queryContext.Context
                                                        );
                                                        instance3 = null;
                                                        entry3 = queryContext.TryGetEntry(
                                                            key: Key: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootJsonEntityCustomNamingId, JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootId, JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.Id PK, 
                                                            keyValues: new object[]
                                                            { 
                                                                namelessParameter{23}[0], 
                                                                namelessParameter{23}[1], 
                                                                namelessParameter{23}[2] 
                                                            }, 
                                                            throwOnNullKey: False, 
                                                            hasNullKey: hasNullKey3);
                                                        !(hasNullKey3) ? entry3 != default(InternalEntityEntry) ? 
                                                        {
                                                            entityType3 = entry3.EntityType;
                                                            return instance3 = (JsonOwnedCustomNameBranch)entry3.Entity;
                                                        } : 
                                                        {
                                                            ValueBuffer shadowValueBuffer3;
                                                            shadowValueBuffer3 = ValueBuffer;
                                                            entityType3 = EntityType: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch CLR Type: JsonOwnedCustomNameBranch Owned;
                                                            instance3 = 
                                                            {
                                                                JsonOwnedCustomNameBranch instance;
                                                                Utf8JsonReaderManager namelessParameter{26};
                                                                JsonTokenType tokenType;
                                                                DateTime namelessParameter{27};
                                                                double namelessParameter{28};
                                                                shadowValueBuffer3 = new ValueBuffer(new object[]
                                                                { 
                                                                    namelessParameter{23}[0], 
                                                                    namelessParameter{23}[1], 
                                                                    namelessParameter{23}[2] 
                                                                });
                                                                namelessParameter{26} = new Utf8JsonReaderManager(namelessParameter{24});
                                                                tokenType = namelessParameter{26}.TokenType();
                                                                Loop(Break: done Continue: )
                                                                {
                                                                    {
                                                                        tokenType = namelessParameter{26}.MoveNext();
                                                                        tokenType != EndObject ? switch (tokenType)
                                                                        {
                                                                            case PropertyName: 
                                                                                namelessParameter{26}.ValueTextEquals(CustomDate.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{26}.MoveNext();
                                                                                    namelessParameter{27} = namelessParameter{26}.CurrentReader.GetDateTime();
                                                                                } : namelessParameter{26}.ValueTextEquals(CustomFraction.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{26}.MoveNext();
                                                                                    namelessParameter{28} = namelessParameter{26}.CurrentReader.GetDouble();
                                                                                } : default(void)
                                                                            default: 
                                                                                namelessParameter{26}.Skip()
                                                                        }
                                                                         : Goto(break done)
                                                                        ;
                                                                    }}
                                                                namelessParameter{26}.CaptureState();
                                                                instance = new JsonOwnedCustomNameBranch();
                                                                instance.<Date>k__BackingField = namelessParameter{27};
                                                                instance.<Fraction>k__BackingField = namelessParameter{28};
                                                                return instance;
                                                            };
                                                            entry3 = entityType3 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                                                                entityType: entityType3, 
                                                                entity: instance3, 
                                                                valueBuffer: shadowValueBuffer3);
                                                            return instance3;
                                                        } : default(void);
                                                        return instance3;
                                                    };
                                                });
                                            namelessParameter{17} = new Utf8JsonReaderManager(namelessParameter{15});
                                        } : namelessParameter{17}.ValueTextEquals(CustomOwnedReferenceBranch.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{17}.MoveNext();
                                            namelessParameter{17}.CaptureState();
                                            namelessParameter{22} = ShaperProcessingExpressionVisitor.MaterializeJsonEntity2<JsonOwnedCustomNameBranch>(
                                                queryContext: queryContext, 
                                                keyPropertyValues: namelessParameter{14}, 
                                                jsonReaderData: namelessParameter{15}, 
                                                nullable: True, 
                                                shaper: (QueryContext queryContext, object[] namelessParameter{29}, JsonReaderData namelessParameter{30}) => 
                                                {
                                                    JsonOwnedCustomNameBranch namelessParameter{31};
                                                    return namelessParameter{31} = 
                                                    {
                                                        MaterializationContext materializationContext4;
                                                        IEntityType entityType4;
                                                        JsonOwnedCustomNameBranch instance4;
                                                        InternalEntityEntry entry4;
                                                        bool hasNullKey4;
                                                        materializationContext4 = new MaterializationContext(
                                                            ValueBuffer, 
                                                            queryContext.Context
                                                        );
                                                        instance4 = null;
                                                        entry4 = queryContext.TryGetEntry(
                                                            key: Key: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedReferenceBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootJsonEntityCustomNamingId, JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedReferenceBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootId PK, 
                                                            keyValues: new object[]
                                                            { 
                                                                namelessParameter{29}[0], 
                                                                namelessParameter{29}[1] 
                                                            }, 
                                                            throwOnNullKey: False, 
                                                            hasNullKey: hasNullKey4);
                                                        !(hasNullKey4) ? entry4 != default(InternalEntityEntry) ? 
                                                        {
                                                            entityType4 = entry4.EntityType;
                                                            return instance4 = (JsonOwnedCustomNameBranch)entry4.Entity;
                                                        } : 
                                                        {
                                                            ValueBuffer shadowValueBuffer4;
                                                            shadowValueBuffer4 = ValueBuffer;
                                                            entityType4 = EntityType: JsonEntityCustomNaming.OwnedCollectionRoot#JsonOwnedCustomNameRoot.OwnedReferenceBranch#JsonOwnedCustomNameBranch CLR Type: JsonOwnedCustomNameBranch Owned;
                                                            instance4 = 
                                                            {
                                                                JsonOwnedCustomNameBranch instance;
                                                                Utf8JsonReaderManager namelessParameter{32};
                                                                JsonTokenType tokenType;
                                                                DateTime namelessParameter{33};
                                                                double namelessParameter{34};
                                                                shadowValueBuffer4 = new ValueBuffer(new object[]
                                                                { 
                                                                    namelessParameter{29}[0], 
                                                                    namelessParameter{29}[1] 
                                                                });
                                                                namelessParameter{32} = new Utf8JsonReaderManager(namelessParameter{30});
                                                                tokenType = namelessParameter{32}.TokenType();
                                                                Loop(Break: done Continue: )
                                                                {
                                                                    {
                                                                        tokenType = namelessParameter{32}.MoveNext();
                                                                        tokenType != EndObject ? switch (tokenType)
                                                                        {
                                                                            case PropertyName: 
                                                                                namelessParameter{32}.ValueTextEquals(CustomDate.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{32}.MoveNext();
                                                                                    namelessParameter{33} = namelessParameter{32}.CurrentReader.GetDateTime();
                                                                                } : namelessParameter{32}.ValueTextEquals(CustomFraction.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{32}.MoveNext();
                                                                                    namelessParameter{34} = namelessParameter{32}.CurrentReader.GetDouble();
                                                                                } : default(void)
                                                                            default: 
                                                                                namelessParameter{32}.Skip()
                                                                        }
                                                                         : Goto(break done)
                                                                        ;
                                                                    }}
                                                                namelessParameter{32}.CaptureState();
                                                                instance = new JsonOwnedCustomNameBranch();
                                                                instance.<Date>k__BackingField = namelessParameter{33};
                                                                instance.<Fraction>k__BackingField = namelessParameter{34};
                                                                return instance;
                                                            };
                                                            entry4 = entityType4 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                                                                entityType: entityType4, 
                                                                entity: instance4, 
                                                                valueBuffer: shadowValueBuffer4);
                                                            return instance4;
                                                        } : default(void);
                                                        return instance4;
                                                    };
                                                });
                                            namelessParameter{17} = new Utf8JsonReaderManager(namelessParameter{15});
                                        } : default(void)
                                    default: 
                                        namelessParameter{17}.Skip()
                                }
                                 : Goto(break done)
                                ;
                            }}
                        namelessParameter{17}.CaptureState();
                        instance = new JsonOwnedCustomNameRoot();
                        instance.<Enum>k__BackingField = namelessParameter{18};
                        instance.<Name>k__BackingField = namelessParameter{19};
                        instance.<Number>k__BackingField = namelessParameter{20};
                        Invoke((JsonOwnedCustomNameRoot namelessParameter{35}, List<JsonOwnedCustomNameBranch> namelessParameter{36}) => 
                        {
                            return namelessParameter{35}.<OwnedCollectionBranch>k__BackingField = namelessParameter{36};
                        }, instance, namelessParameter{21});
                        Invoke((JsonOwnedCustomNameRoot namelessParameter{37}, JsonOwnedCustomNameBranch namelessParameter{38}) => 
                        {
                            return namelessParameter{37}.<OwnedReferenceBranch>k__BackingField = namelessParameter{38};
                        }, instance, namelessParameter{22});
                        return instance;
                    };
                    entry2 = entityType2 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                        entityType: entityType2, 
                        entity: instance2, 
                        valueBuffer: shadowValueBuffer2);
                    return instance2;
                } : default(void);
                return instance2;
            };
        }, 
        fixup: (JsonEntityCustomNaming namelessParameter{39}, JsonOwnedCustomNameRoot namelessParameter{40}) => 
        {
            return ClrICollectionAccessor<JsonEntityCustomNaming, List<JsonOwnedCustomNameRoot>, JsonOwnedCustomNameRoot>.Add(
                entity: namelessParameter{39}, 
                value: namelessParameter{40}, 
                forMaterialization: True);
        });
		
		
		
		
		
		
		
		
    ShaperProcessingExpressionVisitor.IncludeJsonEntityReference2<JsonEntityCustomNaming, JsonOwnedCustomNameRoot>(
        queryContext: queryContext, 
        keyPropertyValues: namelessParameter{8}, 
        jsonReaderData: namelessParameter{6}, 
        entity: namelessParameter{0}, 
        innerShaper: (QueryContext queryContext, object[] namelessParameter{41}, JsonReaderData namelessParameter{42}) => 
        {
            JsonOwnedCustomNameRoot namelessParameter{43};
            return namelessParameter{43} = 
            {
                MaterializationContext materializationContext5;
                IEntityType entityType5;
                JsonOwnedCustomNameRoot instance5;
                InternalEntityEntry entry5;
                bool hasNullKey5;
                materializationContext5 = new MaterializationContext(
                    ValueBuffer, 
                    queryContext.Context
                );
                instance5 = null;
                entry5 = queryContext.TryGetEntry(
                    key: Key: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.JsonEntityCustomNamingId PK, 
                    keyValues: new object[]{ namelessParameter{41}[0] }, 
                    throwOnNullKey: False, 
                    hasNullKey: hasNullKey5);
                !(hasNullKey5) ? entry5 != default(InternalEntityEntry) ? 
                {
                    entityType5 = entry5.EntityType;
                    return instance5 = (JsonOwnedCustomNameRoot)entry5.Entity;
                } : 
                {
                    ValueBuffer shadowValueBuffer5;
                    shadowValueBuffer5 = ValueBuffer;
                    entityType5 = EntityType: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot CLR Type: JsonOwnedCustomNameRoot Owned;
                    instance5 = 
                    {
                        JsonOwnedCustomNameRoot instance;
                        Utf8JsonReaderManager namelessParameter{44};
                        JsonTokenType tokenType;
                        JsonEnum namelessParameter{45};
                        string namelessParameter{46};
                        int namelessParameter{47};
                        List<JsonOwnedCustomNameBranch> namelessParameter{48};
                        JsonOwnedCustomNameBranch namelessParameter{49};
                        shadowValueBuffer5 = new ValueBuffer(new object[]{ namelessParameter{41}[0] });
                        namelessParameter{44} = new Utf8JsonReaderManager(namelessParameter{42});
                        tokenType = namelessParameter{44}.TokenType();
                        Loop(Break: done Continue: )
                        {
                            {
                                tokenType = namelessParameter{44}.MoveNext();
                                tokenType != EndObject ? switch (tokenType)
                                {
                                    case PropertyName: 
                                        namelessParameter{44}.ValueTextEquals(CustomEnum.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{44}.MoveNext();
                                            namelessParameter{45} = (JsonEnum)namelessParameter{44}.CurrentReader.GetInt32();
                                        } : namelessParameter{44}.ValueTextEquals(CustomName.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{44}.MoveNext();
                                            namelessParameter{46} = namelessParameter{44}.CurrentReader.GetString();
                                        } : namelessParameter{44}.ValueTextEquals(CustomNumber.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{44}.MoveNext();
                                            namelessParameter{47} = namelessParameter{44}.CurrentReader.GetInt32();
                                        } : namelessParameter{44}.ValueTextEquals(CustomOwnedCollectionBranch.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{44}.MoveNext();
                                            namelessParameter{44}.CaptureState();
                                            namelessParameter{48} = ShaperProcessingExpressionVisitor.MaterializeJsonEntityCollection2<JsonOwnedCustomNameBranch, List<JsonOwnedCustomNameBranch>>(
                                                queryContext: queryContext, 
                                                keyPropertyValues: namelessParameter{41}, 
                                                jsonReaderData: namelessParameter{42}, 
                                                navigation: Navigation: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch (List<JsonOwnedCustomNameBranch>) Collection ToDependent JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch, 
                                                innerShaper: (QueryContext queryContext, object[] namelessParameter{50}, JsonReaderData namelessParameter{51}) => 
                                                {
                                                    JsonOwnedCustomNameBranch namelessParameter{52};
                                                    return namelessParameter{52} = 
                                                    {
                                                        MaterializationContext materializationContext6;
                                                        IEntityType entityType6;
                                                        JsonOwnedCustomNameBranch instance6;
                                                        InternalEntityEntry entry6;
                                                        bool hasNullKey6;
                                                        materializationContext6 = new MaterializationContext(
                                                            ValueBuffer, 
                                                            queryContext.Context
                                                        );
                                                        instance6 = null;
                                                        entry6 = queryContext.TryGetEntry(
                                                            key: Key: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootJsonEntityCustomNamingId, JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.Id PK, 
                                                            keyValues: new object[]
                                                            { 
                                                                namelessParameter{50}[0], 
                                                                namelessParameter{50}[1] 
                                                            }, 
                                                            throwOnNullKey: False, 
                                                            hasNullKey: hasNullKey6);
                                                        !(hasNullKey6) ? entry6 != default(InternalEntityEntry) ? 
                                                        {
                                                            entityType6 = entry6.EntityType;
                                                            return instance6 = (JsonOwnedCustomNameBranch)entry6.Entity;
                                                        } : 
                                                        {
                                                            ValueBuffer shadowValueBuffer6;
                                                            shadowValueBuffer6 = ValueBuffer;
                                                            entityType6 = EntityType: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch CLR Type: JsonOwnedCustomNameBranch Owned;
                                                            instance6 = 
                                                            {
                                                                JsonOwnedCustomNameBranch instance;
                                                                Utf8JsonReaderManager namelessParameter{53};
                                                                JsonTokenType tokenType;
                                                                DateTime namelessParameter{54};
                                                                double namelessParameter{55};
                                                                shadowValueBuffer6 = new ValueBuffer(new object[]
                                                                { 
                                                                    namelessParameter{50}[0], 
                                                                    namelessParameter{50}[1] 
                                                                });
                                                                namelessParameter{53} = new Utf8JsonReaderManager(namelessParameter{51});
                                                                tokenType = namelessParameter{53}.TokenType();
                                                                Loop(Break: done Continue: )
                                                                {
                                                                    {
                                                                        tokenType = namelessParameter{53}.MoveNext();
                                                                        tokenType != EndObject ? switch (tokenType)
                                                                        {
                                                                            case PropertyName: 
                                                                                namelessParameter{53}.ValueTextEquals(CustomDate.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{53}.MoveNext();
                                                                                    namelessParameter{54} = namelessParameter{53}.CurrentReader.GetDateTime();
                                                                                } : namelessParameter{53}.ValueTextEquals(CustomFraction.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{53}.MoveNext();
                                                                                    namelessParameter{55} = namelessParameter{53}.CurrentReader.GetDouble();
                                                                                } : default(void)
                                                                            default: 
                                                                                namelessParameter{53}.Skip()
                                                                        }
                                                                         : Goto(break done)
                                                                        ;
                                                                    }}
                                                                namelessParameter{53}.CaptureState();
                                                                instance = new JsonOwnedCustomNameBranch();
                                                                instance.<Date>k__BackingField = namelessParameter{54};
                                                                instance.<Fraction>k__BackingField = namelessParameter{55};
                                                                return instance;
                                                            };
                                                            entry6 = entityType6 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                                                                entityType: entityType6, 
                                                                entity: instance6, 
                                                                valueBuffer: shadowValueBuffer6);
                                                            return instance6;
                                                        } : default(void);
                                                        return instance6;
                                                    };
                                                });
                                            namelessParameter{44} = new Utf8JsonReaderManager(namelessParameter{42});
                                        } : namelessParameter{44}.ValueTextEquals(CustomOwnedReferenceBranch.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{44}.MoveNext();
                                            namelessParameter{44}.CaptureState();
                                            namelessParameter{49} = ShaperProcessingExpressionVisitor.MaterializeJsonEntity2<JsonOwnedCustomNameBranch>(
                                                queryContext: queryContext, 
                                                keyPropertyValues: namelessParameter{41}, 
                                                jsonReaderData: namelessParameter{42}, 
                                                nullable: True, 
                                                shaper: (QueryContext queryContext, object[] namelessParameter{56}, JsonReaderData namelessParameter{57}) => 
                                                {
                                                    JsonOwnedCustomNameBranch namelessParameter{58};
                                                    return namelessParameter{58} = 
                                                    {
                                                        MaterializationContext materializationContext7;
                                                        IEntityType entityType7;
                                                        JsonOwnedCustomNameBranch instance7;
                                                        InternalEntityEntry entry7;
                                                        bool hasNullKey7;
                                                        materializationContext7 = new MaterializationContext(
                                                            ValueBuffer, 
                                                            queryContext.Context
                                                        );
                                                        instance7 = null;
                                                        entry7 = queryContext.TryGetEntry(
                                                            key: Key: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedReferenceBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootJsonEntityCustomNamingId PK, 
                                                            keyValues: new object[]{ namelessParameter{56}[0] }, 
                                                            throwOnNullKey: False, 
                                                            hasNullKey: hasNullKey7);
                                                        !(hasNullKey7) ? entry7 != default(InternalEntityEntry) ? 
                                                        {
                                                            entityType7 = entry7.EntityType;
                                                            return instance7 = (JsonOwnedCustomNameBranch)entry7.Entity;
                                                        } : 
                                                        {
                                                            ValueBuffer shadowValueBuffer7;
                                                            shadowValueBuffer7 = ValueBuffer;
                                                            entityType7 = EntityType: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedReferenceBranch#JsonOwnedCustomNameBranch CLR Type: JsonOwnedCustomNameBranch Owned;
                                                            instance7 = 
                                                            {
                                                                JsonOwnedCustomNameBranch instance;
                                                                Utf8JsonReaderManager namelessParameter{59};
                                                                JsonTokenType tokenType;
                                                                DateTime namelessParameter{60};
                                                                double namelessParameter{61};
                                                                shadowValueBuffer7 = new ValueBuffer(new object[]{ namelessParameter{56}[0] });
                                                                namelessParameter{59} = new Utf8JsonReaderManager(namelessParameter{57});
                                                                tokenType = namelessParameter{59}.TokenType();
                                                                Loop(Break: done Continue: )
                                                                {
                                                                    {
                                                                        tokenType = namelessParameter{59}.MoveNext();
                                                                        tokenType != EndObject ? switch (tokenType)
                                                                        {
                                                                            case PropertyName: 
                                                                                namelessParameter{59}.ValueTextEquals(CustomDate.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{59}.MoveNext();
                                                                                    namelessParameter{60} = namelessParameter{59}.CurrentReader.GetDateTime();
                                                                                } : namelessParameter{59}.ValueTextEquals(CustomFraction.EncodedUtf8Bytes) ? 
                                                                                {
                                                                                    namelessParameter{59}.MoveNext();
                                                                                    namelessParameter{61} = namelessParameter{59}.CurrentReader.GetDouble();
                                                                                } : default(void)
                                                                            default: 
                                                                                namelessParameter{59}.Skip()
                                                                        }
                                                                         : Goto(break done)
                                                                        ;
                                                                    }}
                                                                namelessParameter{59}.CaptureState();
                                                                instance = new JsonOwnedCustomNameBranch();
                                                                instance.<Date>k__BackingField = namelessParameter{60};
                                                                instance.<Fraction>k__BackingField = namelessParameter{61};
                                                                return instance;
                                                            };
                                                            entry7 = entityType7 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                                                                entityType: entityType7, 
                                                                entity: instance7, 
                                                                valueBuffer: shadowValueBuffer7);
                                                            return instance7;
                                                        } : default(void);
                                                        return instance7;
                                                    };
                                                });
                                            namelessParameter{44} = new Utf8JsonReaderManager(namelessParameter{42});
                                        } : default(void)
                                    default: 
                                        namelessParameter{44}.Skip()
                                }
                                 : Goto(break done)
                                ;
                            }}
                        namelessParameter{44}.CaptureState();
                        instance = new JsonOwnedCustomNameRoot();
                        instance.<Enum>k__BackingField = namelessParameter{45};
                        instance.<Name>k__BackingField = namelessParameter{46};
                        instance.<Number>k__BackingField = namelessParameter{47};
                        Invoke((JsonOwnedCustomNameRoot namelessParameter{62}, List<JsonOwnedCustomNameBranch> namelessParameter{63}) => 
                        {
                            return namelessParameter{62}.<OwnedCollectionBranch>k__BackingField = namelessParameter{63};
                        }, instance, namelessParameter{48});
                        Invoke((JsonOwnedCustomNameRoot namelessParameter{64}, JsonOwnedCustomNameBranch namelessParameter{65}) => 
                        {
                            return namelessParameter{64}.<OwnedReferenceBranch>k__BackingField = namelessParameter{65};
                        }, instance, namelessParameter{49});
                        return instance;
                    };
                    entry5 = entityType5 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                        entityType: entityType5, 
                        entity: instance5, 
                        valueBuffer: shadowValueBuffer5);
                    return instance5;
                } : default(void);
                return instance5;
            };
        }, 
        fixup: (JsonEntityCustomNaming namelessParameter{66}, JsonOwnedCustomNameRoot namelessParameter{67}) => 
        {
            return namelessParameter{66}.<OwnedReferenceRoot>k__BackingField = namelessParameter{67};
        });
    namelessParameter{13} = ShaperProcessingExpressionVisitor.MaterializeJsonEntityCollection2<JsonOwnedCustomNameBranch, List<JsonOwnedCustomNameBranch>>(
        queryContext: queryContext, 
        keyPropertyValues: namelessParameter{12}, 
        jsonReaderData: namelessParameter{10}, 
        navigation: Navigation: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch (List<JsonOwnedCustomNameBranch>) Collection ToDependent JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch, 
        innerShaper: (QueryContext queryContext, object[] namelessParameter{68}, JsonReaderData namelessParameter{69}) => 
        {
            JsonOwnedCustomNameBranch namelessParameter{70};
            return namelessParameter{70} = 
            {
                MaterializationContext materializationContext8;
                IEntityType entityType8;
                JsonOwnedCustomNameBranch instance8;
                InternalEntityEntry entry8;
                bool hasNullKey8;
                materializationContext8 = new MaterializationContext(
                    ValueBuffer, 
                    queryContext.Context
                );
                instance8 = null;
                entry8 = queryContext.TryGetEntry(
                    key: Key: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.JsonOwnedCustomNameRootJsonEntityCustomNamingId, JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch.Id PK, 
                    keyValues: new object[]
                    { 
                        namelessParameter{68}[0], 
                        namelessParameter{68}[1] 
                    }, 
                    throwOnNullKey: False, 
                    hasNullKey: hasNullKey8);
                !(hasNullKey8) ? entry8 != default(InternalEntityEntry) ? 
                {
                    entityType8 = entry8.EntityType;
                    return instance8 = (JsonOwnedCustomNameBranch)entry8.Entity;
                } : 
                {
                    ValueBuffer shadowValueBuffer8;
                    shadowValueBuffer8 = ValueBuffer;
                    entityType8 = EntityType: JsonEntityCustomNaming.OwnedReferenceRoot#JsonOwnedCustomNameRoot.OwnedCollectionBranch#JsonOwnedCustomNameBranch CLR Type: JsonOwnedCustomNameBranch Owned;
                    instance8 = 
                    {
                        JsonOwnedCustomNameBranch instance;
                        Utf8JsonReaderManager namelessParameter{71};
                        JsonTokenType tokenType;
                        DateTime namelessParameter{72};
                        double namelessParameter{73};
                        shadowValueBuffer8 = new ValueBuffer(new object[]
                        { 
                            namelessParameter{68}[0], 
                            namelessParameter{68}[1] 
                        });
                        namelessParameter{71} = new Utf8JsonReaderManager(namelessParameter{69});
                        tokenType = namelessParameter{71}.TokenType();
                        Loop(Break: done Continue: )
                        {
                            {
                                tokenType = namelessParameter{71}.MoveNext();
                                tokenType != EndObject ? switch (tokenType)
                                {
                                    case PropertyName: 
                                        namelessParameter{71}.ValueTextEquals(CustomDate.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{71}.MoveNext();
                                            namelessParameter{72} = namelessParameter{71}.CurrentReader.GetDateTime();
                                        } : namelessParameter{71}.ValueTextEquals(CustomFraction.EncodedUtf8Bytes) ? 
                                        {
                                            namelessParameter{71}.MoveNext();
                                            namelessParameter{73} = namelessParameter{71}.CurrentReader.GetDouble();
                                        } : default(void)
                                    default: 
                                        namelessParameter{71}.Skip()
                                }
                                 : Goto(break done)
                                ;
                            }}
                        namelessParameter{71}.CaptureState();
                        instance = new JsonOwnedCustomNameBranch();
                        instance.<Date>k__BackingField = namelessParameter{72};
                        instance.<Fraction>k__BackingField = namelessParameter{73};
                        return instance;
                    };
                    entry8 = entityType8 == default(IEntityType) ? default(InternalEntityEntry) : queryContext.StartTracking(
                        entityType: entityType8, 
                        entity: instance8, 
                        valueBuffer: shadowValueBuffer8);
                    return instance8;
                } : default(void);
                return instance8;
            };
        });
    return new { 
        root = namelessParameter{0}, 
        nested_collection = namelessParameter{13}
     };
}

maumar added a commit that referenced this issue Jun 29, 2023
Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #31159
Fixes #30993
maumar added a commit that referenced this issue Jun 29, 2023
Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #31159
Fixes #30993
maumar added a commit that referenced this issue Jun 30, 2023
Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #31159
Fixes #30993
@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 Jun 30, 2023
@maumar maumar added this to the 8.0.0 milestone Jun 30, 2023
maumar added a commit that referenced this issue Jul 1, 2023
…JsonReader/Utf8JsonWriter

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
maumar added a commit that referenced this issue Jul 1, 2023
…JsonReader/Utf8JsonWriter

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
maumar added a commit that referenced this issue Jul 6, 2023
…JsonReader/Utf8JsonWriter

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
maumar added a commit that referenced this issue Jul 7, 2023
…JsonReader/Utf8JsonWriter

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
maumar added a commit that referenced this issue Jul 10, 2023
…JsonReader/Utf8JsonWriter

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
maumar added a commit that referenced this issue Jul 11, 2023
…JsonReader/Utf8JsonWriter (#31160)

Using Utf8JsonReader to read JSON data rather than caching it using DOM. This should reduce allocations significantly. Tricky part is that entity materializers are build in a way that assumes we have random access to all the data we need. This is not the case here.
We read JSON data sequentially and can only do it once, and we don't know the order in which we get the data. This is somewhat problematic in case where entity takes argument in the constructor. Those could be at the very end of the JSON string, so we must read all the data before we can instantiate the object, and populate it's properties and do navigation fixup.
This requires us reading all the JSON data, store them in local variables, and only when we are done reading we instantiate the entity and populate all the properties with data stored in those variables. This adds some allocations (specifically navigations).

We also have to disable de-duplication logic - we can't always safely re-read the JSON string, and definitely can't start reading it from arbitrary position, so now we have to add JSON string for every aggregate projected, even if we already project it's parent.

Serialization implementation (i.e. Utf8JsonWriter) is pretty straighforward.

Also fix to #30993 - Query/Json: data corruption for tracking queries with nested json entities, then updating nested entities outside EF and re-querying

Fix is to recognize and modify shaper in case of tracking query, so that nav expansions are not skipped when parent entity is found in Change Tracker. This is necessary to fix alongside streaming, because now we throw exception from reader (unexpected token) if we don't process the entire stream correctly. Before it would be silently ignored apart from the edge case described in the bug.

Fixes #30604
Fixes #30993
@ajcvickers ajcvickers modified the milestones: 8.0.0, 8.0.0-preview7 Jul 20, 2023
@ajcvickers ajcvickers modified the milestones: 8.0.0-preview7, 8.0.0 Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-json area-query closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
2 participants