Skip to content

Commit

Permalink
Query: Add support for owned collections (#16927)
Browse files Browse the repository at this point in the history
Resolves #16620
Resolves #16392
Partially fix #15285
  • Loading branch information
smitpatel authored Aug 3, 2019
1 parent 3c5e998 commit 49f9f76
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 270 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1009,9 +1009,25 @@ private Expression Expand(Expression source, MemberIdentity member)
{
if (navigation.IsCollection())
{
return CreateShapedQueryExpression(
navigation.GetTargetType(),
_sqlExpressionFactory.Select(navigation.GetTargetType()));
var targetEntityType = navigation.GetTargetType();
var innerSelectExpression = _sqlExpressionFactory.Select(targetEntityType);
var innerShapedQuery = CreateShapedQueryExpression(targetEntityType, innerSelectExpression);

var makeNullable = navigation.ForeignKey.PrincipalKey.Properties
.Concat(navigation.ForeignKey.Properties)
.Select(p => p.ClrType)
.Any(t => t.IsNullableType());

var outerKey = CreateKeyAccessExpression(
entityShaperExpression, navigation.ForeignKey.PrincipalKey.Properties, makeNullable);
var innerKey = CreateKeyAccessExpression(
innerShapedQuery.ShaperExpression, navigation.ForeignKey.Properties, makeNullable);

var correlationPredicate = _sqlTranslator.Translate(Expression.Equal(outerKey, innerKey));

innerSelectExpression.ApplyPredicate(correlationPredicate);

return innerShapedQuery;
}

var entityProjectionExpression = (EntityProjectionExpression)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,21 @@ protected Expression ExpandNavigation(
{
var ownedEntityReference = new EntityReference(navigation.GetTargetType());
ownedEntityReference.MarkAsOptional();
ownedEntityReference.SetIncludePaths(entityReference.IncludePaths[navigation]);
if (entityReference.IncludePaths.ContainsKey(navigation))
{
ownedEntityReference.SetIncludePaths(entityReference.IncludePaths[navigation]);
}

var ownedExpansion = new OwnedNavigationReference(root, navigation, ownedEntityReference);
entityReference.NavigationMap[navigation] = ownedExpansion;
return ownedExpansion;
if (navigation.IsCollection())
{
return new MaterializeCollectionNavigationExpression(ownedExpansion, navigation);
}
else
{
entityReference.NavigationMap[navigation] = ownedExpansion;
return ownedExpansion;
}
}

var innerQueryableType = navigation.GetTargetType().ClrType;
Expand Down Expand Up @@ -455,28 +465,29 @@ private Expression ExpandIncludesHelper(Expression root, EntityReference entityR
var included = ExpandNavigation(convertedRoot, entityReference, navigation, converted);
if (navigation.ForeignKey.IsOwnership)
{
var includedEntityReference = ((OwnedNavigationReference)included).EntityReference;
if (navigation.IsCollection())
{
var subquery = ((MaterializeCollectionNavigationExpression)included).Subquery;
var includedEntityReference = ((OwnedNavigationReference)subquery).EntityReference;
var elementType = navigation.ClrType.TryGetSequenceType();
var collectionSelectorParameter = Expression.Parameter(elementType);
var nestedInclude = ExpandIncludesHelper(collectionSelectorParameter, includedEntityReference);
if (nestedInclude is IncludeExpression)
{

included = Expression.Call(
subquery = Expression.Call(
QueryableMethodProvider.SelectMethodInfo.MakeGenericMethod(elementType, elementType),
Expression.Call(
QueryableMethodProvider.AsQueryableMethodInfo.MakeGenericMethod(elementType),
included),
subquery),
Expression.Quote(
Expression.Lambda(nestedInclude, collectionSelectorParameter)));
}

included = new MaterializeCollectionNavigationExpression(included, navigation);
included = new MaterializeCollectionNavigationExpression(subquery, navigation);
}
else
{
var includedEntityReference = ((OwnedNavigationReference)included).EntityReference;
included = ExpandIncludesHelper(included, includedEntityReference);
}
}
Expand Down Expand Up @@ -567,10 +578,11 @@ public override Expression Visit(Expression expression)
{
var pendingSelector = Visit(navigationExpansionExpression.PendingSelector);
Expression result;
var source = Visit(navigationExpansionExpression.Source);
if (pendingSelector == navigationExpansionExpression.CurrentParameter)
{
// identity projection
result = navigationExpansionExpression.Source;
result = source;
}
else
{
Expand All @@ -580,7 +592,7 @@ public override Expression Visit(Expression expression)
QueryableMethodProvider.SelectMethodInfo.MakeGenericMethod(
navigationExpansionExpression.SourceElementType,
selectorLambda.ReturnType),
navigationExpansionExpression.Source,
source,
Expression.Quote(selectorLambda));
}

Expand Down
37 changes: 33 additions & 4 deletions src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ var outboundNavigations
protected override Expression VisitExtension(Expression extensionExpression)
{
return extensionExpression is NavigationExpansionExpression
|| extensionExpression is OwnedNavigationReference
? extensionExpression
: base.VisitExtension(extensionExpression);
}
Expand All @@ -110,10 +111,19 @@ protected override Expression VisitMember(MemberExpression memberExpression)
if (innerExpression is MaterializeCollectionNavigationExpression materializeCollectionNavigation
&& memberExpression.Member.Name == nameof(List<int>.Count))
{
var subquery = materializeCollectionNavigation.Subquery;
var elementType = subquery.Type.TryGetSequenceType();
if (subquery is OwnedNavigationReference ownedNavigationReference
&& ownedNavigationReference.Navigation.IsCollection())
{
subquery = Expression.Call(
QueryableMethodProvider.AsQueryableMethodInfo.MakeGenericMethod(elementType),
subquery);
}

return Visit(Expression.Call(
QueryableMethodProvider.CountWithoutPredicateMethodInfo.MakeGenericMethod(
materializeCollectionNavigation.Subquery.Type.TryGetSequenceType()),
materializeCollectionNavigation.Subquery));
QueryableMethodProvider.CountWithoutPredicateMethodInfo.MakeGenericMethod(elementType),
subquery));
}

var updatedExpression = (Expression)memberExpression.Update(innerExpression);
Expand Down Expand Up @@ -368,7 +378,26 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
else if (firstArgument is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression
&& methodCallExpression.Method.Name == nameof(Queryable.AsQueryable))
{
return materializeCollectionNavigationExpression.Subquery;
var subquery = materializeCollectionNavigationExpression.Subquery;
if (subquery is OwnedNavigationReference ownedNavigationReference
&& ownedNavigationReference.Navigation.IsCollection())
{
return Visit(Expression.Call(
QueryableMethodProvider.AsQueryableMethodInfo.MakeGenericMethod(subquery.Type.TryGetSequenceType()),
subquery));
}

return subquery;
}
else if (firstArgument is OwnedNavigationReference ownedNavigationReference
&& ownedNavigationReference.Navigation.IsCollection()
&& methodCallExpression.Method.Name == nameof(Queryable.AsQueryable))
{
var parameterName = GetParameterName("o");
var entityReference = ownedNavigationReference.EntityReference;
var currentTree = new NavigationTreeExpression(entityReference);

return new NavigationExpansionExpression(methodCallExpression, currentTree, currentTree, parameterName);
}

throw new NotImplementedException("NonNavSource");
Expand Down
1 change: 0 additions & 1 deletion src/EFCore/Query/QueryOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Query.Internal;

namespace Microsoft.EntityFrameworkCore.Query
Expand Down
10 changes: 7 additions & 3 deletions test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public override void Query_with_owned_entity_equality_operator()
base.Query_with_owned_entity_equality_operator();
}

[ConditionalFact(Skip = "Issue#16392")]
[ConditionalFact(Skip = "Count #16146")]
public override void Navigation_rewrite_on_owned_collection()
{
base.Navigation_rewrite_on_owned_collection();
Expand All @@ -42,16 +42,20 @@ FROM root c
WHERE ((c[""Discriminator""] = ""LeafB"") OR ((c[""Discriminator""] = ""LeafA"") OR ((c[""Discriminator""] = ""Branch"") OR (c[""Discriminator""] = ""OwnedPerson""))))");
}

[ConditionalFact(Skip = "Issue#16392")]
[ConditionalFact(Skip = "Issue#16926")]
public override void Navigation_rewrite_on_owned_collection_with_composition()
{
base.Navigation_rewrite_on_owned_collection_with_composition();

AssertSql(" ");
}

[ConditionalFact(Skip = "Issue#16392")]
[ConditionalFact(Skip = "Issue#16926")]
public override void Navigation_rewrite_on_owned_collection_with_composition_complex()
{
base.Navigation_rewrite_on_owned_collection_with_composition_complex();

AssertSql(" ");
}

public override void Navigation_rewrite_on_owned_reference_projecting_entity()
Expand Down
Loading

0 comments on commit 49f9f76

Please sign in to comment.