Skip to content

Commit

Permalink
Better access to EF.Property and indexer properties
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed May 29, 2019
1 parent 4f43be9 commit 3d422f9
Show file tree
Hide file tree
Showing 13 changed files with 86 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,8 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
{
if (_queryModelVisitor.CurrentParameter?.Type == typeof(JObject))
{
if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out _))
{
var source = methodCallExpression.Arguments[0];
var newSource = Visit(source);

if (source != newSource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ public override Expression Visit(Expression expression)
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object)
&& unaryExpression.Operand is MethodCallExpression methodCall
&& methodCall.Method.IsEFPropertyMethod()
&& methodCall.Arguments[0] is EntityShaperExpression entityShaperExpression
&& methodCall.TryGetEFPropertyArguments(out var source, out _)
&& source is EntityShaperExpression entityShaperExpression
&& entityShaperExpression.EntityType.GetProperties().Count() == newArrayExpression.Expressions.Count)
{
VerifyQueryExpression(entityShaperExpression.ValueBufferExpression);
Expand Down
7 changes: 3 additions & 4 deletions src/EFCore.InMemory/Query/Pipeline/Translator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,12 @@ protected override Expression VisitMember(MemberExpression memberExpression)

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName))
{
var firstArgument = Visit(methodCallExpression.Arguments[0]);
if (firstArgument is EntityShaperExpression entityShaper)
if (source is EntityShaperExpression entityShaper)
{
var entityType = entityShaper.EntityType;
var property = entityType.FindProperty((string)((ConstantExpression)methodCallExpression.Arguments[1]).Value);
var property = entityType.FindProperty(propertyName);

return _inMemoryQueryExpression.BindProperty(entityShaper.ValueBufferExpression, property);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,11 +411,10 @@ protected override Expression VisitUnary(UnaryExpression node)

protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsEFPropertyMethod())
if (node.TryGetEFPropertyArguments(out var source, out var propertyName))
{
if (node.Arguments[0].RemoveConvert() is QuerySourceReferenceExpression querySource
&& node.Arguments[1] is ConstantExpression propertyNameExpression
&& (string)propertyNameExpression.Value == _propertyName)
if (source.RemoveConvert() is QuerySourceReferenceExpression querySource
&& propertyName == _propertyName)
{
_canRemoveNullCheck = querySource.ReferencedQuerySource == _querySource;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,19 @@ private Expression BindProperty(EntityShaperExpression entityShaper, string prop

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName))
{
var firstArgument = methodCallExpression.Arguments[0];

// In certain cases EF.Property would have convert node around the source.
if (firstArgument is UnaryExpression unaryExpression
if (source is UnaryExpression unaryExpression
&& unaryExpression.NodeType == ExpressionType.Convert
&& unaryExpression.Type == typeof(object))
{
firstArgument = unaryExpression.Operand;
source = unaryExpression.Operand;
}

if (firstArgument is EntityShaperExpression entityShaper)
if (source is EntityShaperExpression entityShaper)
{
return BindProperty(entityShaper, (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value);
return BindProperty(entityShaper, propertyName);
}
else
{
Expand Down
55 changes: 55 additions & 0 deletions src/EFCore/Extensions/Internal/EFPropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,29 @@ public static class EFPropertyExtensions
{
private static readonly string _efTypeName = typeof(EF).FullName;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 static bool TryGetEFPropertyArguments(
[NotNull] this MethodCallExpression methodCallExpression,
out Expression entityExpression,
out string propertyName)
{
if (IsEFProperty(methodCallExpression)
&& methodCallExpression.Arguments[1] is ConstantExpression propertyNameExpression)
{
entityExpression = methodCallExpression.Arguments[0];
propertyName = (string)propertyNameExpression.Value;
return true;
}

(entityExpression, propertyName) = (null, null);
return false;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand All @@ -46,6 +69,38 @@ public static bool IsEFPropertyMethod([CanBeNull] this MethodInfo methodInfo)
&& methodInfo.Name == nameof(EF.Property)
&& methodInfo.DeclaringType?.FullName == _efTypeName;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 static bool TryGetEFIndexerArguments(
[NotNull] this MethodCallExpression methodCallExpression,
out Expression entityExpression,
out string propertyName)
{
if (IsEFIndexer(methodCallExpression)
&& methodCallExpression.Arguments[0] is ConstantExpression propertyNameExpression)
{
entityExpression = methodCallExpression.Object;
propertyName = (string)propertyNameExpression.Value;
return true;
}

(entityExpression, propertyName) = (null, null);
return false;
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// 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 static bool IsEFIndexer([NotNull] this MethodCallExpression methodCallExpression)
=> IsEFIndexer(methodCallExpression.Method);

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,14 @@ public virtual void Print(ExpressionPrinter expressionPrinter)
return;
}

if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out _, out var propertyName))
{
var method = methodCallExpression.Method;

expressionPrinter.StringBuilder.Append(method.DeclaringType?.Name + "." + method.Name + "(?");
expressionPrinter.Visit(Caller);
expressionPrinter.StringBuilder.Append("?, ");
expressionPrinter.Visit(methodCallExpression.Arguments[1]);
expressionPrinter.Visit(Constant(propertyName));
expressionPrinter.StringBuilder.Append(")");

return;
Expand Down Expand Up @@ -201,11 +201,11 @@ public override string ToString()
+ "(" + string.Join(",", methodCallExpression.Arguments) + ")";
}

var method = methodCallExpression.Method;
if (method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out _, out var propertyName))
{
var method = methodCallExpression.Method;
return method.DeclaringType?.Name + "." + method.Name
+ "(?" + Caller + "?, " + methodCallExpression.Arguments[1] + ")";
+ "(?" + Caller + "?, " + propertyName + ")";
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/EFCore/Query/Internal/ExpressionPrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp
/// </summary>
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (!methodCallExpression.Method.IsEFPropertyMethod())
if (!methodCallExpression.IsEFProperty())
{
_stringBuilder.Append(methodCallExpression.Method.ReturnType.ShortDisplayName() + " ");
}
Expand All @@ -760,7 +760,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp

var isSimpleMethodOrProperty = _simpleMethods.Contains(methodCallExpression.Method.Name)
|| methodCallExpression.Arguments.Count < 2
|| methodCallExpression.Method.IsEFPropertyMethod();
|| methodCallExpression.IsEFProperty();

var appendAction = isSimpleMethodOrProperty ? (Action<string>)Append : AppendLine;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class PendingSelectorIncludeRewriter : ExpressionVisitor
protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) => typeBinaryExpression;

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
=> methodCallExpression.Method.IsEFPropertyMethod()
=> methodCallExpression.IsEFProperty()
? methodCallExpression
: base.VisitMethodCall(methodCallExpression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ public partial class NavigationExpandingVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName))
{
var newSource = Visit(methodCallExpression.Arguments[0]);
var newSource = Visit(source);

return newSource is NavigationExpansionExpression navigationExpansionExpression
&& navigationExpansionExpression.State.PendingCardinalityReducingOperator != null
Expand All @@ -28,7 +28,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
navigationExpansionExpression,
efProperty: true,
memberInfo: null,
(string)((ConstantExpression)methodCallExpression.Arguments[1]).Value,
propertyName,
methodCallExpression.Type)
: methodCallExpression.Update(methodCallExpression.Object, new[] { newSource, methodCallExpression.Arguments[1] });
}
Expand Down Expand Up @@ -1395,7 +1395,7 @@ public PendingSelectorSourceMappingGenerator(ParameterExpression rootParameter,
protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) => typeBinaryExpression;

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
=> methodCallExpression.Method.IsEFPropertyMethod()
=> methodCallExpression.IsEFProperty()
? methodCallExpression
: base.VisitMethodCall(methodCallExpression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres

protected override Expression VisitUnary(UnaryExpression unaryExpression)
{
if ((unaryExpression.NodeType == ExpressionType.Convert || unaryExpression.NodeType == ExpressionType.TypeAs)
if ((unaryExpression.NodeType == ExpressionType.Convert || unaryExpression.NodeType == ExpressionType.TypeAs)
&& unaryExpression.Type != typeof(object))
{
if (unaryExpression.Type == unaryExpression.Operand.Type)
Expand Down Expand Up @@ -132,10 +132,9 @@ protected override Expression VisitMember(MemberExpression memberExpression)

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Method.IsEFPropertyMethod())
if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName))
{
var newCaller = Visit(methodCallExpression.Arguments[0]);
var propertyName = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value;
var newCaller = Visit(source);
var boundProperty = TryBindProperty(methodCallExpression, newCaller, propertyName);

return boundProperty ?? methodCallExpression.Update(methodCallExpression.Object, new[] { newCaller, methodCallExpression.Arguments[1] });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class PendingIncludeFindingVisitor : ExpressionVisitor
protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) => typeBinaryExpression;

protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
=> methodCallExpression.Method.IsEFPropertyMethod()
=> methodCallExpression.IsEFProperty()
? methodCallExpression
: base.VisitMethodCall(methodCallExpression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ private static Expression RewriteShadowPropertyAccess(Expression expression)
private class ShadowStateAccessRewriter : ExpressionVisitorBase
{
protected override Expression VisitMethodCall(MethodCallExpression expression)
=> expression.Method.IsEFPropertyMethod()
=> expression.IsEFProperty()
? Expression.Property(
expression.Arguments[0].RemoveConvert(),
Expression.Lambda<Func<string>>(expression.Arguments[1]).Compile().Invoke())
Expand Down

0 comments on commit 3d422f9

Please sign in to comment.