Skip to content

Commit

Permalink
Make more types used by Cosmos public
Browse files Browse the repository at this point in the history
Per comment on #16796
  • Loading branch information
ajcvickers committed Jul 28, 2019
1 parent 2fa9ec2 commit ec02e5c
Show file tree
Hide file tree
Showing 28 changed files with 310 additions and 290 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Storage;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -520,11 +518,11 @@ private static void IncludeCollection<TIncludingEntity, TIncludedEntity>(
}

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

private static LambdaExpression GenerateFixup(
Expand Down Expand Up @@ -558,8 +556,7 @@ private static Expression AssignReferenceNavigation(
ParameterExpression entity,
ParameterExpression relatedEntity,
INavigation navigation)
=> entity.MakeMemberAccess(navigation.GetMemberInfo(forConstruction: false, forSet: true))
.CreateAssignExpression(relatedEntity);
=> entity.MakeMemberAccess(navigation.GetMemberInfo(forMaterialization: false, forSet: true)).Assign(relatedEntity);

private static Expression AddToCollectionNavigation(
ParameterExpression entity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Internal;

namespace Microsoft.EntityFrameworkCore.Query
{
Expand Down Expand Up @@ -275,11 +272,11 @@ private static TCollection InitializeCollection<TElement, TCollection>(
}

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

protected override Expression VisitExtension(Expression extensionExpression)
Expand Down Expand Up @@ -469,8 +466,7 @@ private static Expression AssignReferenceNavigation(
ParameterExpression relatedEntity,
INavigation navigation)
{
return entity.MakeMemberAccess(navigation.GetMemberInfo(forConstruction: true, forSet: true))
.CreateAssignExpression(relatedEntity);
return entity.MakeMemberAccess(navigation.GetMemberInfo(forMaterialization: true, forSet: true)).Assign(relatedEntity);
}

private static Expression AddToCollectionNavigation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.Storage;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ private Expression CreateSnapshotExpression(

var memberAccess = (Expression)Expression.MakeMemberAccess(
entityVariable,
propertyBase.GetMemberInfo(forConstruction: false, forSet: false));
propertyBase.GetMemberInfo(forMaterialization: false, forSet: false));

if (memberAccess.Type != propertyBase.ClrType)
{
Expand Down
48 changes: 0 additions & 48 deletions src/EFCore/Extensions/Internal/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,54 +207,6 @@ public static bool IsEntityQueryable([NotNull] this ConstantExpression constantE
=> constantExpression.Type.GetTypeInfo().IsGenericType
&& constantExpression.Type.GetGenericTypeDefinition() == typeof(EntityQueryable<>);

/// <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 MemberExpression MakeMemberAccess(
[CanBeNull] this Expression expression,
[NotNull] MemberInfo member)
{
var memberDeclaringClrType = member.DeclaringType;
if (expression != null
&& memberDeclaringClrType != expression.Type
&& expression.Type.GetTypeInfo().IsAssignableFrom(memberDeclaringClrType.GetTypeInfo()))
{
expression = Expression.Convert(expression, memberDeclaringClrType);
}

return Expression.MakeMemberAccess(expression, member);
}

/// <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 Expression Assign(
[NotNull] this MemberExpression memberExpression,
[NotNull] Expression valueExpression)
{
if (memberExpression.Member is FieldInfo fieldInfo
&& fieldInfo.IsInitOnly)
{
return (BinaryExpression)Activator.CreateInstance(
_assignBinaryExpressionType,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[] { memberExpression, valueExpression },
null);
}

return Expression.Assign(memberExpression, valueExpression);
}

private static readonly Type _assignBinaryExpressionType
= typeof(Expression).Assembly.GetType("System.Linq.Expressions.AssignBinaryExpression");

/// <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
9 changes: 9 additions & 0 deletions src/EFCore/Extensions/NavigationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ namespace Microsoft.EntityFrameworkCore
/// </summary>
public static class NavigationExtensions
{
/// <summary>
/// Gets the <see cref="IClrCollectionAccessor"/> for this navigation property, which must be a collection
/// navigation.
/// </summary>
/// <param name="navigation"> The navigation property. </param>
/// <returns> The accessor. </returns>
public static IClrCollectionAccessor GetCollectionAccessor([NotNull] this INavigation navigation)
=> navigation.AsNavigation().CollectionAccessor;

/// <summary>
/// Gets a value indicating whether the given navigation property is the navigation property on the dependent entity
/// type that points to the principal entity.
Expand Down
74 changes: 74 additions & 0 deletions src/EFCore/Extensions/PropertyBaseExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.ChangeTracking;
Expand All @@ -16,6 +17,79 @@ namespace Microsoft.EntityFrameworkCore
/// </summary>
public static class PropertyBaseExtensions
{
/// <summary>
/// <para>
/// Gets the <see cref="PropertyInfo" /> or <see cref="FieldInfo" /> that should be used to
/// get or set a value for the given property.
/// </para>
/// <para>
/// Note that it is an error to call this method for a shadow property (<see cref="IsShadowProperty" />) since
/// such a property has no associated <see cref="MemberInfo" />.
/// </para>
/// </summary>
/// <param name="propertyBase"> The property. </param>
/// <param name="forMaterialization"> If true, then the member to use for query materialization will be returned. </param>
/// <param name="forSet">
/// If true, then the member to use for setting the property value will be returned, otherwise
/// the member to use for getting the property value will be returned.
/// </param>
/// <returns> The <see cref="MemberInfo" /> to use. </returns>
public static MemberInfo GetMemberInfo(
[NotNull] this IPropertyBase propertyBase,
bool forMaterialization,
bool forSet)
{
if (propertyBase.TryGetMemberInfo(forMaterialization, forSet, out var memberInfo, out var errorMessage))
{
return memberInfo;
}

throw new InvalidOperationException(errorMessage);
}

/// <summary>
/// <para>
/// Gets a <see cref="IClrPropertyGetter" /> for reading the value of this property.
/// </para>
/// <para>
/// Note that it is an error to call this method for a shadow property (<see cref="IsShadowProperty" />) since
/// such a property has no associated <see cref="MemberInfo" />.
/// </para>
/// </summary>
/// <param name="propertyBase"> The property. </param>
/// <returns> The accessor. </returns>
public static IClrPropertyGetter GetGetter([NotNull] this IPropertyBase propertyBase)
=> propertyBase.AsPropertyBase().Getter;

/// <summary>
/// <para>
/// Gets a <see cref="IClrPropertySetter" /> for writing the value of this property.
/// </para>
/// <para>
/// Note that it is an error to call this method for a shadow property (<see cref="IsShadowProperty" />) since
/// such a property has no associated <see cref="MemberInfo" />.
/// </para>
/// </summary>
/// <param name="propertyBase"> The property. </param>
/// <returns> The accessor. </returns>
public static IClrPropertySetter GetSetter([NotNull] this IPropertyBase propertyBase)
=> propertyBase.AsPropertyBase().Setter;

/// <summary>
/// <para>
/// Gets a <see cref="IClrPropertySetter" /> for writing the value of this property during materialization
/// of a query.
/// </para>
/// <para>
/// Note that it is an error to call this method for a shadow property (<see cref="IsShadowProperty" />) since
/// such a property has no associated <see cref="MemberInfo" />.
/// </para>
/// </summary>
/// <param name="propertyBase"> The property. </param>
/// <returns> The accessor. </returns>
public static IClrPropertySetter GetMaterializationSetter([NotNull] this IPropertyBase propertyBase)
=> propertyBase.AsPropertyBase().MaterializationSetter;

/// <summary>
/// Gets the name of the backing field for this property, or <c>null</c> if the backing field
/// is not known.
Expand Down
99 changes: 80 additions & 19 deletions src/EFCore/Infrastructure/ExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
{
/// <summary>
/// <para>
/// Extension methods for <see cref="Expression"/> types.
/// Extension methods for <see cref="Expression" /> types.
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
Expand All @@ -25,9 +25,74 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure
/// </summary>
public static class ExpressionExtensions
{
/// <summary>
/// Checks whether or not the given <see cref="MethodCallExpression" /> represents a call
/// to the <see cref="EF.Property{TProperty}" /> method.
/// </summary>
/// <param name="methodCallExpression"> The method-call expression. </param>
/// <returns> True if it is a call to <see cref="EF.Property{TProperty}" />; false otherwise. </returns>
public static bool IsEFProperty([NotNull] this MethodCallExpression methodCallExpression)
=> methodCallExpression.Method.IsEFPropertyMethod();

/// <summary>
/// Creates a <see cref="MemberExpression"></see> that represents accessing either a field or a property.
/// </summary>
/// <param name="expression"> An <see cref="Expression"></see> that represents the object that the member belongs to. </param>
/// <param name="member"> The <see cref="MemberInfo"></see> that describes the field or property to be accessed. </param>
/// <returns> The <see cref="MemberExpression"></see> that results from calling the appropriate factory method. </returns>
public static MemberExpression MakeMemberAccess(
[CanBeNull] this Expression expression,
[NotNull] MemberInfo member)
{
var memberDeclaringClrType = member.DeclaringType;
if (expression != null
&& memberDeclaringClrType != expression.Type
&& expression.Type.GetTypeInfo().IsAssignableFrom(memberDeclaringClrType.GetTypeInfo()))
{
expression = Expression.Convert(expression, memberDeclaringClrType);
}

return Expression.MakeMemberAccess(expression, member);
}

/// <summary>
/// Creates a <see cref="BinaryExpression"></see> that represents an assignment operation.
/// </summary>
/// <param name="memberExpression"> The member to which assignment will be made. </param>
/// <param name="valueExpression"> The value that will be assigned. </param>
/// <returns> The <see cref="BinaryExpression" /> representing the assignment binding. </returns>
public static Expression Assign(
[NotNull] this MemberExpression memberExpression,
[NotNull] Expression valueExpression)
{
if (memberExpression.Member is FieldInfo fieldInfo
&& fieldInfo.IsInitOnly)
{
return (BinaryExpression)Activator.CreateInstance(
_assignBinaryExpressionType,
BindingFlags.NonPublic | BindingFlags.Instance,
null,
new object[]
{
memberExpression, valueExpression
},
null);
}

return Expression.Assign(memberExpression, valueExpression);
}

private static readonly Type _assignBinaryExpressionType
= typeof(Expression).Assembly.GetType("System.Linq.Expressions.AssignBinaryExpression");

/// <summary>
/// If the given a method-call expression represents a call to <see cref="EF.Property{TProperty}"/>, then this
/// method extracts the entity expression and property name.
/// </summary>
/// <param name="methodCallExpression"> The method-call expression for <see cref="EF.Property{TProperty}"/> </param>
/// <param name="entityExpression"> The extracted entity access expression. </param>
/// <param name="propertyName"> The accessed property name. </param>
/// <returns> True if the method-call was for <see cref="EF.Property{TProperty}"/>; false otherwise. </returns>
public static bool TryGetEFPropertyArguments(
[NotNull] this MethodCallExpression methodCallExpression,
out Expression entityExpression,
Expand All @@ -45,20 +110,16 @@ public static bool TryGetEFPropertyArguments(
return false;
}

public static Expression CreateAssignExpression(
[NotNull] this MemberExpression left,
[NotNull] Expression right)
{
var leftType = left.Type;
if (leftType != right.Type
&& right.Type.GetTypeInfo().IsAssignableFrom(leftType.GetTypeInfo()))
{
right = Expression.Convert(right, leftType);
}

return left.Assign(right);
}

/// <summary>
/// <para>
/// Gets the <see cref="PropertyInfo"/> represented by a simple property-access expression.
/// </para>
/// <para>
/// This method is typically used to parse property access lambdas from fluent APIs.
/// </para>
/// </summary>
/// <param name="propertyAccessExpression"> The expression. </param>
/// <returns> The <see cref="PropertyInfo"/>. </returns>
public static PropertyInfo GetPropertyAccess([NotNull] this LambdaExpression propertyAccessExpression)
{
Debug.Assert(propertyAccessExpression.Parameters.Count == 1);
Expand Down Expand Up @@ -99,8 +160,8 @@ public static PropertyInfo GetPropertyAccess([NotNull] this LambdaExpression pro

/// <summary>
/// <para>
/// Returns a list of <see cref="PropertyInfo"/> extracted from the given simple
/// <see cref="LambdaExpression"/>.
/// Returns a list of <see cref="PropertyInfo" /> extracted from the given simple
/// <see cref="LambdaExpression" />.
/// </para>
/// <para>
/// Only simple expressions are supported, such as those used to reference a property.
Expand Down Expand Up @@ -140,8 +201,8 @@ var propertyPaths

/// <summary>
/// <para>
/// Returns a new expression with any see <see cref="ExpressionType.Convert"/> or
/// <see cref="ExpressionType.ConvertChecked"/> nodes removed from the head of the
/// Returns a new expression with any see <see cref="ExpressionType.Convert" /> or
/// <see cref="ExpressionType.ConvertChecked" /> nodes removed from the head of the
/// given expression tree/
/// </para>
/// <para>
Expand Down
1 change: 1 addition & 0 deletions src/EFCore/Internal/DbSetProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.Internal
Expand Down
Loading

0 comments on commit ec02e5c

Please sign in to comment.