Skip to content

Commit

Permalink
Fix to #29355 - FromSqlRaw throws Exception when querying all objects…
Browse files Browse the repository at this point in the history
… that contain a certain string property in a json array column

Problem was that were not creating navigation bindings for JSON entities in the SelectExpression ctor which takes TableExpressionBase as argument. This would cause error in SharedTypeEntityExpandingExpressionVisitor which depends on those bindings to be present for entities mapped to JSON.

Fix is to use the same logic we use in the "main" SelectExpression ctor.

Fixes #29355
  • Loading branch information
maumar committed Oct 15, 2022
1 parent 7de5e80 commit f1d64aa
Showing 1 changed file with 54 additions and 46 deletions.
100 changes: 54 additions & 46 deletions src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,52 +440,7 @@ void GenerateNonHierarchyNonSplittingEntityType(ITableBase table, TableExpressio
}

var entityProjection = new EntityProjectionExpression(entityType, propertyExpressions);

foreach (var ownedJsonNavigation in GetAllNavigationsInHierarchy(entityType)
.Where(
n => n.ForeignKey.IsOwnership
&& n.TargetEntityType.IsMappedToJson()
&& n.ForeignKey.PrincipalToDependent == n))
{
var targetEntityType = ownedJsonNavigation.TargetEntityType;
var jsonColumnName = targetEntityType.GetContainerColumnName()!;
var jsonColumnTypeMapping = targetEntityType.GetContainerColumnTypeMapping()!;

var jsonColumn = new ConcreteColumnExpression(
jsonColumnName,
tableReferenceExpression,
jsonColumnTypeMapping.ClrType,
jsonColumnTypeMapping,
nullable: !ownedJsonNavigation.ForeignKey.IsRequiredDependent || ownedJsonNavigation.IsCollection);

// for json collections we need to skip ordinal key (which is always the last one)
// simple copy from parent is safe here, because we only do it at top level
// so there is no danger of multiple keys being synthesized (like we have in multi-level nav chains)
var keyPropertiesMap = new Dictionary<IProperty, ColumnExpression>();
var keyProperties = targetEntityType.FindPrimaryKey()!.Properties;
var keyPropertiesCount = ownedJsonNavigation.IsCollection
? keyProperties.Count - 1
: keyProperties.Count;

for (var i = 0; i < keyPropertiesCount; i++)
{
var correspondingParentKeyProperty = ownedJsonNavigation.ForeignKey.PrincipalKey.Properties[i];
keyPropertiesMap[keyProperties[i]] = propertyExpressions[correspondingParentKeyProperty];
}

var entityShaperExpression = new RelationalEntityShaperExpression(
targetEntityType,
new JsonQueryExpression(
targetEntityType,
jsonColumn,
keyPropertiesMap,
ownedJsonNavigation.ClrType,
ownedJsonNavigation.IsCollection),
!ownedJsonNavigation.ForeignKey.IsRequiredDependent);

entityProjection.AddNavigationBinding(ownedJsonNavigation, entityShaperExpression);
}

AddJsonNavigationBindings(entityType, entityProjection, propertyExpressions, tableReferenceExpression);
_projectionMapping[new ProjectionMember()] = entityProjection;

var primaryKey = entityType.FindPrimaryKey();
Expand Down Expand Up @@ -522,6 +477,7 @@ internal SelectExpression(IEntityType entityType, TableExpressionBase tableExpre
}

var entityProjection = new EntityProjectionExpression(entityType, propertyExpressions);
AddJsonNavigationBindings(entityType, entityProjection, propertyExpressions, tableReferenceExpression);
_projectionMapping[new ProjectionMember()] = entityProjection;

var primaryKey = entityType.FindPrimaryKey();
Expand All @@ -534,6 +490,58 @@ internal SelectExpression(IEntityType entityType, TableExpressionBase tableExpre
}
}

private void AddJsonNavigationBindings(
IEntityType entityType,
EntityProjectionExpression entityProjection,
Dictionary<IProperty, ColumnExpression> propertyExpressions,
TableReferenceExpression tableReferenceExpression)
{
foreach (var ownedJsonNavigation in GetAllNavigationsInHierarchy(entityType)
.Where(
n => n.ForeignKey.IsOwnership
&& n.TargetEntityType.IsMappedToJson()
&& n.ForeignKey.PrincipalToDependent == n))
{
var targetEntityType = ownedJsonNavigation.TargetEntityType;
var jsonColumnName = targetEntityType.GetContainerColumnName()!;
var jsonColumnTypeMapping = targetEntityType.GetContainerColumnTypeMapping()!;

var jsonColumn = new ConcreteColumnExpression(
jsonColumnName,
tableReferenceExpression,
jsonColumnTypeMapping.ClrType,
jsonColumnTypeMapping,
nullable: !ownedJsonNavigation.ForeignKey.IsRequiredDependent || ownedJsonNavigation.IsCollection);

// for json collections we need to skip ordinal key (which is always the last one)
// simple copy from parent is safe here, because we only do it at top level
// so there is no danger of multiple keys being synthesized (like we have in multi-level nav chains)
var keyPropertiesMap = new Dictionary<IProperty, ColumnExpression>();
var keyProperties = targetEntityType.FindPrimaryKey()!.Properties;
var keyPropertiesCount = ownedJsonNavigation.IsCollection
? keyProperties.Count - 1
: keyProperties.Count;

for (var i = 0; i < keyPropertiesCount; i++)
{
var correspondingParentKeyProperty = ownedJsonNavigation.ForeignKey.PrincipalKey.Properties[i];
keyPropertiesMap[keyProperties[i]] = propertyExpressions[correspondingParentKeyProperty];
}

var entityShaperExpression = new RelationalEntityShaperExpression(
targetEntityType,
new JsonQueryExpression(
targetEntityType,
jsonColumn,
keyPropertiesMap,
ownedJsonNavigation.ClrType,
ownedJsonNavigation.IsCollection),
!ownedJsonNavigation.ForeignKey.IsRequiredDependent);

entityProjection.AddNavigationBinding(ownedJsonNavigation, entityShaperExpression);
}
}

/// <summary>
/// The list of tags applied to this <see cref="SelectExpression" />.
/// </summary>
Expand Down

0 comments on commit f1d64aa

Please sign in to comment.