Skip to content

Commit

Permalink
Query: Add TableReferenceExpression as a bridge to ColumnExpression f…
Browse files Browse the repository at this point in the history
…or referential integrity

Part of #17337
  • Loading branch information
smitpatel committed Mar 20, 2021
1 parent 92a1b91 commit f801da6
Show file tree
Hide file tree
Showing 19 changed files with 1,028 additions and 824 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal
/// </summary>
public class EntityProjectionExpression : Expression, IPrintableExpression, IAccessExpression
{
private readonly IDictionary<IProperty, IAccessExpression> _propertyExpressionsMap
= new Dictionary<IProperty, IAccessExpression>();

private readonly IDictionary<INavigation, IAccessExpression> _navigationExpressionsMap
= new Dictionary<INavigation, IAccessExpression>();
private readonly Dictionary<IProperty, IAccessExpression> _propertyExpressionsMap = new();
private readonly Dictionary<INavigation, IAccessExpression> _navigationExpressionsMap = new();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Query.Internal
/// </summary>
public class EntityProjectionExpression : Expression, IPrintableExpression
{
private readonly IDictionary<IProperty, MethodCallExpression> _readExpressionMap;

private readonly IDictionary<INavigation, EntityShaperExpression> _navigationExpressionsCache
= new Dictionary<INavigation, EntityShaperExpression>();
private readonly IReadOnlyDictionary<IProperty, MethodCallExpression> _readExpressionMap;
private readonly Dictionary<INavigation, EntityShaperExpression> _navigationExpressionsCache = new();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -33,7 +31,7 @@ private readonly IDictionary<INavigation, EntityShaperExpression> _navigationExp
/// </summary>
public EntityProjectionExpression(
IEntityType entityType,
IDictionary<IProperty, MethodCallExpression> readExpressionMap)
IReadOnlyDictionary<IProperty, MethodCallExpression> readExpressionMap)
{
EntityType = entityType;
_readExpressionMap = readExpressionMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ public class InMemoryProjectionBindingExpressionVisitor : ExpressionVisitor
private InMemoryQueryExpression _queryExpression;
private bool _clientEval;

private readonly IDictionary<ProjectionMember, Expression> _projectionMapping
= new Dictionary<ProjectionMember, Expression>();
private Dictionary<EntityProjectionExpression, ProjectionBindingExpression>? _entityProjectionCache;

private readonly Dictionary<ProjectionMember, Expression> _projectionMapping = new();
private readonly Stack<ProjectionMember> _projectionMembers = new();

/// <summary>
Expand Down Expand Up @@ -68,6 +68,7 @@ public virtual Expression Translate(InMemoryQueryExpression queryExpression, Exp
if (result == QueryCompilationContext.NotTranslatedExpression)
{
_clientEval = true;
_entityProjectionCache = new();

expandedExpression = _queryableMethodTranslatingExpressionVisitor.ExpandWeakEntities(_queryExpression, expression);
result = Visit(expandedExpression);
Expand Down Expand Up @@ -255,8 +256,14 @@ protected override Expression VisitExtension(Expression extensionExpression)

if (_clientEval)
{
return entityShaperExpression.Update(
new ProjectionBindingExpression(_queryExpression, _queryExpression.AddToProjection(entityProjectionExpression)));
if (!_entityProjectionCache!.TryGetValue(entityProjectionExpression, out var entityProjectionBinding))
{
entityProjectionBinding = new ProjectionBindingExpression(
_queryExpression, _queryExpression.AddToProjection(entityProjectionExpression));
_entityProjectionCache[entityProjectionExpression] = entityProjectionBinding;
}

return entityShaperExpression.Update(entityProjectionBinding);
}

_projectionMapping[_projectionMembers.Peek()] = entityProjectionExpression;
Expand Down
18 changes: 5 additions & 13 deletions src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ private static readonly PropertyInfo _valueBufferCountMemberInfo
private readonly List<Expression> _clientProjectionExpressions = new();
private readonly List<MethodCallExpression> _projectionMappingExpressions = new();

private readonly IDictionary<EntityProjectionExpression, IDictionary<IProperty, int>> _entityProjectionCache
= new Dictionary<EntityProjectionExpression, IDictionary<IProperty, int>>();

private readonly ParameterExpression _valueBufferParameter;

private IDictionary<ProjectionMember, Expression> _projectionMapping = new Dictionary<ProjectionMember, Expression>();
Expand Down Expand Up @@ -319,17 +316,12 @@ EntityProjectionExpression UpdateEntityProjection(EntityProjectionExpression ent
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual IDictionary<IProperty, int> AddToProjection(EntityProjectionExpression entityProjectionExpression)
public virtual IReadOnlyDictionary<IProperty, int> AddToProjection(EntityProjectionExpression entityProjectionExpression)
{
if (!_entityProjectionCache.TryGetValue(entityProjectionExpression, out var indexMap))
var indexMap = new Dictionary<IProperty, int>();
foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType))
{
indexMap = new Dictionary<IProperty, int>();
foreach (var property in GetAllPropertiesInHierarchy(entityProjectionExpression.EntityType))
{
indexMap[property] = AddToProjection(entityProjectionExpression.BindProperty(property));
}

_entityProjectionCache[entityProjectionExpression] = indexMap;
indexMap[property] = AddToProjection(entityProjectionExpression.BindProperty(property));
}

return indexMap;
Expand Down Expand Up @@ -1032,7 +1024,7 @@ public ShaperRemappingExpressionVisitor(IDictionary<ProjectionMember, Expression
&& projectionBindingExpression.ProjectionMember != null)
{
var mappingValue = ((ConstantExpression)_projectionMapping[projectionBindingExpression.ProjectionMember]).Value;
return mappingValue is IDictionary<IProperty, int> indexMap
return mappingValue is IReadOnlyDictionary<IProperty, int> indexMap
? new ProjectionBindingExpression(projectionBindingExpression.QueryExpression, indexMap)
: mappingValue is int index
? new ProjectionBindingExpression(
Expand Down
8 changes: 3 additions & 5 deletions src/EFCore.Relational/Query/EntityProjectionExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ namespace Microsoft.EntityFrameworkCore.Query
/// </summary>
public class EntityProjectionExpression : Expression
{
private readonly IDictionary<IProperty, ColumnExpression> _propertyExpressionMap = new Dictionary<IProperty, ColumnExpression>();

private readonly IDictionary<INavigation, EntityShaperExpression> _ownedNavigationMap
= new Dictionary<INavigation, EntityShaperExpression>();
private readonly IReadOnlyDictionary<IProperty, ColumnExpression> _propertyExpressionMap;
private readonly Dictionary<INavigation, EntityShaperExpression> _ownedNavigationMap = new();

/// <summary>
/// Creates a new instance of the <see cref="EntityProjectionExpression" /> class.
Expand All @@ -49,7 +47,7 @@ public EntityProjectionExpression(IEntityType entityType, TableExpressionBase in
/// <param name="discriminatorExpression"> A <see cref="SqlExpression" /> to generate discriminator for each concrete entity type in hierarchy. </param>
public EntityProjectionExpression(
IEntityType entityType,
IDictionary<IProperty, ColumnExpression> propertyExpressionMap,
IReadOnlyDictionary<IProperty, ColumnExpression> propertyExpressionMap,
SqlExpression? discriminatorExpression = null)
{
Check.NotNull(entityType, nameof(entityType));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public EnumHasFlagTranslator(ISqlExpressionFactory sqlExpressionFactory)
var argument = arguments[0];
return instance.Type != argument.Type
? null
// TODO: If argument is SelectExpression, we need to clone it.
: (SqlExpression)_sqlExpressionFactory.Equal(_sqlExpressionFactory.And(instance, argument), argument);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ private static readonly MethodInfo _getParameterValueMethodInfo
private SelectExpression _selectExpression;
private SqlExpression[] _existingProjections;
private bool _clientEval;
private Dictionary<EntityProjectionExpression, ProjectionBindingExpression>? _entityProjectionCache;

private readonly IDictionary<ProjectionMember, Expression> _projectionMapping
= new Dictionary<ProjectionMember, Expression>();

private readonly Dictionary<ProjectionMember, Expression> _projectionMapping = new();
private readonly Stack<ProjectionMember> _projectionMembers = new();

/// <summary>
Expand Down Expand Up @@ -77,6 +76,7 @@ public virtual Expression Translate(SelectExpression selectExpression, Expressio
if (result == QueryCompilationContext.NotTranslatedExpression)
{
_clientEval = true;
_entityProjectionCache = new();

expandedExpression = _queryableMethodTranslatingExpressionVisitor.ExpandWeakEntities(_selectExpression, expression);
_existingProjections = _selectExpression.Projection.Select(e => e.Expression).ToArray();
Expand Down Expand Up @@ -334,9 +334,14 @@ protected override Expression VisitExtension(Expression extensionExpression)

if (_clientEval)
{
return entityShaperExpression.Update(
new ProjectionBindingExpression(
_selectExpression, _selectExpression.AddToProjection(entityProjectionExpression)));
if (!_entityProjectionCache!.TryGetValue(entityProjectionExpression, out var entityProjectionBinding))
{
entityProjectionBinding = new ProjectionBindingExpression(
_selectExpression, _selectExpression.AddToProjection(entityProjectionExpression));
_entityProjectionCache[entityProjectionExpression] = entityProjectionBinding;
}

return entityShaperExpression.Update(entityProjectionBinding);
}

_projectionMapping[_projectionMembers.Peek()] = entityProjectionExpression;
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ private bool IsNonComposedSetOperation(SelectExpression selectExpression)
&& selectExpression.Projection.Count == setOperation.Source1.Projection.Count
&& selectExpression.Projection.Select(
(pe, index) => pe.Expression is ColumnExpression column
&& string.Equals(column.Table.Alias, setOperation.Alias, StringComparison.OrdinalIgnoreCase)
&& string.Equals(column.TableAlias, setOperation.Alias, StringComparison.OrdinalIgnoreCase)
&& string.Equals(
column.Name, setOperation.Source1.Projection[index].Alias, StringComparison.OrdinalIgnoreCase))
.All(e => e);
Expand Down Expand Up @@ -332,7 +332,7 @@ protected override Expression VisitColumn(ColumnExpression columnExpression)
Check.NotNull(columnExpression, nameof(columnExpression));

_relationalCommandBuilder
.Append(_sqlGenerationHelper.DelimitIdentifier(columnExpression.Table.Alias!))
.Append(_sqlGenerationHelper.DelimitIdentifier(columnExpression.TableAlias))
.Append(".")
.Append(_sqlGenerationHelper.DelimitIdentifier(columnExpression.Name));

Expand Down
Loading

0 comments on commit f801da6

Please sign in to comment.