Skip to content

Commit

Permalink
InMemory: Support for DefaultIfEmpty
Browse files Browse the repository at this point in the history
Part of #16963
  • Loading branch information
smitpatel committed Sep 3, 2019
1 parent 1746dd0 commit 06d4f56
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 85 deletions.
56 changes: 55 additions & 1 deletion src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,61 @@ public virtual void PushdownIntoSubquery()
}
}

private IPropertyBase InferPropertyFromInner(Expression expression)
public virtual void ApplyDefaultIfEmpty()
{
if (_valueBufferSlots.Count != 0)
{
throw new InvalidOperationException("Cannot Apply DefaultIfEmpty after ClientProjection.");
}

var result = new Dictionary<ProjectionMember, Expression>();
foreach (var keyValuePair in _projectionMapping)
{
if (keyValuePair.Value is EntityProjectionExpression entityProjection)
{
var map = new Dictionary<IProperty, Expression>();
foreach (var property in GetAllPropertiesInHierarchy(entityProjection.EntityType))
{
var index = AddToProjection(entityProjection.BindProperty(property));
map[property] = CreateReadValueExpression(property.ClrType.MakeNullable(), index, property);
}
result[keyValuePair.Key] = new EntityProjectionExpression(entityProjection.EntityType, map);
}
else
{
var index = AddToProjection(keyValuePair.Value);
result[keyValuePair.Key] = CreateReadValueExpression(
keyValuePair.Value.Type.MakeNullable(), index, InferPropertyFromInner(keyValuePair.Value));
}
}

_projectionMapping = result;

var selectorLambda = Lambda(
New(
_valueBufferConstructor,
NewArrayInit(
typeof(object),
_valueBufferSlots
.Select(e => e.Type.IsValueType ? Convert(e, typeof(object)) : e))),
CurrentParameter);

_groupingParameter = null;

ServerQueryExpression = Call(
InMemoryLinqOperatorProvider.Select.MakeGenericMethod(ServerQueryExpression.Type.TryGetSequenceType(), typeof(ValueBuffer)),
ServerQueryExpression,
selectorLambda);

ServerQueryExpression = Call(
InMemoryLinqOperatorProvider.DefaultIfEmptyWithArgument.MakeGenericMethod(typeof(ValueBuffer)),
ServerQueryExpression,
New(_valueBufferConstructor, NewArrayInit(typeof(object), Enumerable.Repeat(Constant(null), _valueBufferSlots.Count))));

_valueBufferSlots.Clear();
}

private static IPropertyBase InferPropertyFromInner(Expression expression)
{
if (expression is MethodCallExpression methodCallExpression
&& methodCallExpression.Method.IsGenericMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,17 @@ protected override ShapedQueryExpression TranslateCount(ShapedQueryExpression so
}

protected override ShapedQueryExpression TranslateDefaultIfEmpty(ShapedQueryExpression source, Expression defaultValue)
=> null;
{
if (defaultValue == null)
{
((InMemoryQueryExpression)source.QueryExpression).ApplyDefaultIfEmpty();
source.ShaperExpression = MarkShaperNullable(source.ShaperExpression);

return source;
}

return null;
}

protected override ShapedQueryExpression TranslateDistinct(ShapedQueryExpression source)
{
Expand Down
2 changes: 1 addition & 1 deletion src/EFCore/Query/EntityMaterializerSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static readonly MethodInfo TryReadValueMethod
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static TValue TryReadValue<TValue>(
in ValueBuffer valueBuffer, int index, IPropertyBase property)
=> (TValue)valueBuffer[index];
=> valueBuffer[index] is TValue value ? value : default;

public virtual Expression CreateMaterializeExpression(
IEntityType entityType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,41 +13,5 @@ public AsyncSimpleQueryInMemoryTest(NorthwindQueryInMemoryFixture<NoopModelCusto
: base(fixture)
{
}

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

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

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

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

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

[ConditionalFact(Skip = "Issue #16963 Set Operation")]
public override Task Union_non_entity()
{
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,80 +159,43 @@ public override Task Projection_when_arithmetic_mixed_subqueries(bool isAsync)
return base.Projection_when_arithmetic_mixed_subqueries(isAsync);
}

#region DefaultIfEmpty

public override Task DefaultIfEmpty_in_subquery(bool isAsync)
{
return Task.CompletedTask;
}

public override Task DefaultIfEmpty_in_subquery_nested(bool isAsync)
{
return Task.CompletedTask;
}

public override Task DefaultIfEmpty_in_subquery_not_correlated(bool isAsync)
{
return Task.CompletedTask;
}

public override void DefaultIfEmpty_without_group_join()
{
}
#region SelectMany

public override Task Default_if_empty_top_level(bool isAsync)
public override Task SelectMany_Joined_DefaultIfEmpty(bool isAsync)
{
return Task.CompletedTask;
}

public override Task Default_if_empty_top_level_followed_by_projecting_constant(bool isAsync)
public override Task SelectMany_Joined_DefaultIfEmpty2(bool isAsync)
{
return Task.CompletedTask;
}

public override Task Default_if_empty_top_level_positive(bool isAsync)
public override Task SelectMany_correlated_with_outer_3(bool isAsync)
{
return Task.CompletedTask;
}

public override Task Default_if_empty_top_level_projection(bool isAsync)
public override Task SelectMany_correlated_with_outer_4(bool isAsync)
{
return Task.CompletedTask;
}

public override Task Join_with_default_if_empty_on_both_sources(bool isAsync)
{
return Task.CompletedTask;
}
#endregion

#region SelectMany

public override Task SelectMany_Joined_DefaultIfEmpty(bool isAsync)
{
return Task.CompletedTask;
}

public override Task SelectMany_Joined_DefaultIfEmpty2(bool isAsync)
{
return Task.CompletedTask;
}
#region NullableError

public override Task SelectMany_correlated_with_outer_3(bool isAsync)
public override Task Project_single_element_from_collection_with_OrderBy_Distinct_and_FirstOrDefault_followed_by_projecting_length(bool isAsync)
{
return Task.CompletedTask;
}

public override Task SelectMany_correlated_with_outer_4(bool isAsync)
public override Task DefaultIfEmpty_in_subquery_nested(bool isAsync)
{
return Task.CompletedTask;
}

#endregion

#region NullableError

public override Task Project_single_element_from_collection_with_OrderBy_Distinct_and_FirstOrDefault_followed_by_projecting_length(bool isAsync)
public override Task Default_if_empty_top_level_projection(bool isAsync)
{
return Task.CompletedTask;
}
Expand Down

0 comments on commit 06d4f56

Please sign in to comment.