From e794c53f02c4857f75fdac42851c243398a3526b Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Mon, 23 Mar 2020 16:00:46 -0700 Subject: [PATCH] Cleanup some of the internal API usage in Relational - Make EnumerableExtensions shared internal - Make ExpressionEqualityComparer public - Suppress Internal usage in EntityFrameworkRelationalServicesBuilder - Make ConfigurationSourceExtensions public - Suppress convention-less modelBuilder creation in Migration/Snapshot --- ...yableMethodTranslatingExpressionVisitor.cs | 8 +- .../Storage/Internal/InMemoryTable.cs | 3 +- .../RelationalEntityTypeExtensions.cs | 1 - .../RelationalQueryableExtensions.cs | 1 - ...ntityFrameworkRelationalServicesBuilder.cs | 6 + .../Infrastructure/ModelSnapshot.cs | 2 + .../RelationalModelValidator.cs | 2 +- .../Conventions/SharedTableConvention.cs | 4 +- src/EFCore.Relational/Metadata/IColumnBase.cs | 1 - .../Metadata/Internal/ColumnExtensions.cs | 2 +- .../Internal/ColumnMappingExtensions.cs | 2 +- .../Internal/DatabaseModelExtensions.cs | 1 + .../ForeignKeyConstraintExtensions.cs | 4 +- .../Metadata/Internal/TableExtensions.cs | 2 +- .../Metadata/Internal/TableIndexExtensions.cs | 2 +- .../Internal/TableMappingExtensions.cs | 2 +- .../Internal/UniqueConstraintExtensions.cs | 2 +- .../Metadata/Internal/ViewColumnExtensions.cs | 2 +- .../Internal/ViewColumnMappingExtensions.cs | 2 +- .../Metadata/Internal/ViewExtensions.cs | 2 +- .../Internal/ViewMappingExtensions.cs | 2 +- src/EFCore.Relational/Migrations/Migration.cs | 2 + .../Operations/UpdateDataOperation.cs | 1 - .../Query/RelationalEntityShaperExpression.cs | 2 - ...yableMethodTranslatingExpressionVisitor.cs | 9 +- .../Query/SqlExpressions/SelectExpression.cs | 1 - .../ConventionEntityTypeExtensions.cs | 19 ++ src/EFCore/Extensions/EntityTypeExtensions.cs | 57 ++++++ src/EFCore/Extensions/ForeignKeyExtensions.cs | 10 + .../Internal/EFPropertyExtensions.cs | 21 -- .../Internal/EnumerableExtensions.cs | 190 ------------------ .../Extensions/Internal/TypeExtensions.cs | 17 -- .../Extensions/MutableEntityTypeExtensions.cs | 19 ++ .../Extensions/PropertyBaseExtensions.cs | 8 + .../Infrastructure/AnnotatableExtensions.cs | 33 +++ .../Infrastructure/ExpressionExtensions.cs | 31 +++ .../Internal/AnnotatableExtensions.cs | 49 ----- src/EFCore/Infrastructure/TypeExtensions.cs | 19 ++ .../Metadata/ConfigurationSourceExtensions.cs | 23 +++ .../Internal/ConfigurationSourceExtensions.cs | 39 ---- .../Metadata/Internal/EntityTypeExtensions.cs | 54 +---- .../Metadata/Internal/ForeignKeyExtensions.cs | 10 +- .../Metadata/Internal/IndexExtensions.cs | 1 + src/EFCore/Metadata/Internal/KeyExtensions.cs | 1 + .../Internal/MetadataDebugStringOptions.cs | 65 ------ .../Metadata/Internal/ModelExtensions.cs | 1 + .../Internal/PropertyBaseExtensions.cs | 9 - .../Metadata/MetadataDebugStringOptions.cs | 44 ++++ .../Query/CompiledQueryCacheKeyGenerator.cs | 1 - .../ExpressionEqualityComparer.cs | 29 +-- ...ingExpressionVisitor.ExpressionVisitors.cs | 6 +- src/Shared/EnumerableExtensions.cs | 141 +++++++++++++ .../TestUtilities/EnumerableExtensions.cs | 30 --- .../Query/ExpressionEqualityComparerTest.cs | 1 - 54 files changed, 455 insertions(+), 541 deletions(-) delete mode 100644 src/EFCore/Extensions/Internal/EnumerableExtensions.cs delete mode 100644 src/EFCore/Infrastructure/Internal/AnnotatableExtensions.cs delete mode 100644 src/EFCore/Metadata/Internal/ConfigurationSourceExtensions.cs delete mode 100644 src/EFCore/Metadata/Internal/MetadataDebugStringOptions.cs create mode 100644 src/EFCore/Metadata/MetadataDebugStringOptions.cs rename src/EFCore/Query/{Internal => }/ExpressionEqualityComparer.cs (93%) create mode 100644 src/Shared/EnumerableExtensions.cs delete mode 100644 test/EFCore.Specification.Tests/TestUtilities/EnumerableExtensions.cs diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs index f1ae01ac5d9..b9c275677e9 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs @@ -1044,12 +1044,12 @@ private Expression TryExpand(Expression source, MemberIdentity member) .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var outerKey = entityShaperExpression.CreateKeyAccessExpression( + var outerKey = entityShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyAccessExpression( + var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, @@ -1092,12 +1092,12 @@ ProjectionBindingExpression projectionBindingExpression .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var outerKey = entityShaperExpression.CreateKeyAccessExpression( + var outerKey = entityShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyAccessExpression( + var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs index cf787bf8fdc..a4d3db3fa9e 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs @@ -107,8 +107,7 @@ public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator _integerGenerators = new Dictionary(); } - // WARNING: The in-memory provider is using EF internal code here. This should not be copied by other providers. See #15096 - var propertyIndex = EntityFrameworkCore.Metadata.Internal.PropertyBaseExtensions.GetIndex(property); + var propertyIndex = property.GetIndex(); if (!_integerGenerators.TryGetValue(propertyIndex, out var generator)) { generator = new InMemoryIntegerValueGenerator(propertyIndex); diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index f5471a0dcf9..381f1712afc 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -1,7 +1,6 @@ // 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.Collections.Generic; using System.Linq; using JetBrains.Annotations; diff --git a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs index 4b415757ef4..39466a29551 100644 --- a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs @@ -6,7 +6,6 @@ using System.Data.Common; using System.Linq; using System.Linq.Expressions; -using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Query; diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index d1038743cc2..9e5fd9ee1e7 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -53,6 +53,7 @@ public class EntityFrameworkRelationalServicesBuilder : EntityFrameworkServicesB /// [EntityFrameworkInternal] public static readonly IDictionary RelationalServices +#pragma warning disable EF1001 // Internal EF Core API usage. = new Dictionary { { typeof(IKeyValueIndexFactorySource), new ServiceCharacteristics(ServiceLifetime.Singleton) }, @@ -94,6 +95,7 @@ public static readonly IDictionary RelationalServi { typeof(IMemberTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }, { typeof(IRelationalParameterBasedQueryTranslationPostprocessorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) } }; +#pragma warning restore EF1001 // Internal EF Core API usage. /// /// Used by relational database providers to create a new for @@ -116,7 +118,9 @@ protected override ServiceCharacteristics GetServiceCharacteristics(Type service { return RelationalServices.TryGetValue(serviceType, out var characteristics) ? characteristics +#pragma warning disable EF1001 // Internal EF Core API usage. : base.GetServiceCharacteristics(serviceType); +#pragma warning restore EF1001 // Internal EF Core API usage. } /// @@ -173,6 +177,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() TryAdd(); TryAdd(); +#pragma warning disable EF1001 // Internal EF Core API usage. ServiceCollectionMap.GetInfrastructure() .AddDependencySingleton() .AddDependencySingleton() @@ -205,6 +210,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped(); +#pragma warning restore EF1001 // Internal EF Core API usage. return base.TryAddCoreServices(); } diff --git a/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs b/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs index b461bf806dc..46872cd81ce 100644 --- a/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs +++ b/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs @@ -16,12 +16,14 @@ public abstract class ModelSnapshot private IModel CreateModel() { +#pragma warning disable EF1001 // Internal EF Core API usage. var model = new Model(); var modelBuilder = new ModelBuilder(model); BuildModel(modelBuilder); return model; +#pragma warning restore EF1001 // Internal EF Core API usage. } /// diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 80f96cdb351..8fc57d13606 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -714,7 +714,7 @@ protected virtual void ValidateInheritanceMapping( foreach (var entityType in model.GetEntityTypes()) { if (entityType.BaseType != null - && ((EntityType)entityType).FindAnnotation(RelationalAnnotationNames.TableName)?.GetConfigurationSource() + && ((IConventionEntityType)entityType).FindAnnotation(RelationalAnnotationNames.TableName)?.GetConfigurationSource() == ConfigurationSource.Explicit) { throw new InvalidOperationException( diff --git a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs index 8d095b63666..f61d1460fa7 100644 --- a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs @@ -157,9 +157,9 @@ private static void TryUniquifyColumnNames( continue; } - var identifyingMemberInfo = property.GetIdentifyingMemberInfo(); + var identifyingMemberInfo = property.PropertyInfo ?? (MemberInfo)property.FieldInfo; if (identifyingMemberInfo != null - && identifyingMemberInfo.IsSameAs(otherProperty.GetIdentifyingMemberInfo())) + && identifyingMemberInfo.IsSameAs(otherProperty.PropertyInfo ?? (MemberInfo)otherProperty.FieldInfo)) { continue; } diff --git a/src/EFCore.Relational/Metadata/IColumnBase.cs b/src/EFCore.Relational/Metadata/IColumnBase.cs index 4d4a20b8a04..3c6dde37cdf 100644 --- a/src/EFCore.Relational/Metadata/IColumnBase.cs +++ b/src/EFCore.Relational/Metadata/IColumnBase.cs @@ -1,7 +1,6 @@ // 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.Collections; using System.Collections.Generic; using Microsoft.EntityFrameworkCore.Infrastructure; diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ColumnExtensions.cs index e8b99b83267..e4abddedcd6 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnMappingExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ColumnMappingExtensions.cs index fb7cc070650..ec9e6ba450a 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnMappingExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnMappingExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/DatabaseModelExtensions.cs b/src/EFCore.Relational/Metadata/Internal/DatabaseModelExtensions.cs index 8a17a1d6008..0ae95755a4a 100644 --- a/src/EFCore.Relational/Metadata/Internal/DatabaseModelExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/DatabaseModelExtensions.cs @@ -3,6 +3,7 @@ using System.Text; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal diff --git a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraintExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraintExtensions.cs index 784e886477b..f9af1e88142 100644 --- a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraintExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraintExtensions.cs @@ -1,11 +1,9 @@ // 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.Collections.Generic; -using System.Linq; using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; namespace Microsoft.EntityFrameworkCore.Metadata.Internal diff --git a/src/EFCore.Relational/Metadata/Internal/TableExtensions.cs b/src/EFCore.Relational/Metadata/Internal/TableExtensions.cs index eafc78c8d81..467e964294e 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableExtensions.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/TableIndexExtensions.cs b/src/EFCore.Relational/Metadata/Internal/TableIndexExtensions.cs index 66e20efba1f..5f39bf64cbd 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableIndexExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableIndexExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/TableMappingExtensions.cs b/src/EFCore.Relational/Metadata/Internal/TableMappingExtensions.cs index 6810b25efa1..32d3b3ca9cc 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableMappingExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableMappingExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/UniqueConstraintExtensions.cs b/src/EFCore.Relational/Metadata/Internal/UniqueConstraintExtensions.cs index 8d34ab71876..63fc9067336 100644 --- a/src/EFCore.Relational/Metadata/Internal/UniqueConstraintExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/UniqueConstraintExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/ViewColumnExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ViewColumnExtensions.cs index b6465babec9..a6addb2d689 100644 --- a/src/EFCore.Relational/Metadata/Internal/ViewColumnExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ViewColumnExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/ViewColumnMappingExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ViewColumnMappingExtensions.cs index c4ab203da93..3da06d088ee 100644 --- a/src/EFCore.Relational/Metadata/Internal/ViewColumnMappingExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ViewColumnMappingExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/ViewExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ViewExtensions.cs index a208e6041cb..691d2966ef8 100644 --- a/src/EFCore.Relational/Metadata/Internal/ViewExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ViewExtensions.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Metadata/Internal/ViewMappingExtensions.cs b/src/EFCore.Relational/Metadata/Internal/ViewMappingExtensions.cs index 72dd61df650..5f22e49134b 100644 --- a/src/EFCore.Relational/Metadata/Internal/ViewMappingExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/ViewMappingExtensions.cs @@ -3,7 +3,7 @@ using System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Infrastructure.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore.Relational/Migrations/Migration.cs b/src/EFCore.Relational/Migrations/Migration.cs index aa1c065512d..959b67b8009 100644 --- a/src/EFCore.Relational/Migrations/Migration.cs +++ b/src/EFCore.Relational/Migrations/Migration.cs @@ -33,12 +33,14 @@ public virtual IModel TargetModel { IModel Create() { +#pragma warning disable EF1001 // Internal EF Core API usage. var model = new Model(); var modelBuilder = new ModelBuilder(model); BuildTargetModel(modelBuilder); model = (Model)RelationalModel.Add(model, null); return model.FinalizeModel(); +#pragma warning restore EF1001 // Internal EF Core API usage. } return _targetModel ??= Create(); diff --git a/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs b/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs index a8ba583e1a1..34f990fe2ba 100644 --- a/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs +++ b/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs @@ -6,7 +6,6 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Migrations.Internal; using Microsoft.EntityFrameworkCore.Update; using Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs b/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs index 36401f594fb..dd0d961b06f 100644 --- a/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs +++ b/src/EFCore.Relational/Query/RelationalEntityShaperExpression.cs @@ -1,13 +1,11 @@ // 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.Linq; using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 55eb3e54572..1835fcf3f14 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -10,7 +10,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; @@ -1186,12 +1185,12 @@ private Expression TryExpand(Expression source, MemberIdentity member) var innerSequenceType = innerShapedQuery.Type.TryGetSequenceType(); var correlationPredicateParameter = Expression.Parameter(innerSequenceType); - var outerKey = entityShaperExpression.CreateKeyAccessExpression( + var outerKey = entityShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, makeNullable); - var innerKey = correlationPredicateParameter.CreateKeyAccessExpression( + var innerKey = correlationPredicateParameter.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, @@ -1247,12 +1246,12 @@ private Expression TryExpand(Expression source, MemberIdentity member) .Select(p => p.ClrType) .Any(t => t.IsNullableType()); - var outerKey = entityShaperExpression.CreateKeyAccessExpression( + var outerKey = entityShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.Properties : foreignKey.PrincipalKey.Properties, makeNullable); - var innerKey = innerShapedQuery.ShaperExpression.CreateKeyAccessExpression( + var innerKey = innerShapedQuery.ShaperExpression.CreateKeyValueReadExpression( navigation.IsOnDependent ? foreignKey.PrincipalKey.Properties : foreignKey.Properties, diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index c8cbe4c3c74..9f88bf795c2 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -10,7 +10,6 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.SqlExpressions diff --git a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs index f811ce7f98a..01b8cae1301 100644 --- a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs +++ b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs @@ -31,6 +31,16 @@ public static class ConventionEntityTypeExtensions public static IConventionEntityType GetRootType([NotNull] this IConventionEntityType entityType) => (IConventionEntityType)((IEntityType)entityType).GetRootType(); + /// + /// Gets all types in the model which a given entity type derives from. + /// + /// The type to find base types. + /// + /// The base types. + /// + public static IEnumerable GetAllBaseTypes([NotNull] this IConventionEntityType entityType) + => entityType.GetAllBaseTypesAscending().Reverse().Cast(); + /// /// Gets all types in the model that derive from a given entity type. /// @@ -79,6 +89,15 @@ public static IEnumerable GetAllBaseTypesInclusiveAscendi } } + + /// + /// Returns all types in hierarchy of the given . + /// + /// The entity type. + /// All types in the hierarchy. + public static IEnumerable GetTypesInHierarchy([NotNull] this IConventionEntityType entityType) + => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()).Cast(); + /// /// /// Gets all keys declared on the given . diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs index 7b3dd841b4d..495c779c30b 100644 --- a/src/EFCore/Extensions/EntityTypeExtensions.cs +++ b/src/EFCore/Extensions/EntityTypeExtensions.cs @@ -48,6 +48,16 @@ public static IEntityType GetRootType([NotNull] this IEntityType entityType) return entityType.BaseType?.GetRootType() ?? entityType; } + /// + /// Gets all types in the model which a given entity type derives from. + /// + /// The type to find base types. + /// + /// The base types. + /// + public static IEnumerable GetAllBaseTypes([NotNull] this IEntityType entityType) + => entityType.GetAllBaseTypesAscending().Reverse(); + /// /// Gets all types in the model that derive from a given entity type. /// @@ -182,6 +192,27 @@ public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNul } } + /// + /// Determines if an entity type is in same hierarchy as the given entity type. + /// + /// An entity type for hierachy. + /// The entity type to check if it is in same hierarchy from . + /// + /// true if is in same hierarchy as , + /// otherwise false. + /// + public static bool IsSameHierarchy([NotNull] this IEntityType firstEntityType, [NotNull] IEntityType secondEntityType) + => firstEntityType.IsAssignableFrom(secondEntityType) + || secondEntityType.IsAssignableFrom(firstEntityType); + + /// + /// Returns all types in hierarchy of the given . + /// + /// The entity type. + /// All types in the hierarchy. + public static IEnumerable GetTypesInHierarchy([NotNull] this IEntityType entityType) + => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()); + /// /// /// Gets all keys declared on the given . @@ -375,6 +406,32 @@ public static bool HasDefiningNavigation([NotNull] this IEntityType entityType) public static bool IsOwned([NotNull] this IEntityType entityType) => entityType.GetForeignKeys().Any(fk => fk.IsOwnership); + /// + /// Gets a value indicating whether given entity type is in ownership path for this entity type. + /// + /// The entity type. + /// Entity type to search for in ownership path. + /// true if is in ownership path of , + /// otherwise false. + public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] IEntityType targetType) + { + var owner = entityType; + while (true) + { + var ownOwnership = owner.FindOwnership(); + if (ownOwnership == null) + { + return false; + } + + owner = ownOwnership.PrincipalEntityType; + if (owner.IsAssignableFrom(targetType)) + { + return true; + } + } + } + /// /// Gets the primary or alternate key that is defined on the given property. Returns null if no key is defined /// for the given property. diff --git a/src/EFCore/Extensions/ForeignKeyExtensions.cs b/src/EFCore/Extensions/ForeignKeyExtensions.cs index 2dd4db6afd5..a9ce9c8f580 100644 --- a/src/EFCore/Extensions/ForeignKeyExtensions.cs +++ b/src/EFCore/Extensions/ForeignKeyExtensions.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore @@ -49,5 +50,14 @@ public static IEntityType GetRelatedEntityType([NotNull] this IForeignKey foreig /// public static INavigation GetNavigation([NotNull] this IForeignKey foreignKey, bool pointsToPrincipal) => pointsToPrincipal ? foreignKey.DependentToPrincipal : foreignKey.PrincipalToDependent; + + /// + /// Gets a value indicating whether given foreign key is defined in same hierarchy. + /// + /// The foreign key to check. + /// true if is defined in same hierarchy, + /// otherwise false. + public static bool IsIntraHierarchical([NotNull] this IForeignKey foreignKey) + => foreignKey.DeclaringEntityType.IsSameHierarchy(foreignKey.PrincipalEntityType); } } diff --git a/src/EFCore/Extensions/Internal/EFPropertyExtensions.cs b/src/EFCore/Extensions/Internal/EFPropertyExtensions.cs index 67223b0b928..ea0d5900d9e 100644 --- a/src/EFCore/Extensions/Internal/EFPropertyExtensions.cs +++ b/src/EFCore/Extensions/Internal/EFPropertyExtensions.cs @@ -24,27 +24,6 @@ namespace Microsoft.EntityFrameworkCore.Internal // ReSharper disable once InconsistentNaming public static class EFPropertyExtensions { - /// - /// 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. - /// - public static Expression CreateKeyAccessExpression( - [NotNull] this Expression target, - [NotNull] IReadOnlyList properties, - bool makeNullable = false) - => properties.Count == 1 - ? target.CreateEFPropertyExpression(properties[0], makeNullable) - : Expression.New( - AnonymousObject.AnonymousObjectCtor, - Expression.NewArrayInit( - typeof(object), - properties - .Select(p => Expression.Convert(target.CreateEFPropertyExpression(p, makeNullable), typeof(object))) - .Cast() - .ToArray())); - /// /// 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 diff --git a/src/EFCore/Extensions/Internal/EnumerableExtensions.cs b/src/EFCore/Extensions/Internal/EnumerableExtensions.cs deleted file mode 100644 index 4c35a3e71d0..00000000000 --- a/src/EFCore/Extensions/Internal/EnumerableExtensions.cs +++ /dev/null @@ -1,190 +0,0 @@ -// 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.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using JetBrains.Annotations; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore.Internal -{ - /// - /// 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. - /// - [DebuggerStepThrough] - public static class EnumerableExtensions - { - /// - /// 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. - /// - public static IOrderedEnumerable OrderByOrdinal( - [NotNull] this IEnumerable source, - [NotNull] Func keySelector) - => source.OrderBy(keySelector, StringComparer.Ordinal); - - /// - /// 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. - /// - public static IEnumerable Distinct( - [NotNull] this IEnumerable source, - [NotNull] Func comparer) - where T : class - => source.Distinct(new DynamicEqualityComparer(comparer)); - - private sealed class DynamicEqualityComparer : IEqualityComparer - where T : class - { - private readonly Func _func; - - public DynamicEqualityComparer(Func func) - { - _func = func; - } - - public bool Equals(T x, T y) => _func(x, y); - - public int GetHashCode(T obj) => 0; - } - - /// - /// 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. - /// - public static string Join( - [NotNull] this IEnumerable source, - [NotNull] string separator = ", ") - => string.Join(separator, source); - - /// - /// 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. - /// - public static bool StructuralSequenceEqual( - [NotNull] this IEnumerable first, - [NotNull] IEnumerable second) - { - if (ReferenceEquals(first, second)) - { - return true; - } - - using var firstEnumerator = first.GetEnumerator(); - using var secondEnumerator = second.GetEnumerator(); - while (firstEnumerator.MoveNext()) - { - if (!secondEnumerator.MoveNext() - || !StructuralComparisons.StructuralEqualityComparer - .Equals(firstEnumerator.Current, secondEnumerator.Current)) - { - return false; - } - } - - return !secondEnumerator.MoveNext(); - } - - /// - /// 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. - /// - public static bool StartsWith( - [NotNull] this IEnumerable first, - [NotNull] IEnumerable second) - { - if (ReferenceEquals(first, second)) - { - return true; - } - - using (var firstEnumerator = first.GetEnumerator()) - { - using var secondEnumerator = second.GetEnumerator(); - while (secondEnumerator.MoveNext()) - { - if (!firstEnumerator.MoveNext() - || !Equals(firstEnumerator.Current, secondEnumerator.Current)) - { - return false; - } - } - } - - return true; - } - - /// - /// 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. - /// - public static int IndexOf([NotNull] this IEnumerable source, [NotNull] T item) - => IndexOf(source, item, EqualityComparer.Default); - - /// - /// 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. - /// - public static int IndexOf( - [NotNull] this IEnumerable source, [NotNull] T item, - [NotNull] IEqualityComparer comparer) - => source.Select( - (x, index) => - comparer.Equals(item, x) ? index : -1) - .FirstOr(x => x != -1, -1); - - /// - /// 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. - /// - public static T FirstOr([NotNull] this IEnumerable source, [NotNull] T alternate) - => source.DefaultIfEmpty(alternate).First(); - - /// - /// 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. - /// - public static T FirstOr([NotNull] this IEnumerable source, [NotNull] Func predicate, [NotNull] T alternate) - => source.Where(predicate).FirstOr(alternate); - - /// - /// 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. - /// - public static bool Any([NotNull] this IEnumerable source) - { - foreach (var _ in source) - { - return true; - } - - return false; - } - } -} diff --git a/src/EFCore/Extensions/Internal/TypeExtensions.cs b/src/EFCore/Extensions/Internal/TypeExtensions.cs index a2fa302a057..06f586a4f98 100644 --- a/src/EFCore/Extensions/Internal/TypeExtensions.cs +++ b/src/EFCore/Extensions/Internal/TypeExtensions.cs @@ -197,23 +197,6 @@ public static string GenerateParameterName([NotNull] this Type type) return removeLowerCase.Length > 0 ? removeLowerCase.ToLower() : type.Name.ToLower().Substring(0, 1); } - /// - /// 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. - /// - public static bool IsQueryableType([NotNull] this Type type) - { - if (type.IsGenericType - && type.GetGenericTypeDefinition() == typeof(IQueryable<>)) - { - return true; - } - - return type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueryable<>)); - } - /// /// 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 diff --git a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs index faef543c416..5343b5b7302 100644 --- a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs +++ b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; @@ -30,6 +31,16 @@ public static class MutableEntityTypeExtensions public static IMutableEntityType GetRootType([NotNull] this IMutableEntityType entityType) => (IMutableEntityType)((IEntityType)entityType).GetRootType(); + /// + /// Gets all types in the model which a given entity type derives from. + /// + /// The type to find base types. + /// + /// The base types. + /// + public static IEnumerable GetAllBaseTypes([NotNull] this IMutableEntityType entityType) + => entityType.GetAllBaseTypesAscending().Reverse().Cast(); + /// /// Gets all types in the model that derive from a given entity type. /// @@ -54,6 +65,14 @@ public static IEnumerable GetDerivedTypesInclusive([NotNull] public static IEnumerable GetDirectlyDerivedTypes([NotNull] this IMutableEntityType entityType) => ((EntityType)entityType).GetDirectlyDerivedTypes(); + /// + /// Returns all types in hierarchy of the given . + /// + /// The entity type. + /// All types in the hierarchy. + public static IEnumerable GetTypesInHierarchy([NotNull] this IMutableEntityType entityType) + => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()).Cast(); + /// /// /// Gets all keys declared on the given . diff --git a/src/EFCore/Extensions/PropertyBaseExtensions.cs b/src/EFCore/Extensions/PropertyBaseExtensions.cs index 894477b47a4..5e8da01d71a 100644 --- a/src/EFCore/Extensions/PropertyBaseExtensions.cs +++ b/src/EFCore/Extensions/PropertyBaseExtensions.cs @@ -93,5 +93,13 @@ public static bool IsShadowProperty([NotNull] this IPropertyBase property) public static bool IsIndexerProperty([NotNull] this IPropertyBase property) => Check.NotNull(property, nameof(property)).GetIdentifyingMemberInfo() is PropertyInfo propertyInfo && propertyInfo == property.DeclaringType.FindIndexerPropertyInfo(); + + /// + /// Gets the property index for this property. + /// + /// The property for which the property index will be returned. + /// The index of the property. + public static int GetIndex([NotNull] this IPropertyBase property) + => property.GetPropertyIndexes().Index; } } diff --git a/src/EFCore/Infrastructure/AnnotatableExtensions.cs b/src/EFCore/Infrastructure/AnnotatableExtensions.cs index 9630bff1439..4729846b7fb 100644 --- a/src/EFCore/Infrastructure/AnnotatableExtensions.cs +++ b/src/EFCore/Infrastructure/AnnotatableExtensions.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; +using System.Text; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Utilities; @@ -32,5 +34,36 @@ public static IAnnotation GetAnnotation([NotNull] this IAnnotatable annotatable, return annotation; } + + /// + /// Gets the debug string for all annotations declared on the object. + /// + /// The object to get the annotations to print in debug string. + /// The indentation to use when printing. + /// Debug string representation of all annotations. + public static string AnnotationsToDebugString([NotNull] this IAnnotatable annotatable, [NotNull] string indent = "") + { + var annotations = annotatable.GetAnnotations().ToList(); + if (annotations.Count == 0) + { + return ""; + } + + var builder = new StringBuilder(); + + builder.AppendLine().Append(indent).Append("Annotations: "); + foreach (var annotation in annotations) + { + builder + .AppendLine() + .Append(indent) + .Append(" ") + .Append(annotation.Name) + .Append(": ") + .Append(annotation.Value); + } + + return builder.ToString(); + } } } diff --git a/src/EFCore/Infrastructure/ExpressionExtensions.cs b/src/EFCore/Infrastructure/ExpressionExtensions.cs index e7f6c80df1c..ab20345221e 100644 --- a/src/EFCore/Infrastructure/ExpressionExtensions.cs +++ b/src/EFCore/Infrastructure/ExpressionExtensions.cs @@ -12,6 +12,7 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; @@ -268,7 +269,37 @@ public static readonly MethodInfo ValueBufferTryReadValueMethod [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TValue ValueBufferTryReadValue( +#pragma warning disable IDE0060 // Remove unused parameter in ValueBuffer valueBuffer, int index, IPropertyBase property) +#pragma warning restore IDE0060 // Remove unused parameter => valueBuffer[index] is TValue value ? value : default; + + /// + /// + /// Creates an tree representing reading of a key values on given expression. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The expression that will be root for generated read operation. + /// The list of properties to use to generate key values. + /// A value indicating if the key values should be read nullable. + /// An expression to read the key values. + public static Expression CreateKeyValueReadExpression( + [NotNull] this Expression target, + [NotNull] IReadOnlyList properties, + bool makeNullable = false) + => properties.Count == 1 + ? target.CreateEFPropertyExpression(properties[0], makeNullable) + : Expression.New( + AnonymousObject.AnonymousObjectCtor, + Expression.NewArrayInit( + typeof(object), + properties + .Select(p => Expression.Convert(target.CreateEFPropertyExpression(p, makeNullable), typeof(object))) + .Cast() + .ToArray())); } } diff --git a/src/EFCore/Infrastructure/Internal/AnnotatableExtensions.cs b/src/EFCore/Infrastructure/Internal/AnnotatableExtensions.cs deleted file mode 100644 index 2f5980a2c08..00000000000 --- a/src/EFCore/Infrastructure/Internal/AnnotatableExtensions.cs +++ /dev/null @@ -1,49 +0,0 @@ -// 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.Linq; -using System.Text; -using JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Infrastructure.Internal -{ - /// - /// 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. - /// - public static class AnnotatableExtensions - { - /// - /// 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. - /// - public static string AnnotationsToDebugString([NotNull] this IAnnotatable annotatable, [NotNull] string indent = "") - { - var annotations = annotatable.GetAnnotations().ToList(); - if (annotations.Count == 0) - { - return ""; - } - - var builder = new StringBuilder(); - - builder.AppendLine().Append(indent).Append("Annotations: "); - foreach (var annotation in annotations) - { - builder - .AppendLine() - .Append(indent) - .Append(" ") - .Append(annotation.Name) - .Append(": ") - .Append(annotation.Value); - } - - return builder.ToString(); - } - } -} diff --git a/src/EFCore/Infrastructure/TypeExtensions.cs b/src/EFCore/Infrastructure/TypeExtensions.cs index 8b19379fce5..55a5960eaca 100644 --- a/src/EFCore/Infrastructure/TypeExtensions.cs +++ b/src/EFCore/Infrastructure/TypeExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Internal; @@ -35,5 +36,23 @@ public static class TypeExtensions /// The human-readable name. public static string ShortDisplayName([NotNull] this Type type) => type.DisplayName(fullName: false); + + /// + /// Gets a value indicating whether this type is same as or implements + /// + /// The type to check. + /// + /// True if the type is same as or implements , otherwise false. + /// + public static bool IsQueryableType([NotNull] this Type type) + { + if (type.IsGenericType + && type.GetGenericTypeDefinition() == typeof(IQueryable<>)) + { + return true; + } + + return type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueryable<>)); + } } } diff --git a/src/EFCore/Metadata/ConfigurationSourceExtensions.cs b/src/EFCore/Metadata/ConfigurationSourceExtensions.cs index 3f315cb7ee1..5ad5b8efb7d 100644 --- a/src/EFCore/Metadata/ConfigurationSourceExtensions.cs +++ b/src/EFCore/Metadata/ConfigurationSourceExtensions.cs @@ -68,5 +68,28 @@ public static bool OverridesStrictly(this ConfigurationSource newConfigurationSo /// true if the configuration source always takes precedence over the other configuration source. public static bool OverridesStrictly(this ConfigurationSource? newConfigurationSource, ConfigurationSource? oldConfigurationSource) => newConfigurationSource.HasValue && newConfigurationSource.Value.OverridesStrictly(oldConfigurationSource); + + /// + /// Returns the configuration source which has higher priority. + /// + /// The left configuration source. + /// The right configuration source. + /// The configuration source with higher priority. + [ContractAnnotation("left:notnull => notnull;right:notnull => notnull")] + public static ConfigurationSource? Max(this ConfigurationSource? left, ConfigurationSource? right) + => left.Overrides(right) + ? left + : right; + + /// + /// Returns the configuration source which has higher priority. + /// + /// The left configuration source. + /// The right configuration source. + /// The configuration source with higher priority. + public static ConfigurationSource Max(this ConfigurationSource left, ConfigurationSource? right) + => left.Overrides(right) + ? left + : right.Value; } } diff --git a/src/EFCore/Metadata/Internal/ConfigurationSourceExtensions.cs b/src/EFCore/Metadata/Internal/ConfigurationSourceExtensions.cs deleted file mode 100644 index f80d2c7f641..00000000000 --- a/src/EFCore/Metadata/Internal/ConfigurationSourceExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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 JetBrains.Annotations; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// 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. - /// - public static class ConfigurationSourceExtensions - { - /// - /// 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. - /// - [ContractAnnotation("left:notnull => notnull;right:notnull => notnull")] - public static ConfigurationSource? Max(this ConfigurationSource? left, ConfigurationSource? right) - => left.Overrides(right) - ? left - : right; - - /// - /// 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. - /// - public static ConfigurationSource Max(this ConfigurationSource left, ConfigurationSource? right) - => left.Overrides(right) - ? left - : right.Value; - } -} diff --git a/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs index df24624bae6..57583710de7 100644 --- a/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs +++ b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; using Microsoft.EntityFrameworkCore.Utilities; @@ -47,15 +48,6 @@ public static MemberInfo GetNavigationMemberInfo( return memberInfo; } - /// - /// 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. - /// - public static IEnumerable GetAllBaseTypes([NotNull] this IEntityType entityType) - => entityType.GetAllBaseTypesAscending().Reverse(); - /// /// 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 @@ -188,31 +180,6 @@ public static IEntityType FindInOwnershipPath([NotNull] this IEntityType entityT public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] Type targetType) => entityType.FindInOwnershipPath(targetType) != null; - /// - /// 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. - /// - public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] IEntityType targetType) - { - var owner = entityType; - while (true) - { - var ownOwnership = owner.FindOwnership(); - if (ownOwnership == null) - { - return false; - } - - owner = ownOwnership.PrincipalEntityType; - if (owner.IsAssignableFrom(targetType)) - { - return true; - } - } - } - /// /// 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 @@ -374,25 +341,6 @@ public static PropertyCounts CalculateCounts([NotNull] this EntityType entityTyp public static Func GetEmptyShadowValuesFactory([NotNull] this IEntityType entityType) => entityType.AsEntityType().EmptyShadowValuesFactory; - /// - /// 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. - /// - public static IEnumerable GetTypesInHierarchy([NotNull] this IEntityType entityType) - => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()); - - /// - /// 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. - /// - public static bool IsSameHierarchy([NotNull] this IEntityType firstEntityType, [NotNull] IEntityType secondEntityType) - => firstEntityType.IsAssignableFrom(secondEntityType) - || secondEntityType.IsAssignableFrom(firstEntityType); - /// /// 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 diff --git a/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs b/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs index dd8cde3156d..60928390255 100644 --- a/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs +++ b/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs @@ -8,6 +8,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal @@ -29,15 +30,6 @@ public static class ForeignKeyExtensions public static bool IsSelfReferencing([NotNull] this IForeignKey foreignKey) => foreignKey.DeclaringEntityType == foreignKey.PrincipalEntityType; - /// - /// 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. - /// - public static bool IsIntraHierarchical([NotNull] this IForeignKey foreignKey) - => foreignKey.DeclaringEntityType.IsSameHierarchy(foreignKey.PrincipalEntityType); - /// /// 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 diff --git a/src/EFCore/Metadata/Internal/IndexExtensions.cs b/src/EFCore/Metadata/Internal/IndexExtensions.cs index 03044ad5f42..d90626f871f 100644 --- a/src/EFCore/Metadata/Internal/IndexExtensions.cs +++ b/src/EFCore/Metadata/Internal/IndexExtensions.cs @@ -6,6 +6,7 @@ using System.Text; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal diff --git a/src/EFCore/Metadata/Internal/KeyExtensions.cs b/src/EFCore/Metadata/Internal/KeyExtensions.cs index e4c5262e0c3..8c1b865f28a 100644 --- a/src/EFCore/Metadata/Internal/KeyExtensions.cs +++ b/src/EFCore/Metadata/Internal/KeyExtensions.cs @@ -7,6 +7,7 @@ using System.Text; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal diff --git a/src/EFCore/Metadata/Internal/MetadataDebugStringOptions.cs b/src/EFCore/Metadata/Internal/MetadataDebugStringOptions.cs deleted file mode 100644 index 8695381e4ec..00000000000 --- a/src/EFCore/Metadata/Internal/MetadataDebugStringOptions.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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; - -namespace Microsoft.EntityFrameworkCore.Metadata.Internal -{ - /// - /// 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. - /// - [Flags] - public enum MetadataDebugStringOptions - { - /// - /// 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. - /// - IncludeAnnotations = 1, - - /// - /// 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. - /// - IncludePropertyIndexes = 2, - - /// - /// 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. - /// - SingleLine = 4, - - /// - /// 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. - /// - ShortDefault = 0, - - /// - /// 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. - /// - LongDefault = IncludeAnnotations, - - /// - /// 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. - /// - SingleLineDefault = SingleLine - } -} diff --git a/src/EFCore/Metadata/Internal/ModelExtensions.cs b/src/EFCore/Metadata/Internal/ModelExtensions.cs index 4c3aa9f5df9..4e7fd2d7f5d 100644 --- a/src/EFCore/Metadata/Internal/ModelExtensions.cs +++ b/src/EFCore/Metadata/Internal/ModelExtensions.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Text; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Infrastructure.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal diff --git a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs index 4092df78314..2942db728f5 100644 --- a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs +++ b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs @@ -47,15 +47,6 @@ public static int GetStoreGeneratedIndex([NotNull] this IPropertyBase propertyBa public static int GetRelationshipIndex([NotNull] this IPropertyBase propertyBase) => propertyBase.GetPropertyIndexes().RelationshipIndex; - /// - /// 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. - /// - public static int GetIndex([NotNull] this IPropertyBase property) - => property.GetPropertyIndexes().Index; - /// /// 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 diff --git a/src/EFCore/Metadata/MetadataDebugStringOptions.cs b/src/EFCore/Metadata/MetadataDebugStringOptions.cs new file mode 100644 index 00000000000..8b20cce424e --- /dev/null +++ b/src/EFCore/Metadata/MetadataDebugStringOptions.cs @@ -0,0 +1,44 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Options to print debug string differently for metadata objects. + /// + [Flags] + public enum MetadataDebugStringOptions + { + /// + /// Include annotations in debug string. + /// + IncludeAnnotations = 1, + + /// + /// Include property indexes in debug string. + /// + IncludePropertyIndexes = 2, + + /// + /// Print debug string on single line only. + /// + SingleLine = 4, + + /// + /// Default settings for short debug string. + /// + ShortDefault = 0, + + /// + /// Default settings for long debug string. + /// + LongDefault = IncludeAnnotations, + + /// + /// Default settings for single line debug string. + /// + SingleLineDefault = SingleLine + } +} diff --git a/src/EFCore/Query/CompiledQueryCacheKeyGenerator.cs b/src/EFCore/Query/CompiledQueryCacheKeyGenerator.cs index 1988923c075..6bfb2816708 100644 --- a/src/EFCore/Query/CompiledQueryCacheKeyGenerator.cs +++ b/src/EFCore/Query/CompiledQueryCacheKeyGenerator.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; diff --git a/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs b/src/EFCore/Query/ExpressionEqualityComparer.cs similarity index 93% rename from src/EFCore/Query/Internal/ExpressionEqualityComparer.cs rename to src/EFCore/Query/ExpressionEqualityComparer.cs index 059b5fcf582..6e8d90667fc 100644 --- a/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs +++ b/src/EFCore/Query/ExpressionEqualityComparer.cs @@ -7,19 +7,12 @@ using System.Linq.Expressions; using System.Reflection; using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Internal; // ReSharper disable SwitchStatementMissingSomeCases // ReSharper disable ForCanBeConvertedToForeach // ReSharper disable LoopCanBeConvertedToQuery -namespace Microsoft.EntityFrameworkCore.Query.Internal +namespace Microsoft.EntityFrameworkCore.Query { - /// - /// 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. - /// public class ExpressionEqualityComparer : IEqualityComparer { /// @@ -30,19 +23,15 @@ private ExpressionEqualityComparer() } /// - /// 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. + /// Gets an instance of . /// public static ExpressionEqualityComparer Instance { get; } = new ExpressionEqualityComparer(); /// - /// 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. + /// Returns the hash code for given expression. /// + /// The obj to compute hash code for. + /// The hash code value for . public virtual int GetHashCode(Expression obj) { if (obj == null) @@ -285,11 +274,11 @@ void AddMemberBindingsToHash(IReadOnlyList memberBindings) } /// - /// 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. + /// Returns a value indicating whether the given expressions are equal. /// + /// The left expression. + /// The right expression. + /// true if the expressions are equal, false otherwise. public virtual bool Equals(Expression x, Expression y) => new ExpressionComparer().Compare(x, y); private struct ExpressionComparer diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index c129c668529..f349efd8c66 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -194,7 +194,7 @@ protected Expression ExpandNavigation( { // This is FirstOrDefault ending so we need to push down properties. var temporaryParameter = Expression.Parameter(root.Type); - var temporaryKey = temporaryParameter.CreateKeyAccessExpression( + var temporaryKey = temporaryParameter.CreateKeyValueReadExpression( navigation.IsOnDependent ? navigation.ForeignKey.Properties : navigation.ForeignKey.PrincipalKey.Properties, @@ -208,14 +208,14 @@ protected Expression ExpandNavigation( } else { - outerKey = root.CreateKeyAccessExpression( + outerKey = root.CreateKeyValueReadExpression( navigation.IsOnDependent ? navigation.ForeignKey.Properties : navigation.ForeignKey.PrincipalKey.Properties, makeNullable: true); } - var innerKey = innerParameter.CreateKeyAccessExpression( + var innerKey = innerParameter.CreateKeyValueReadExpression( navigation.IsOnDependent ? navigation.ForeignKey.PrincipalKey.Properties : navigation.ForeignKey.Properties, diff --git a/src/Shared/EnumerableExtensions.cs b/src/Shared/EnumerableExtensions.cs new file mode 100644 index 00000000000..4f28f638717 --- /dev/null +++ b/src/Shared/EnumerableExtensions.cs @@ -0,0 +1,141 @@ +// 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.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using JetBrains.Annotations; + +// ReSharper disable once CheckNamespace +namespace Microsoft.EntityFrameworkCore.Internal +{ + [DebuggerStepThrough] + internal static class EnumerableExtensions + { + public static IOrderedEnumerable OrderByOrdinal( + [NotNull] this IEnumerable source, + [NotNull] Func keySelector) + => source.OrderBy(keySelector, StringComparer.Ordinal); + + public static IEnumerable Distinct( + [NotNull] this IEnumerable source, + [NotNull] Func comparer) + where T : class + => source.Distinct(new DynamicEqualityComparer(comparer)); + + private sealed class DynamicEqualityComparer : IEqualityComparer + where T : class + { + private readonly Func _func; + + public DynamicEqualityComparer(Func func) + { + _func = func; + } + + public bool Equals(T x, T y) => _func(x, y); + + public int GetHashCode(T obj) => 0; + } + + public static string Join( + [NotNull] this IEnumerable source, + [NotNull] string separator = ", ") + => string.Join(separator, source); + + public static bool StructuralSequenceEqual( + [NotNull] this IEnumerable first, + [NotNull] IEnumerable second) + { + if (ReferenceEquals(first, second)) + { + return true; + } + + using var firstEnumerator = first.GetEnumerator(); + using var secondEnumerator = second.GetEnumerator(); + while (firstEnumerator.MoveNext()) + { + if (!secondEnumerator.MoveNext() + || !StructuralComparisons.StructuralEqualityComparer + .Equals(firstEnumerator.Current, secondEnumerator.Current)) + { + return false; + } + } + + return !secondEnumerator.MoveNext(); + } + + public static bool StartsWith( + [NotNull] this IEnumerable first, + [NotNull] IEnumerable second) + { + if (ReferenceEquals(first, second)) + { + return true; + } + + using (var firstEnumerator = first.GetEnumerator()) + { + using var secondEnumerator = second.GetEnumerator(); + while (secondEnumerator.MoveNext()) + { + if (!firstEnumerator.MoveNext() + || !Equals(firstEnumerator.Current, secondEnumerator.Current)) + { + return false; + } + } + } + + return true; + } + + public static int IndexOf([NotNull] this IEnumerable source, [NotNull] T item) + => IndexOf(source, item, EqualityComparer.Default); + + public static int IndexOf( + [NotNull] this IEnumerable source, [NotNull] T item, + [NotNull] IEqualityComparer comparer) + => source.Select( + (x, index) => + comparer.Equals(item, x) ? index : -1) + .FirstOr(x => x != -1, -1); + + public static T FirstOr([NotNull] this IEnumerable source, [NotNull] T alternate) + => source.DefaultIfEmpty(alternate).First(); + + public static T FirstOr([NotNull] this IEnumerable source, [NotNull] Func predicate, [NotNull] T alternate) + => source.Where(predicate).FirstOr(alternate); + + public static bool Any([NotNull] this IEnumerable source) + { + foreach (var _ in source) + { + return true; + } + + return false; + } + + public static async Task> ToListAsync( + this IAsyncEnumerable source, CancellationToken cancellationToken = default) + { + var list = new List(); + await foreach (var element in source.WithCancellation(cancellationToken)) + { + list.Add(element); + } + + return list; + } + + public static List ToList(this IEnumerable source) + => source.OfType().ToList(); + } +} diff --git a/test/EFCore.Specification.Tests/TestUtilities/EnumerableExtensions.cs b/test/EFCore.Specification.Tests/TestUtilities/EnumerableExtensions.cs deleted file mode 100644 index cd10448d4ef..00000000000 --- a/test/EFCore.Specification.Tests/TestUtilities/EnumerableExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// 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.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore.Internal -{ - public static class EnumerableExtensions - { - public static async Task> ToListAsync( - this IAsyncEnumerable source, CancellationToken cancellationToken = default) - { - var list = new List(); - await foreach (var element in source.WithCancellation(cancellationToken)) - { - list.Add(element); - } - - return list; - } - - public static List ToList(this IEnumerable source) - => source.OfType().ToList(); - } -} diff --git a/test/EFCore.Tests/Query/ExpressionEqualityComparerTest.cs b/test/EFCore.Tests/Query/ExpressionEqualityComparerTest.cs index ed90b20dea1..36cf1a412d8 100644 --- a/test/EFCore.Tests/Query/ExpressionEqualityComparerTest.cs +++ b/test/EFCore.Tests/Query/ExpressionEqualityComparerTest.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Query.Internal; using Xunit; // ReSharper disable AssignNullToNotNullAttribute