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: Add support for owned collections #16927

Merged
merged 1 commit into from
Aug 3, 2019
Merged
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 @@ -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