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

InMemory: Add support for collection projection #17400

Merged
merged 1 commit into from
Aug 23, 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 @@ -7,6 +7,7 @@
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;

Expand Down Expand Up @@ -85,51 +86,40 @@ public override Expression Visit(Expression expression)
return expression;

case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression:

var translated = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
materializeCollectionNavigationExpression.Subquery);

var index = _queryExpression.AddSubqueryProjection(translated, out var innerShaper);

return new CollectionShaperExpression(
new ProjectionBindingExpression(
_queryExpression,
index,
typeof(IEnumerable<ValueBuffer>)),
innerShaper,
return AddCollectionProjection(
_queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
materializeCollectionNavigationExpression.Subquery),
materializeCollectionNavigationExpression.Navigation,
materializeCollectionNavigationExpression.Navigation.GetTargetType().ClrType);
null);

case MethodCallExpression methodCallExpression:
{
if (methodCallExpression.Method.IsGenericMethod
&& methodCallExpression.Method.DeclaringType == typeof(Enumerable)
&& methodCallExpression.Method.Name == nameof(Enumerable.ToList))
{
//var elementType = methodCallExpression.Method.GetGenericArguments()[0];

//var result = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression.Arguments[0]);

//return _selectExpression.AddCollectionProjection(result, null, elementType);
throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name));
return AddCollectionProjection(
_queryableMethodTranslatingExpressionVisitor.TranslateSubquery(
methodCallExpression.Arguments[0]),
null,
methodCallExpression.Method.GetGenericArguments()[0]);
}

var subquery = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression);

if (subquery != null)
{
//if (subquery.ResultType == ResultType.Enumerable)
//{
// return _selectExpression.AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
//}
if (subquery.ResultCardinality == ResultCardinality.Enumerable)
{
return AddCollectionProjection(subquery, null, subquery.ShaperExpression.Type);
}

throw new InvalidOperationException(CoreStrings.QueryFailed(methodCallExpression.Print(), GetType().Name));
}

break;
}
}


var translation = _expressionTranslatingExpressionVisitor.Translate(expression);
return translation == null
? base.Visit(expression)
Expand All @@ -153,6 +143,19 @@ public override Expression Visit(Expression expression)
return base.Visit(expression);
}

private CollectionShaperExpression AddCollectionProjection(
ShapedQueryExpression subquery, INavigation navigation, Type elementType)
=> new CollectionShaperExpression(
new ProjectionBindingExpression(
_queryExpression,
_queryExpression.AddSubqueryProjection(
subquery,
out var innerShaper),
typeof(IEnumerable<ValueBuffer>)),
innerShaper,
navigation,
elementType);

protected override Expression VisitExtension(Expression extensionExpression)
{
if (extensionExpression is EntityShaperExpression entityShaperExpression)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,11 +635,11 @@ private ShapedQueryExpression TranslateScalarAggregate(

inMemoryQueryExpression.ServerQueryExpression
= Expression.Call(
InMemoryLinqOperatorProvider
.GetAggregateMethod(methodName, selector.ReturnType, parameterCount: 1)
.MakeGenericMethod(typeof(ValueBuffer)),
inMemoryQueryExpression.ServerQueryExpression,
selector);
InMemoryLinqOperatorProvider
.GetAggregateMethod(methodName, selector.ReturnType, parameterCount: 1)
.MakeGenericMethod(typeof(ValueBuffer)),
inMemoryQueryExpression.ServerQueryExpression,
selector);

source.ShaperExpression = inMemoryQueryExpression.GetSingleScalarProjection();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ private static readonly MethodInfo _includeCollectionMethodInfo
= typeof(CustomShaperCompilingExpressionVisitor).GetTypeInfo()
.GetDeclaredMethod(nameof(IncludeCollection));

private static readonly MethodInfo _materializeCollectionMethodInfo
= typeof(CustomShaperCompilingExpressionVisitor).GetTypeInfo()
.GetDeclaredMethod(nameof(MaterializeCollection));

private static void SetIsLoadedNoTracking(object entity, INavigation navigation)
=> ((ILazyLoader)(navigation
.DeclaringEntityType
.GetServiceProperties()
.FirstOrDefault(p => p.ClrType == typeof(ILazyLoader)))
?.GetGetter().GetClrValue(entity))
?.SetLoaded(entity, navigation.Name);

private static void IncludeReference<TEntity, TIncludingEntity, TIncludedEntity>(
QueryContext queryContext,
TEntity entity,
Expand Down Expand Up @@ -114,13 +126,23 @@ private static void IncludeCollection<TEntity, TIncludingEntity, TIncludedEntity
}
}

