diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
index f2a6a01fe8d..a6c6ea9b389 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.cs
@@ -16,6 +16,8 @@ namespace Microsoft.EntityFrameworkCore.Query;
public partial class RelationalShapedQueryCompilingExpressionVisitor
{
+ private ShaperProcessingExpressionVisitor? _currentShaperProcessor;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
@@ -24,6 +26,8 @@ public partial class RelationalShapedQueryCompilingExpressionVisitor
///
public sealed partial class ShaperProcessingExpressionVisitor : ExpressionVisitor
{
+ private ShaperProcessingExpressionVisitor? _parentShaperProcessor;
+
///
/// Reading database values
///
@@ -297,25 +301,35 @@ public LambdaExpression ProcessRelationalGroupingResult(
out LambdaExpression? relatedDataLoaders,
ref int collectionId)
{
- _inline = true;
- keySelector = Lambda(
- Visit(relationalGroupByResultExpression.KeyShaper),
- QueryCompilationContext.QueryContextParameter,
- _dataReaderParameter);
+ _parentShaperProcessor = _parentVisitor._currentShaperProcessor;
+ _parentVisitor._currentShaperProcessor = this;
- keyIdentifier = Lambda(
- Visit(relationalGroupByResultExpression.KeyIdentifier),
- QueryCompilationContext.QueryContextParameter,
- _dataReaderParameter);
+ try
+ {
+ _inline = true;
+ keySelector = Lambda(
+ Visit(relationalGroupByResultExpression.KeyShaper),
+ QueryCompilationContext.QueryContextParameter,
+ _dataReaderParameter);
- _inline = false;
+ keyIdentifier = Lambda(
+ Visit(relationalGroupByResultExpression.KeyIdentifier),
+ QueryCompilationContext.QueryContextParameter,
+ _dataReaderParameter);
+
+ _inline = false;
- return ProcessShaper(
- relationalGroupByResultExpression.ElementShaper,
- out relationalCommandResolver!,
- out readerColumns,
- out relatedDataLoaders,
- ref collectionId);
+ return ProcessShaper(
+ relationalGroupByResultExpression.ElementShaper,
+ out relationalCommandResolver!,
+ out readerColumns,
+ out relatedDataLoaders,
+ ref collectionId);
+ }
+ finally
+ {
+ _parentVisitor._currentShaperProcessor = _parentShaperProcessor;
+ }
}
///
@@ -331,145 +345,155 @@ public LambdaExpression ProcessShaper(
out LambdaExpression? relatedDataLoaders,
ref int collectionId)
{
- relatedDataLoaders = null;
- _collectionId = collectionId;
+ _parentShaperProcessor = _parentVisitor._currentShaperProcessor;
+ _parentVisitor._currentShaperProcessor = this;
- if (_indexMapParameter != null)
+ try
{
- var result = Visit(shaperExpression);
- _expressions.Add(result);
- result = Block(_variables, _expressions);
-
- relationalCommandResolver = _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression);
- readerColumns = _readerColumns;
+ relatedDataLoaders = null;
+ _collectionId = collectionId;
- return Lambda(
- result,
- QueryCompilationContext.QueryContextParameter,
- _dataReaderParameter,
- _indexMapParameter);
- }
+ if (_indexMapParameter != null)
+ {
+ var result = Visit(shaperExpression);
+ _expressions.Add(result);
+ result = Block(_variables, _expressions);
- _containsCollectionMaterialization = new CollectionShaperFindingExpressionVisitor()
- .ContainsCollectionMaterialization(shaperExpression);
+ relationalCommandResolver = _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression);
+ readerColumns = _readerColumns;
- // for NoTrackingWithIdentityResolution we need to make sure we see JSON entities in the correct order
- // specifically, if we project JSON collection, it needs to be projected before any individual element from that collection
- // otherwise we store JSON entities in incorrect order in the Change Tracker, leading to possible data corruption
- // we only need to do this once, on top level
- // see issue #33073 for more context
- if (_queryStateManager && !_isTracking && collectionId == 0)
- {
- var jsonCorrectOrderOfEntitiesForChangeTrackerValidator =
- new JsonCorrectOrderOfEntitiesForChangeTrackerValidator(_selectExpression);
- jsonCorrectOrderOfEntitiesForChangeTrackerValidator.Validate(shaperExpression);
- }
+ return Lambda(
+ result,
+ QueryCompilationContext.QueryContextParameter,
+ _dataReaderParameter,
+ _indexMapParameter);
+ }
- if (!_containsCollectionMaterialization)
- {
- var result = Visit(shaperExpression);
- _expressions.AddRange(_includeExpressions);
- _expressions.AddRange(_jsonEntityExpressions);
- _expressions.Add(result);
- result = Block(_variables, _expressions);
-
- relationalCommandResolver = _generateCommandResolver
- ? _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression)
- : Constant(null, typeof(RelationalCommandResolver));
- readerColumns = _readerColumns;
-
- return Lambda(
- result,
- QueryCompilationContext.QueryContextParameter,
- _dataReaderParameter,
- _resultContextParameter,
- _resultCoordinatorParameter);
- }
- else
- {
- _valuesArrayExpression = MakeMemberAccess(_resultContextParameter, ResultContextValuesMemberInfo);
- _collectionPopulatingExpressions = [];
- _valuesArrayInitializers = [];
+ _containsCollectionMaterialization = new CollectionShaperFindingExpressionVisitor()
+ .ContainsCollectionMaterialization(shaperExpression);
- var result = Visit(shaperExpression);
+ // for NoTrackingWithIdentityResolution we need to make sure we see JSON entities in the correct order
+ // specifically, if we project JSON collection, it needs to be projected before any individual element from that collection
+ // otherwise we store JSON entities in incorrect order in the Change Tracker, leading to possible data corruption
+ // we only need to do this once, on top level
+ // see issue #33073 for more context
+ if (_queryStateManager && !_isTracking && collectionId == 0)
+ {
+ var jsonCorrectOrderOfEntitiesForChangeTrackerValidator =
+ new JsonCorrectOrderOfEntitiesForChangeTrackerValidator(_selectExpression);
+ jsonCorrectOrderOfEntitiesForChangeTrackerValidator.Validate(shaperExpression);
+ }
- var valueArrayInitializationExpression = Assign(
- _valuesArrayExpression, NewArrayInit(typeof(object), _valuesArrayInitializers));
+ if (!_containsCollectionMaterialization)
+ {
+ var result = Visit(shaperExpression);
+ _expressions.AddRange(_includeExpressions);
+ _expressions.AddRange(_jsonEntityExpressions);
+ _expressions.Add(result);
+ result = Block(_variables, _expressions);
+
+ relationalCommandResolver = _generateCommandResolver
+ ? _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression)
+ : Constant(null, typeof(RelationalCommandResolver));
+ readerColumns = _readerColumns;
+
+ return Lambda(
+ result,
+ QueryCompilationContext.QueryContextParameter,
+ _dataReaderParameter,
+ _resultContextParameter,
+ _resultCoordinatorParameter);
+ }
+ else
+ {
+ _valuesArrayExpression = MakeMemberAccess(_resultContextParameter, ResultContextValuesMemberInfo);
+ _collectionPopulatingExpressions = [];
+ _valuesArrayInitializers = [];
- _expressions.AddRange(_jsonEntityExpressions);
- _expressions.Add(valueArrayInitializationExpression);
- _expressions.AddRange(_includeExpressions);
+ var result = Visit(shaperExpression);
- if (_splitQuery)
- {
- _expressions.Add(Default(result.Type));
+ var valueArrayInitializationExpression = Assign(
+ _valuesArrayExpression, NewArrayInit(typeof(object), _valuesArrayInitializers));
- var initializationBlock = Block(_variables, _expressions);
- result = Condition(
- Equal(_valuesArrayExpression, Constant(null, typeof(object[]))),
- initializationBlock,
- result);
+ _expressions.AddRange(_jsonEntityExpressions);
+ _expressions.Add(valueArrayInitializationExpression);
+ _expressions.AddRange(_includeExpressions);
- if (_isAsync)
+ if (_splitQuery)
{
- var tasks = NewArrayInit(
- typeof(Func), _collectionPopulatingExpressions.Select(
- e => Lambda>(e)));
- relatedDataLoaders =
- Lambda>(
- Call(TaskAwaiterMethodInfo, tasks),
- QueryCompilationContext.QueryContextParameter,
- _executionStrategyParameter!,
- _resultCoordinatorParameter);
+ _expressions.Add(Default(result.Type));
+
+ var initializationBlock = Block(_variables, _expressions);
+ result = Condition(
+ Equal(_valuesArrayExpression, Constant(null, typeof(object[]))),
+ initializationBlock,
+ result);
+
+ if (_isAsync)
+ {
+ var tasks = NewArrayInit(
+ typeof(Func), _collectionPopulatingExpressions.Select(
+ e => Lambda>(e)));
+ relatedDataLoaders =
+ Lambda>(
+ Call(TaskAwaiterMethodInfo, tasks),
+ QueryCompilationContext.QueryContextParameter,
+ _executionStrategyParameter!,
+ _resultCoordinatorParameter);
+ }
+ else
+ {
+ relatedDataLoaders =
+ Lambda>(
+ Block(_collectionPopulatingExpressions),
+ QueryCompilationContext.QueryContextParameter,
+ _executionStrategyParameter!,
+ _resultCoordinatorParameter);
+ }
}
else
{
- relatedDataLoaders =
- Lambda>(
- Block(_collectionPopulatingExpressions),
- QueryCompilationContext.QueryContextParameter,
- _executionStrategyParameter!,
- _resultCoordinatorParameter);
- }
- }
- else
- {
- var initializationBlock = Block(_variables, _expressions);
+ var initializationBlock = Block(_variables, _expressions);
- var conditionalMaterializationExpressions = new List
+ var conditionalMaterializationExpressions = new List
{
IfThen(
Equal(_valuesArrayExpression, Constant(null, typeof(object[]))),
initializationBlock)
};
- conditionalMaterializationExpressions.AddRange(_collectionPopulatingExpressions);
+ conditionalMaterializationExpressions.AddRange(_collectionPopulatingExpressions);
- conditionalMaterializationExpressions.Add(
- Condition(
- IsTrue(
- MakeMemberAccess(
- _resultCoordinatorParameter, SingleQueryResultCoordinatorResultReadyMemberInfo)),
- result,
- Default(result.Type)));
+ conditionalMaterializationExpressions.Add(
+ Condition(
+ IsTrue(
+ MakeMemberAccess(
+ _resultCoordinatorParameter, SingleQueryResultCoordinatorResultReadyMemberInfo)),
+ result,
+ Default(result.Type)));
- result = Block(conditionalMaterializationExpressions);
- }
+ result = Block(conditionalMaterializationExpressions);
+ }
- relationalCommandResolver = _generateCommandResolver
- ? _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression)
- : Constant(null, typeof(RelationalCommandCache));
- ;
- readerColumns = _readerColumns;
+ relationalCommandResolver = _generateCommandResolver
+ ? _parentVisitor.CreateRelationalCommandResolverExpression(_selectExpression)
+ : Constant(null, typeof(RelationalCommandCache));
+ ;
+ readerColumns = _readerColumns;
- collectionId = _collectionId;
+ collectionId = _collectionId;
- return Lambda(
- result,
- QueryCompilationContext.QueryContextParameter,
- _dataReaderParameter,
- _resultContextParameter,
- _resultCoordinatorParameter);
+ return Lambda(
+ result,
+ QueryCompilationContext.QueryContextParameter,
+ _dataReaderParameter,
+ _resultContextParameter,
+ _resultCoordinatorParameter);
+ }
+ }
+ finally
+ {
+ _parentVisitor._currentShaperProcessor = _parentShaperProcessor;
}
}
@@ -575,9 +599,9 @@ protected override Expression VisitExtension(Expression extensionExpression)
switch (extensionExpression)
{
case RelationalStructuralTypeShaperExpression
- {
- ValueBufferExpression: ProjectionBindingExpression projectionBindingExpression
- } shaper
+ {
+ ValueBufferExpression: ProjectionBindingExpression projectionBindingExpression
+ } shaper
when !_inline:
{
// we can't cache ProjectionBindingExpression results for non-tracking queries
@@ -696,30 +720,6 @@ protected override Expression VisitExtension(Expression extensionExpression)
entityParameter,
shaper.Type);
- if (GetProjectionIndex(projectionBindingExpression) is IDictionary propertyMap)
- {
- foreach (var (property, projectionIndex) in propertyMap)
- {
- if (property is IComplexProperty { ComplexType: var complexType } complexProperty
- && complexType.IsMappedToJson())
- {
- var jsonReaderDataVariable = GenerateJsonReader(projectionIndex, complexType);
-
- var shaperResult = CreateJsonShapers(
- complexType,
- nullable: true, // TODO
- jsonReaderDataVariable,
- keyValuesParameter: null!, // TODO
- containerEntityExpression: entityParameter,
- relationship: complexProperty);
-
- var visitedShaperResult = Visit(shaperResult);
-
- _includeExpressions.Add(visitedShaperResult);
- }
- }
- }
-
break;
}
}
@@ -2473,7 +2473,42 @@ private bool IsPropertyAssignment(
}
}
- private ParameterExpression GenerateJsonReader(int jsonColumnIndex, ITypeBase structuralType)
+ ///
+ /// Injects JSON shaper code for JSON-mapped complex properties.
+ ///
+ internal void ProcessTopLevelComplexJsonProperties(
+ StructuralTypeShaperExpression shaper,
+ ParameterExpression instanceVariable,
+ List expressions)
+ {
+ // Note that the following processes only top-level complex properties (where the projection is a dictionary of properties to their projection index).
+ // For nested JSON types, CreateJsonShapers calls itself recursively.
+ if (shaper is RelationalStructuralTypeShaperExpression { ValueBufferExpression: ProjectionBindingExpression projectionBindingExpression }
+ && GetProjectionIndex(projectionBindingExpression) is Dictionary propertyMap)
+ {
+ foreach (var (property, projectionIndex) in propertyMap)
+ {
+ if (property is IComplexProperty { ComplexType: var complexType } complexProperty
+ && complexType.IsMappedToJson())
+ {
+ var jsonReaderDataVariable = GenerateJsonReader(projectionIndex, complexType);
+
+ var shaperResult = CreateJsonShapers(
+ complexType,
+ nullable: shaper.IsNullable || complexProperty.IsNullable,
+ jsonReaderDataVariable,
+ keyValuesParameter: null, // For owned entities only
+ containerEntityExpression: instanceVariable,
+ relationship: complexProperty);
+
+ var visitedShaperResult = Visit(shaperResult);
+ expressions.Add(visitedShaperResult);
+ }
+ }
+ }
+ }
+
+ internal ParameterExpression GenerateJsonReader(int jsonColumnIndex, ITypeBase structuralType)
{
Check.DebugAssert(structuralType.IsMappedToJson());
diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
index dbbe1c92358..1c97795b66a 100644
--- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.cs
@@ -484,6 +484,27 @@ private static Expression CreateReaderColumnsExpression(
return result;
}
+ ///
+ /// Called after a structural type is materialized, but before it's handed off to the change tracker.
+ /// Here we inject the JSON shapers for any complex JSON properties the type has.
+ ///
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public override void AddStructuralTypeInitialization(
+ StructuralTypeShaperExpression shaper,
+ ParameterExpression instanceVariable,
+ List variables,
+ List expressions)
+ {
+ Check.DebugAssert(_currentShaperProcessor is not null);
+
+ _currentShaperProcessor.ProcessTopLevelComplexJsonProperties(shaper, instanceVariable, expressions);
+ }
+
private Expression CreateRelationalCommandResolverExpression(Expression queryExpression)
{
// In the regular case, we generate code that accesses the RelationalCommandCache (which invokes the 2nd part of the
diff --git a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs
index 3fec2e421c5..2745b19b62d 100644
--- a/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs
+++ b/src/EFCore/Query/Internal/StructuralTypeMaterializerSource.cs
@@ -140,7 +140,7 @@ IServiceProperty serviceProperty
=> serviceProperty.ParameterBinding.BindToParameter(bindingInfo),
IComplexProperty { IsCollection: true } complexProperty
- => Expression.Default(complexProperty.ClrType), // Initialize collections to null, they'll be populated separately
+ => Default(complexProperty.ClrType), // Initialize collections to null, they'll be populated separately
IComplexProperty complexProperty
=> CreateMaterializeExpression(
diff --git a/src/EFCore/Query/LiftableConstantExpressionHelpers.cs b/src/EFCore/Query/LiftableConstantExpressionHelpers.cs
index a372036b624..8c5a8d81522 100644
--- a/src/EFCore/Query/LiftableConstantExpressionHelpers.cs
+++ b/src/EFCore/Query/LiftableConstantExpressionHelpers.cs
@@ -150,7 +150,7 @@ public static Expression BuildMemberAccessForEntityOrComplexType(
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public static Expression> BuildMemberAccessLambdaForEntityOrComplexType(
+ public static Expression> BuildMemberAccessLambdaForStructuralType(
ITypeBase type)
{
var prm = Parameter(typeof(MaterializerLiftableConstantContext));
diff --git a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs
index bec097af0b9..bf13c3e75c8 100644
--- a/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs
+++ b/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs
@@ -55,6 +55,7 @@ protected ShapedQueryCompilingExpressionVisitor(
_structuralTypeMaterializerInjector =
new StructuralTypeMaterializerInjector(
+ this,
dependencies.EntityMaterializerSource,
dependencies.LiftableConstantFactory,
queryCompilationContext.QueryTrackingBehavior,
@@ -233,12 +234,12 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio
{
{ Value: IEntityType entityTypeValue } => liftableConstantFactory.CreateLiftableConstant(
constantExpression.Value,
- LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForEntityOrComplexType(entityTypeValue),
+ LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForStructuralType(entityTypeValue),
entityTypeValue.ShortName() + "EntityType",
constantExpression.Type),
{ Value: IComplexType complexTypeValue } => liftableConstantFactory.CreateLiftableConstant(
constantExpression.Value,
- LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForEntityOrComplexType(complexTypeValue),
+ LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForStructuralType(complexTypeValue),
complexTypeValue.ShortName() + "ComplexType",
constantExpression.Type),
{ Value: IProperty propertyValue } => liftableConstantFactory.CreateLiftableConstant(
@@ -361,8 +362,20 @@ protected override Expression VisitExtension(Expression extensionExpression)
}
}
+ ///
+ /// Called after a structural type is materialized, but before it's handed off to the change tracker.
+ ///
+ public virtual void AddStructuralTypeInitialization(
+ StructuralTypeShaperExpression shaper,
+ ParameterExpression instanceVariable,
+ List variables,
+ List expressions)
+ {
+ }
+
private sealed class StructuralTypeMaterializerInjector(
- IStructuralTypeMaterializerSource entityMaterializerSource,
+ ShapedQueryCompilingExpressionVisitor shapedQueryCompiler,
+ IStructuralTypeMaterializerSource materializerSource,
ILiftableConstantFactory liftableConstantFactory,
QueryTrackingBehavior queryTrackingBehavior,
bool supportsPrecompiledQuery)
@@ -426,10 +439,10 @@ bool ContainsOwner(IEntityType? owner)
protected override Expression VisitExtension(Expression extensionExpression)
=> extensionExpression is StructuralTypeShaperExpression shaper
- ? ProcessEntityShaper(shaper)
+ ? ProcessStructuralTypeShaper(shaper)
: base.VisitExtension(extensionExpression);
- private Expression ProcessEntityShaper(StructuralTypeShaperExpression shaper)
+ private Expression ProcessStructuralTypeShaper(StructuralTypeShaperExpression shaper)
{
_currentEntityIndex++;
@@ -565,7 +578,7 @@ private Expression ProcessEntityShaper(StructuralTypeShaperExpression shaper)
supportsPrecompiledQuery
? liftableConstantFactory.CreateLiftableConstant(
typeBase,
- LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForEntityOrComplexType(typeBase),
+ LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForStructuralType(typeBase),
typeBase.Name + "EntityType",
typeof(IEntityType))
: Constant(typeBase),
@@ -606,7 +619,7 @@ private Expression MaterializeEntity(
ParameterExpression instanceVariable,
ParameterExpression? entryVariable)
{
- var typeBase = shaper.StructuralType;
+ var structuralType = shaper.StructuralType;
var expressions = new List();
var variables = new List();
@@ -626,7 +639,7 @@ private Expression MaterializeEntity(
typeof(ISnapshot))
: Constant(Snapshot.Empty, typeof(ISnapshot))));
- var returnType = typeBase.ClrType;
+ var returnType = structuralType.ClrType;
var valueBufferExpression = Call(materializationContextVariable, MaterializationContext.GetValueBufferMethod);
var materializationConditionBody = ReplacingExpressionVisitor.Replace(
@@ -637,23 +650,23 @@ private Expression MaterializeEntity(
var expressionContext = (returnType, materializationContextVariable, concreteEntityTypeVariable, shadowValuesVariable);
expressions.Add(Assign(concreteEntityTypeVariable, materializationConditionBody));
- var (primaryKey, concreteEntityTypes) = typeBase is IEntityType entityType
+ var (primaryKey, concreteStructuralTypes) = structuralType is IEntityType entityType
? (entityType.FindPrimaryKey(), entityType.GetConcreteDerivedTypesInclusive().Cast().ToArray())
- : (null, [typeBase]);
+ : (null, [structuralType]);
- var switchCases = new SwitchCase[concreteEntityTypes.Length];
- for (var i = 0; i < concreteEntityTypes.Length; i++)
+ var switchCases = new SwitchCase[concreteStructuralTypes.Length];
+ for (var i = 0; i < concreteStructuralTypes.Length; i++)
{
- var concreteEntityType = concreteEntityTypes[i];
+ var concreteStructuralType = concreteStructuralTypes[i];
switchCases[i] = SwitchCase(
- CreateFullMaterializeExpression(concreteEntityTypes[i], expressionContext),
+ CreateFullMaterializeExpression(concreteStructuralTypes[i], expressionContext),
supportsPrecompiledQuery
? liftableConstantFactory.CreateLiftableConstant(
- concreteEntityTypes[i],
- LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForEntityOrComplexType(concreteEntityType),
- concreteEntityType.ShortName() + (typeBase is IEntityType ? "EntityType" : "ComplexType"),
- typeBase is IEntityType ? typeof(IEntityType) : typeof(IComplexType))
- : Constant(concreteEntityTypes[i], typeBase is IEntityType ? typeof(IEntityType) : typeof(IComplexType)));
+ concreteStructuralTypes[i],
+ LiftableConstantExpressionHelpers.BuildMemberAccessLambdaForStructuralType(concreteStructuralType),
+ concreteStructuralType.ShortName() + (structuralType is IEntityType ? "EntityType" : "ComplexType"),
+ structuralType is IEntityType ? typeof(IEntityType) : typeof(IComplexType))
+ : Constant(concreteStructuralTypes[i], structuralType is IEntityType ? typeof(IEntityType) : typeof(IComplexType)));
}
var materializationExpression = Switch(
@@ -663,9 +676,11 @@ private Expression MaterializeEntity(
expressions.Add(Assign(instanceVariable, materializationExpression));
+ shapedQueryCompiler.AddStructuralTypeInitialization(shaper, instanceVariable, variables, expressions);
+
if (_queryStateManager && primaryKey is not null)
{
- if (typeBase is IEntityType entityType2)
+ if (structuralType is IEntityType entityType2)
{
foreach (var et in entityType2.GetAllBaseTypes().Concat(entityType2.GetDerivedTypesInclusive()))
{
@@ -696,7 +711,7 @@ private Expression MaterializeEntity(
}
private BlockExpression CreateFullMaterializeExpression(
- ITypeBase concreteTypeBase,
+ ITypeBase concreteStructuralType,
(Type ReturnType,
ParameterExpression MaterializationContextVariable,
ParameterExpression ConcreteEntityTypeVariable,
@@ -709,14 +724,14 @@ private BlockExpression CreateFullMaterializeExpression(
var blockExpressions = new List(2);
- var materializer = entityMaterializerSource
+ var materializer = materializerSource
.CreateMaterializeExpression(
new StructuralTypeMaterializerSourceParameters(
- concreteTypeBase, "instance", queryTrackingBehavior), materializationContextVariable);
+ concreteStructuralType, "instance", queryTrackingBehavior), materializationContextVariable);
// TODO: Properly support shadow properties for complex types #35613
if (_queryStateManager
- && concreteTypeBase is IRuntimeEntityType { ShadowPropertyCount: > 0 } runtimeEntityType)
+ && concreteStructuralType is IRuntimeEntityType { ShadowPropertyCount: > 0 } runtimeEntityType)
{
var valueBufferExpression = Call(
materializationContextVariable, MaterializationContext.GetValueBufferMethod);
diff --git a/test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs
index 247a5fb50c0..d951a771d9e 100644
--- a/test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Update/ComplexCollectionJsonUpdateTestBase.cs
@@ -11,7 +11,7 @@ public abstract class ComplexCollectionJsonUpdateTestBase(TFixture fix
protected ComplexCollectionJsonContext CreateContext()
=> (ComplexCollectionJsonContext)Fixture.CreateContext();
- [ConditionalFact(Skip = "Issue #36433")]
+ [ConditionalFact]
public virtual Task Add_element_to_complex_collection_mapped_to_json()
=> TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
@@ -26,17 +26,10 @@ public virtual Task Add_element_to_complex_collection_mapped_to_json()
company.Contacts!.Add(new Contact { Name = "New Contact", PhoneNumbers = ["555-0000"] });
- Assert.Equal("""
-CompanyWithComplexCollections {Id: 1} Unchanged
- Id: 1 PK
- Name: 'Test Company'
- Contacts (Complex: List)
- Department (Complex: Department)
- Budget: 10000.00
- Name: 'Initial Department'
- Employees (Complex: List)
-
-""", context.ChangeTracker.DebugView.LongView);
+ Assert.Contains("Contacts (Complex: List)", context.ChangeTracker.DebugView.LongView);
+ Assert.Contains("Department (Complex: Department)", context.ChangeTracker.DebugView.LongView);
+ Assert.Contains("Name: 'Initial Department'", context.ChangeTracker.DebugView.LongView);
+ Assert.Contains("Employees (Complex: List)", context.ChangeTracker.DebugView.LongView);
ClearLog();
await context.SaveChangesAsync();
@@ -126,7 +119,7 @@ public virtual Task Move_elements_in_complex_collection_mapped_to_json()
}
});
- [ConditionalFact(Skip = "Issue #36433")]
+ [ConditionalFact]
public virtual Task Change_complex_collection_mapped_to_json_to_null_and_to_empty()
=> TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
@@ -354,7 +347,7 @@ public virtual Task Modify_nested_complex_property_in_complex_collection_mapped_
}
});
- [ConditionalFact(Skip = "Issue #36433")]
+ [ConditionalFact]
public virtual Task Set_complex_collection_to_null_mapped_to_json()
=> TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
@@ -499,7 +492,7 @@ public virtual Task Complex_collection_with_empty_nested_collections_mapped_to_j
}
});
- [ConditionalFact(Skip = "Issue #36433")]
+ [ConditionalFact]
public virtual Task Set_complex_property_mapped_to_json_to_null()
=> TestHelpers.ExecuteWithStrategyInTransactionAsync(
CreateContext,
diff --git a/test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs
index 8fc1b5fb1a8..0ad7a4e3f29 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Update/ComplexCollectionJsonUpdateSqlServerTest.cs
@@ -33,15 +33,13 @@ public override async Task Remove_element_from_complex_collection_mapped_to_json
AssertSql(
"""
@p0='[{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 66)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -52,15 +50,13 @@ public override async Task Modify_element_in_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"First Contact - Modified","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 141)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -71,15 +67,13 @@ public override async Task Move_elements_in_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]},{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -117,16 +111,14 @@ public override async Task Complex_collection_with_nested_complex_type_mapped_to
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"John Doe","PhoneNumbers":["555-1234","555-5678"],"Address":{"City":"Seattle","Country":"USA","PostalCode":"98101","Street":"123 Main St"}},{"Name":"Jane Smith","PhoneNumbers":["555-9876"],"Address":{"City":"Portland","Country":"USA","PostalCode":"97201","Street":"456 Oak Ave"}}]' (Nullable = false) (Size = 289)
-@p3='1'
+@p0='[{"Name":"John Doe","PhoneNumbers":["555-1234","555-5678"],"Address":{"City":"Seattle","Country":"USA","PostalCode":"98101","Street":"123 Main St"}},{"Name":"Jane Smith","PhoneNumbers":["555-9876"],"Address":{"City":"Portland","Country":"USA","PostalCode":"97201","Street":"456 Oak Ave"}}]' (Nullable = false) (Size = 289)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Employees] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -138,14 +130,13 @@ public override async Task Modify_multiple_complex_properties_mapped_to_json()
"""
@p0='[{"Name":"Contact 1","PhoneNumbers":["555-1111"]}]' (Nullable = false) (Size = 50)
@p1='{"Budget":50000.00,"Name":"Department A"}' (Nullable = false) (Size = 41)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p2='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p2;
""");
}
@@ -156,15 +147,13 @@ public override async Task Clear_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[]' (Nullable = false) (Size = 2)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -175,15 +164,13 @@ public override async Task Replace_entire_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"Replacement Contact 1","PhoneNumbers":["999-1111"]},{"Name":"Replacement Contact 2","PhoneNumbers":["999-2222","999-3333"]}]' (Nullable = false) (Size = 134)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Contacts] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -193,16 +180,14 @@ public override async Task Add_element_to_nested_complex_collection_mapped_to_js
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001","555-9999"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 163)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001","555-9999"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 163)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Employees] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -212,16 +197,14 @@ public override async Task Modify_nested_complex_property_in_complex_collection_
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Modified City","Country":"USA","PostalCode":"99999","Street":"100 First St"}}]' (Nullable = false) (Size = 153)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Modified City","Country":"USA","PostalCode":"99999","Street":"100 First St"}}]' (Nullable = false) (Size = 153)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Employees] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -265,16 +248,14 @@ public override async Task Replace_complex_collection_element_mapped_to_json()
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Replacement Employee","PhoneNumbers":["555-7777","555-8888"],"Address":{"City":"Replace City","Country":"Canada","PostalCode":"54321","Street":"789 Replace St"}}]' (Nullable = false) (Size = 172)
-@p3='1'
+@p0='[{"Name":"Replacement Employee","PhoneNumbers":["555-7777","555-8888"],"Address":{"City":"Replace City","Country":"Canada","PostalCode":"54321","Street":"789 Replace St"}}]' (Nullable = false) (Size = 172)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Employees] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -284,16 +265,14 @@ public override async Task Complex_collection_with_empty_nested_collections_mapp
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":10000.00,"Name":"Initial Department"}' (Nullable = false) (Size = 47)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}},{"Name":"Employee No Phone","PhoneNumbers":[],"Address":{"City":"Quiet City","Country":"USA","PostalCode":"00000","Street":"456 No Phone St"}}]' (Nullable = false) (Size = 295)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}},{"Name":"Employee No Phone","PhoneNumbers":[],"Address":{"City":"Quiet City","Country":"USA","PostalCode":"00000","Street":"456 No Phone St"}}]' (Nullable = false) (Size = 295)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Employees] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
@@ -337,16 +316,14 @@ public override async Task Replace_complex_property_mapped_to_json()
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":99999.99,"Name":"Replacement Department"}' (Nullable = false) (Size = 51)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p0='{"Budget":99999.99,"Name":"Replacement Department"}' (Nullable = false) (Size = 51)
+@p1='1'
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
-UPDATE [Companies] SET [Contacts] = @p0, [Department] = @p1, [Employees] = @p2
+UPDATE [Companies] SET [Department] = @p0
OUTPUT 1
-WHERE [Id] = @p3;
+WHERE [Id] = @p1;
""");
}
diff --git a/test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs
index db11e377884..0b4491b38c0 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Update/ComplexCollectionJsonUpdateSqliteTest.cs
@@ -31,12 +31,10 @@ public override async Task Remove_element_from_complex_collection_mapped_to_json
AssertSql(
"""
@p0='[{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 66)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -48,12 +46,10 @@ public override async Task Modify_element_in_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"First Contact - Modified","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 141)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -65,12 +61,10 @@ public override async Task Move_elements_in_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]},{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -105,13 +99,11 @@ public override async Task Complex_collection_with_nested_complex_type_mapped_to
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"John Doe","PhoneNumbers":["555-1234","555-5678"],"Address":{"City":"Seattle","Country":"USA","PostalCode":"98101","Street":"123 Main St"}},{"Name":"Jane Smith","PhoneNumbers":["555-9876"],"Address":{"City":"Portland","Country":"USA","PostalCode":"97201","Street":"456 Oak Ave"}}]' (Nullable = false) (Size = 289)
-@p3='1'
+@p0='[{"Name":"John Doe","PhoneNumbers":["555-1234","555-5678"],"Address":{"City":"Seattle","Country":"USA","PostalCode":"98101","Street":"123 Main St"}},{"Name":"Jane Smith","PhoneNumbers":["555-9876"],"Address":{"City":"Portland","Country":"USA","PostalCode":"97201","Street":"456 Oak Ave"}}]' (Nullable = false) (Size = 289)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Employees" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -124,11 +116,10 @@ public override async Task Modify_multiple_complex_properties_mapped_to_json()
"""
@p0='[{"Name":"Contact 1","PhoneNumbers":["555-1111"]}]' (Nullable = false) (Size = 50)
@p1='{"Budget":"50000.0","Name":"Department A"}' (Nullable = false) (Size = 42)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p2='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1
+WHERE "Id" = @p2
RETURNING 1;
""");
}
@@ -140,12 +131,10 @@ public override async Task Clear_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[]' (Nullable = false) (Size = 2)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -157,12 +146,10 @@ public override async Task Replace_entire_complex_collection_mapped_to_json()
AssertSql(
"""
@p0='[{"Name":"Replacement Contact 1","PhoneNumbers":["999-1111"]},{"Name":"Replacement Contact 2","PhoneNumbers":["999-2222","999-3333"]}]' (Nullable = false) (Size = 134)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Contacts" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -173,13 +160,11 @@ public override async Task Add_element_to_nested_complex_collection_mapped_to_js
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001","555-9999"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 163)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001","555-9999"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 163)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Employees" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -190,13 +175,11 @@ public override async Task Modify_nested_complex_property_in_complex_collection_
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Modified City","Country":"USA","PostalCode":"99999","Street":"100 First St"}}]' (Nullable = false) (Size = 153)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Modified City","Country":"USA","PostalCode":"99999","Street":"100 First St"}}]' (Nullable = false) (Size = 153)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Employees" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -237,13 +220,11 @@ public override async Task Replace_complex_collection_element_mapped_to_json()
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Replacement Employee","PhoneNumbers":["555-7777","555-8888"],"Address":{"City":"Replace City","Country":"Canada","PostalCode":"54321","Street":"789 Replace St"}}]' (Nullable = false) (Size = 172)
-@p3='1'
+@p0='[{"Name":"Replacement Employee","PhoneNumbers":["555-7777","555-8888"],"Address":{"City":"Replace City","Country":"Canada","PostalCode":"54321","Street":"789 Replace St"}}]' (Nullable = false) (Size = 172)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Employees" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -254,13 +235,11 @@ public override async Task Complex_collection_with_empty_nested_collections_mapp
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"10000.0","Name":"Initial Department"}' (Nullable = false) (Size = 48)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}},{"Name":"Employee No Phone","PhoneNumbers":[],"Address":{"City":"Quiet City","Country":"USA","PostalCode":"00000","Street":"456 No Phone St"}}]' (Nullable = false) (Size = 295)
-@p3='1'
+@p0='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}},{"Name":"Employee No Phone","PhoneNumbers":[],"Address":{"City":"Quiet City","Country":"USA","PostalCode":"00000","Street":"456 No Phone St"}}]' (Nullable = false) (Size = 295)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Employees" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}
@@ -301,13 +280,11 @@ public override async Task Replace_complex_property_mapped_to_json()
AssertSql(
"""
-@p0='[{"Name":"First Contact","PhoneNumbers":["555-1234","555-5678"]},{"Name":"Second Contact","PhoneNumbers":["555-9876","555-5432"]}]' (Nullable = false) (Size = 130)
-@p1='{"Budget":"99999.99","Name":"Replacement Department"}' (Nullable = false) (Size = 53)
-@p2='[{"Name":"Initial Employee","PhoneNumbers":["555-0001"],"Address":{"City":"Initial City","Country":"USA","PostalCode":"00001","Street":"100 First St"}}]' (Nullable = false) (Size = 152)
-@p3='1'
+@p0='{"Budget":"99999.99","Name":"Replacement Department"}' (Nullable = false) (Size = 53)
+@p1='1'
-UPDATE "Companies" SET "Contacts" = @p0, "Department" = @p1, "Employees" = @p2
-WHERE "Id" = @p3
+UPDATE "Companies" SET "Department" = @p0
+WHERE "Id" = @p1
RETURNING 1;
""");
}