Skip to content

Commit

Permalink
InMemory: Add support for collection projection
Browse files Browse the repository at this point in the history
Part of #16963
  • Loading branch information
smitpatel committed Aug 23, 2019
1 parent 8a124a7 commit ac48ba4
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 144 deletions.
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()
{
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

0 comments on commit ac48ba4

Please sign in to comment.