private static void SetIsLoadedNoTracking(object entity, INavigation navigation)
=> ((ILazyLoader)(navigation
.DeclaringEntityType
.GetServiceProperties()
.FirstOrDefault(p => p.ClrType == typeof(ILazyLoader)))
?.GetGetter().GetClrValue(entity))
?.SetLoaded(entity, navigation.Name);
private static TCollection MaterializeCollection<TElement, TCollection>(
QueryContext queryContext,
IEnumerable<ValueBuffer> innerValueBuffers,
Func<QueryContext, ValueBuffer, TElement> innerShaper,
IClrCollectionAccessor clrCollectionAccessor)
where TCollection : class, ICollection<TElement>
{
var collection = (TCollection)(clrCollectionAccessor?.Create() ?? new List<TElement>());

foreach (var valueBuffer in innerValueBuffers)
{
var element = innerShaper(queryContext, valueBuffer);
collection.Add(element);
}

return collection;
}

protected override Expression VisitExtension(Expression extensionExpression)
{
Expand All @@ -138,12 +160,12 @@ protected override Expression VisitExtension(Expression extensionExpression)

if (includeExpression.Navigation.IsCollection())
{
var collectionShaperExpression = (CollectionShaperExpression)includeExpression.NavigationExpression;
var collectionShaper = (CollectionShaperExpression)includeExpression.NavigationExpression;
return Expression.Call(
_includeCollectionMethodInfo.MakeGenericMethod(entityClrType, includingClrType, relatedEntityClrType),
QueryCompilationContext.QueryContextParameter,
collectionShaperExpression.Projection,
Expression.Constant(((LambdaExpression)Visit(collectionShaperExpression.InnerShaper)).Compile()),
collectionShaper.Projection,
Expression.Constant(((LambdaExpression)Visit(collectionShaper.InnerShaper)).Compile()),
includeExpression.EntityExpression,
Expression.Constant(includeExpression.Navigation),
Expression.Constant(inverseNavigation, typeof(INavigation)),
Expand All @@ -166,6 +188,21 @@ protected override Expression VisitExtension(Expression extensionExpression)
Expression.Constant(_tracking));
}

if (extensionExpression is CollectionShaperExpression collectionShaperExpression)
{
var elementType = collectionShaperExpression.ElementType;
var collectionType = collectionShaperExpression.Type;

return Expression.Call(
_materializeCollectionMethodInfo.MakeGenericMethod(elementType, collectionType),
QueryCompilationContext.QueryContextParameter,
collectionShaperExpression.Projection,
Expression.Constant(((LambdaExpression)Visit(collectionShaperExpression.InnerShaper)).Compile()),
Expression.Constant(
collectionShaperExpression.Navigation?.GetCollectionAccessor(),
typeof(IClrCollectionAccessor)));
}

return base.VisitExtension(extensionExpression);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ protected override Expression VisitExtension(Expression extensionExpression)
case IncludeExpression includeExpression:
{
var entity = Visit(includeExpression.EntityExpression);
if (includeExpression.NavigationExpression is CollectionShaperExpression collectionShaperExpression)
if (includeExpression.NavigationExpression is CollectionShaperExpression collectionShaper)
{
var innerLambda = (LambdaExpression)collectionShaperExpression.InnerShaper;
var innerLambda = (LambdaExpression)collectionShaper.InnerShaper;
var innerShaper = new ShaperExpressionProcessingExpressionVisitor(null, innerLambda.Parameters[0])
.Inject(innerLambda.Body);

_expressions.Add(
includeExpression.Update(
entity,
collectionShaperExpression.Update(
Visit(collectionShaperExpression.Projection),
collectionShaper.Update(
Visit(collectionShaper.Projection),
innerShaper)));
}
else
Expand All @@ -98,6 +98,27 @@ protected override Expression VisitExtension(Expression extensionExpression)

return entity;
}

case CollectionShaperExpression collectionShaperExpression:
{
var key = GenerateKey((ProjectionBindingExpression)collectionShaperExpression.Projection);
if (!_mapping.TryGetValue(key, out var variable))
{
var projection = Visit(collectionShaperExpression.Projection);

variable = Expression.Parameter(collectionShaperExpression.Type);
_variables.Add(variable);

var innerLambda = (LambdaExpression)collectionShaperExpression.InnerShaper;
var innerShaper = new ShaperExpressionProcessingExpressionVisitor(null, innerLambda.Parameters[0])
.Inject(innerLambda.Body);

_expressions.Add(Expression.Assign(variable, collectionShaperExpression.Update(projection, innerShaper)));
_mapping[key] = variable;
}

return variable;
}
}

return base.VisitExtension(extensionExpression);
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Query/CollectionShaperExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public CollectionShaperExpression(
Projection = projection;
InnerShaper = innerShaper;
Navigation = navigation;
ElementType = elementType;
ElementType = elementType ?? navigation.ClrType.TryGetSequenceType();
}

protected override Expression VisitChildren(ExpressionVisitor visitor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public AsyncGearsOfWarQueryInMemoryTest(GearsOfWarQueryInMemoryFixture fixture,
{
}

[ConditionalFact(Skip = "Issue#16963")]
[ConditionalFact(Skip = "Issue#16963 Group By")]
public override Task GroupBy_Select_sum()
{
return base.GroupBy_Select_sum();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,40 @@ public AsyncSimpleQueryInMemoryTest(NorthwindQueryInMemoryFixture<NoopModelCusto
{
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Concat_dbset()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Concat_simple()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Concat_non_entity()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Except_non_entity()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Intersect_non_entity()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue #16963")]
[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Union_non_entity()
{
return Task.CompletedTask;
}

[ConditionalFact(Skip = "Issue#16963")]
public override Task ToList_context_subquery_deadlock_issue()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This are the set of tests which covers the scenario.

{
return base.ToList_context_subquery_deadlock_issue();
}

[ConditionalFact(Skip = "Issue#16963")]
public override Task ToList_on_nav_subquery_in_projection()
{
return base.ToList_on_nav_subquery_in_projection();
}

[ConditionalFact(Skip = "Issue#16963")]
public override Task ToList_on_nav_subquery_with_predicate_in_projection()
{
return base.ToList_on_nav_subquery_with_predicate_in_projection();
}

[ConditionalFact(Skip = "See issue#16963")]
public override Task Query_backed_by_database_view()
{
return base.Query_backed_by_database_view();
}

[ConditionalFact(Skip = "See issue#16963")]
public override Task ToArray_on_nav_subquery_in_projection()
{
return base.ToArray_on_nav_subquery_in_projection();
}

[ConditionalFact(Skip = "See issue#16963")]
public override Task ToArray_on_nav_subquery_in_projection_nested()
{
return base.ToArray_on_nav_subquery_in_projection_nested();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,12 @@ public CompiledQueryInMemoryTest(NorthwindQueryInMemoryFixture<NoopModelCustomiz
{
}

[ConditionalFact(Skip = "See issue #13857")]
[ConditionalFact(Skip = "See issue #17386")]
public override void Query_with_array_parameter()
{
}

[ConditionalFact(Skip = "See issue #13857")]
[ConditionalFact(Skip = "See issue #17386")]
public override Task Query_with_array_parameter_async() => null;

[ConditionalFact(Skip = "See issue#13857")]
public override void DbQuery_query()
{
base.DbQuery_query();
}

[ConditionalFact(Skip = "See issue#13857")]
public override Task DbQuery_query_async()
{
return base.DbQuery_query_async();
}

[ConditionalFact(Skip = "See issue#13857")]
public override void DbQuery_query_first()
{
base.DbQuery_query_first();
}

[ConditionalFact(Skip = "See issue#13857")]
public override Task DbQuery_query_first_async()
{
return base.DbQuery_query_first_async();
}
}
}
Loading