Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,6 @@ static void ValidateType(ITypeBase typeBase, IDiagnosticsLogger<DbLoggerCategory

foreach (var complexProperty in typeBase.GetDeclaredComplexProperties())
{
if (complexProperty.IsCollection)
{
throw new InvalidOperationException(
CosmosStrings.ComplexTypeCollectionsNotSupported(
complexProperty.ComplexType.ShortName(),
complexProperty.Name));
}

ValidateType(complexProperty.ComplexType, logger);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,20 +352,45 @@ UnaryExpression unaryExpression

Expression NullSafeUpdate(Expression? expression)
{
// @TODO: This code appears to be causing multi matiralization
if (expression?.Type.IsNullableType() == true && !memberExpression.Type.IsNullableType() && memberExpression.Expression is MemberExpression innerMember && innerMember.Type.IsNullableValueType() == true && memberExpression.Member.Name == nameof(Nullable<>.Value) && expression is not null)
{
var nullCheck = Expression.Not(
Expression.Property(expression, nameof(Nullable<>.HasValue)));
return Expression.Condition(
nullCheck,
Expression.Default(memberExpression.Type),
Expression.Property(expression, nameof(Nullable<>.Value)));
}

Expression updatedMemberExpression = memberExpression.Update(
expression != null ? MatchTypes(expression, memberExpression.Expression!.Type) : expression);
expression != null ? MatchTypes(expression, memberExpression.Expression!.Type) : expression);

if (expression?.Type.IsNullableType() == true)
{
var nullableReturnType = memberExpression.Type.MakeNullable();
if (!memberExpression.Type.IsNullableType())

if (!updatedMemberExpression.Type.IsNullableType())
{
updatedMemberExpression = Expression.Convert(updatedMemberExpression, nullableReturnType);
}

Expression nullCheck;
if (expression.Type.IsNullableValueType())
{
// For Nullable<T>, use HasValue property instead of equality comparison
// to avoid issues with value types that don't define the == operator
nullCheck = Expression.Not(
Expression.Property(expression, nameof(Nullable<>.HasValue)));
}
else
{
nullCheck = Expression.Equal(expression, Expression.Default(expression.Type));
}

updatedMemberExpression = Expression.Condition(
Expression.Equal(expression, Expression.Default(expression.Type)),
Expression.Constant(null, nullableReturnType),
nullCheck,
Expression.Default(nullableReturnType),
updatedMemberExpression);
}

Expand Down Expand Up @@ -639,8 +664,21 @@ UnaryExpression unaryExpression
updatedMethodCallExpression = Expression.Convert(updatedMethodCallExpression, nullableReturnType);
}

Expression nullCheck;
if (@object.Type.IsNullableValueType())
{
// For Nullable<T>, use HasValue property instead of equality comparison
// to avoid issues with value types that don't define the == operator
nullCheck = Expression.Not(
Expression.Property(@object, nameof(Nullable<>.HasValue)));
}
else
{
nullCheck = Expression.Equal(@object, Expression.Constant(null, @object.Type));
}

return Expression.Condition(
Expression.Equal(@object, Expression.Default(@object.Type)),
nullCheck,
Expression.Constant(null, nullableReturnType),
updatedMethodCallExpression);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,15 +340,15 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis
return CreateShapedQueryExpression(entityType, selectExpression);
}

private ShapedQueryExpression? CreateShapedQueryExpression(IEntityType entityType, SelectExpression queryExpression)
private ShapedQueryExpression? CreateShapedQueryExpression(ITypeBase structuralType, SelectExpression queryExpression)
{
if (!entityType.IsOwned())
if (structuralType is IEntityType entityType && !entityType.IsOwned())
{
var existingEntityType = _queryCompilationContext.RootEntityType;
if (existingEntityType is not null && existingEntityType != entityType)
if (existingEntityType is not null && existingEntityType != structuralType)
{
AddTranslationErrorDetails(
CosmosStrings.MultipleRootEntityTypesReferencedInQuery(entityType.DisplayName(), existingEntityType.DisplayName()));
CosmosStrings.MultipleRootEntityTypesReferencedInQuery(structuralType.DisplayName(), existingEntityType.DisplayName()));
return null;
}

Expand All @@ -358,7 +358,7 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis
return new ShapedQueryExpression(
queryExpression,
new StructuralTypeShaperExpression(
entityType,
structuralType,
new ProjectionBindingExpression(queryExpression, new ProjectionMember(), typeof(ValueBuffer)),
nullable: false));
}
Expand Down Expand Up @@ -532,6 +532,11 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou
return null;
}

if (source.ShaperExpression is StructuralTypeShaperExpression { StructuralType: IComplexType })
{
return null;
}

select.ApplyDistinct();

return source;
Expand Down Expand Up @@ -607,7 +612,7 @@ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression sou

var translatedSelect =
new SelectExpression(
new EntityProjectionExpression(translation, (IEntityType)projectedStructuralTypeShaper.StructuralType));
new EntityProjectionExpression(translation, projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
new StructuralTypeShaperExpression(
Expand Down Expand Up @@ -1132,8 +1137,8 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
slice,
alias,
new EntityProjectionExpression(
new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, alias),
(IEntityType)projectedStructuralTypeShaper.StructuralType));
new ObjectReferenceExpression(projectedStructuralTypeShaper.StructuralType, alias),
projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
new StructuralTypeShaperExpression(
Expand Down Expand Up @@ -1271,8 +1276,8 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
slice,
alias,
new EntityProjectionExpression(
new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, alias),
(IEntityType)projectedStructuralTypeShaper.StructuralType));
new ObjectReferenceExpression(projectedStructuralTypeShaper.StructuralType, alias),
projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
new StructuralTypeShaperExpression(
Expand Down Expand Up @@ -1390,7 +1395,17 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
return CreateShapedQueryExpression(targetEntityType, select);
}

// TODO: Collection of complex type (#31253)
case StructuralTypeShaperExpression shaper when property is IComplexProperty { IsCollection: true }:
{
var targetEntityType = shaper.StructuralType;
var projection = new EntityProjectionExpression(
new ObjectReferenceExpression(targetEntityType, sourceAlias), targetEntityType);
var select = SelectExpression.CreateForCollection(
shaper.ValueBufferExpression,
sourceAlias,
projection);
return CreateShapedQueryExpression(targetEntityType, select);
}

// Note that non-collection navigations/complex types are handled in CosmosSqlTranslatingExpressionVisitor
// (no collection -> no queryable operators)
Expand Down Expand Up @@ -1658,7 +1673,7 @@ private bool TryPushdownIntoSubquery(SelectExpression select)
var translation = new ObjectFunctionExpression(functionName, [array1, array2], arrayType);
var alias = _aliasManager.GenerateSourceAlias(translation);
var select = SelectExpression.CreateForCollection(
translation, alias, new ObjectReferenceExpression((IEntityType)structuralType1, alias));
translation, alias, new ObjectReferenceExpression(structuralType1, alias));
return CreateShapedQueryExpression(select, structuralType1.ClrType);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
public partial class CosmosShapedQueryCompilingExpressionVisitor
{
private sealed class CosmosProjectionBindingRemovingExpressionVisitor(
IStructuralTypeMaterializerSource entityMaterializerSource,
SelectExpression selectExpression,
ParameterExpression jTokenParameter,
bool trackQueryResults)
: CosmosProjectionBindingRemovingExpressionVisitorBase(jTokenParameter, trackQueryResults)
: CosmosProjectionBindingRemovingExpressionVisitorBase(entityMaterializerSource, jTokenParameter, trackQueryResults)
{
protected override ProjectionExpression GetProjection(ProjectionBindingExpression projectionBindingExpression)
=> selectExpression.Projection[GetProjectionIndex(projectionBindingExpression)];
Expand Down
Loading
Loading