diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs index 5b2276fca91..eb02368a6f5 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs @@ -6,7 +6,7 @@ using System.ComponentModel; using Castle.DynamicProxy; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs index 8fe134569ea..dc2ffd19018 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs @@ -6,7 +6,7 @@ using System.ComponentModel; using Castle.DynamicProxy; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnMappingBaseComparer.cs b/src/EFCore.Relational/Metadata/Internal/ColumnMappingBaseComparer.cs index 9791b9048b5..91386ccceed 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnMappingBaseComparer.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnMappingBaseComparer.cs @@ -52,9 +52,7 @@ public int Compare(IColumnMappingBase x, IColumnMappingBase y) return result; } -#pragma warning disable EF1001 // Internal EF Core API usage. result = EntityTypePathComparer.Instance.Compare(x.Property.DeclaringEntityType, y.Property.DeclaringEntityType); -#pragma warning restore EF1001 // Internal EF Core API usage. if (result != 0) { return result; @@ -90,9 +88,7 @@ public int GetHashCode(IColumnMappingBase obj) var hashCode = new HashCode(); hashCode.Add(obj.Property.Name); hashCode.Add(obj.Column.Name); -#pragma warning disable EF1001 // Internal EF Core API usage. hashCode.Add(obj.Property.DeclaringEntityType, EntityTypePathComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. hashCode.Add(obj.Column.Table.Name); hashCode.Add(obj.Column.Table.Schema); diff --git a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs index f8a4fb8982c..c3a30b24fdf 100644 --- a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs +++ b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs @@ -47,9 +47,7 @@ public ForeignKeyConstraint( /// 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. /// -#pragma warning disable EF1001 // Internal EF Core API usage. public virtual SortedSet MappedForeignKeys { get; } = new SortedSet(ForeignKeyComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index 6e883ace13e..ab72a104257 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -486,26 +486,20 @@ private static void PopulateInternalForeignKeys(TableBase table) { if (internalForeignKeys == null) { -#pragma warning disable EF1001 // Internal EF Core API usage. internalForeignKeys = new SortedSet(ForeignKeyComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. } internalForeignKeys.Add(foreignKey); if (referencingInternalForeignKeyMap == null) { referencingInternalForeignKeyMap = -#pragma warning disable EF1001 // Internal EF Core API usage. new SortedDictionary>(EntityTypePathComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. } var principalEntityType = foreignKey.PrincipalEntityType; if (!referencingInternalForeignKeyMap.TryGetValue(principalEntityType, out var internalReferencingForeignKeys)) { -#pragma warning disable EF1001 // Internal EF Core API usage. internalReferencingForeignKeys = new SortedSet(ForeignKeyComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. referencingInternalForeignKeyMap[principalEntityType] = internalReferencingForeignKeys; } ((SortedSet)internalReferencingForeignKeys).Add(foreignKey); @@ -517,9 +511,7 @@ private static void PopulateInternalForeignKeys(TableBase table) if (internalForeignKeyMap == null) { internalForeignKeyMap = -#pragma warning disable EF1001 // Internal EF Core API usage. new SortedDictionary>(EntityTypePathComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. table.InternalForeignKeys = internalForeignKeyMap; } diff --git a/src/EFCore.Relational/Metadata/Internal/TableIndex.cs b/src/EFCore.Relational/Metadata/Internal/TableIndex.cs index 6160760dfa1..33e55a84055 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableIndex.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableIndex.cs @@ -44,9 +44,7 @@ public TableIndex( /// 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. /// -#pragma warning disable EF1001 // Internal EF Core API usage. public virtual SortedSet MappedIndexes { get; } = new SortedSet(IndexComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs b/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs index 22308392b66..749c3230a90 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs @@ -36,9 +36,7 @@ private TableMappingBaseComparer() /// public int Compare(ITableMappingBase x, ITableMappingBase y) { -#pragma warning disable EF1001 // Internal EF Core API usage. var result = EntityTypePathComparer.Instance.Compare(x.EntityType, y.EntityType); -#pragma warning restore EF1001 // Internal EF Core API usage. if (result != 0) { return result; @@ -97,9 +95,7 @@ public bool Equals(ITableMappingBase x, ITableMappingBase y) public int GetHashCode(ITableMappingBase obj) { var hashCode = new HashCode(); -#pragma warning disable EF1001 // Internal EF Core API usage. hashCode.Add(obj.EntityType, EntityTypePathComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. hashCode.Add(obj.Table.Name); hashCode.Add(obj.Table.Schema); foreach (var columnMapping in obj.ColumnMappings) diff --git a/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs b/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs index b79dddd04c9..92bbddb1d8e 100644 --- a/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs +++ b/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs @@ -45,9 +45,7 @@ public UniqueConstraint( /// 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. /// -#pragma warning disable EF1001 // Internal EF Core API usage. public virtual SortedSet MappedKeys { get; } = new SortedSet(KeyComparer.Instance); -#pragma warning restore EF1001 // Internal EF Core API usage. /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Query/FromSqlParameterApplyingExpressionVisitor.cs b/src/EFCore.Relational/Query/FromSqlParameterApplyingExpressionVisitor.cs index d422696c14b..10b940b4f38 100644 --- a/src/EFCore.Relational/Query/FromSqlParameterApplyingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/FromSqlParameterApplyingExpressionVisitor.cs @@ -6,7 +6,7 @@ using System.Data.Common; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.Internal; diff --git a/src/EFCore.Relational/Query/Internal/TableAliasUniquifyingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/TableAliasUniquifyingExpressionVisitor.cs index fdea49de7e9..a5da057785b 100644 --- a/src/EFCore.Relational/Query/Internal/TableAliasUniquifyingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Internal/TableAliasUniquifyingExpressionVisitor.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/EFCore/ChangeTracking/EntryCurrentValueComparer`.cs b/src/EFCore/ChangeTracking/EntryCurrentValueComparer`.cs new file mode 100644 index 00000000000..d2ced30dfd8 --- /dev/null +++ b/src/EFCore/ChangeTracking/EntryCurrentValueComparer`.cs @@ -0,0 +1,66 @@ +// 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 JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Update; + +namespace Microsoft.EntityFrameworkCore.ChangeTracking +{ + /// + /// + /// An implementation of and to compare current values + /// contained in internal tracking entities. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The type of the property. + public sealed class EntryCurrentValueComparer : IComparer, IEqualityComparer + { + private readonly IPropertyBase _property; + private readonly IComparer _underlyingComparer; + + /// + /// Creates a new instance using a the default comparer for the property type. + /// + /// The property to use for comparisons. + public EntryCurrentValueComparer([NotNull] IPropertyBase property) + { + _property = property; + _underlyingComparer = Comparer.Default; + } + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. + public int Compare(IUpdateEntry x, IUpdateEntry y) + => _underlyingComparer.Compare( + x.GetCurrentValue(_property), + y.GetCurrentValue(_property)); + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public bool Equals(IUpdateEntry x, IUpdateEntry y) + => Compare(x, y) == 0; + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(IUpdateEntry obj) + => obj.GetCurrentValue(_property).GetHashCode(); + } +} diff --git a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs index c07b3621013..cff56b96601 100644 --- a/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs +++ b/src/EFCore/ChangeTracking/Internal/ChangeDetector.cs @@ -6,7 +6,7 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.Extensions.DependencyInjection; diff --git a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs index 07ff9934730..e2e6ffefa38 100644 --- a/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/CurrentValueComparerFactory.cs @@ -34,18 +34,18 @@ public virtual IComparer Create([NotNull] IPropertyBase propertyBa if (IsGenericComparable(modelType, nonNullableModelType)) { return (IComparer)Activator.CreateInstance( - typeof(CurrentValueComparer<>).MakeGenericType(modelType), + typeof(EntryCurrentValueComparer<>).MakeGenericType(modelType), propertyBase); } if (typeof(IStructuralComparable).IsAssignableFrom(nonNullableModelType)) { - return new StructuralCurrentValueComparer(propertyBase); + return new StructuralEntryCurrentValueComparer(propertyBase); } if (typeof(IComparable).IsAssignableFrom(nonNullableModelType)) { - return new CurrentValueComparer(propertyBase); + return new EntryCurrentValueComparer(propertyBase); } if (propertyBase is IProperty property) @@ -71,12 +71,12 @@ public virtual IComparer Create([NotNull] IPropertyBase propertyBa if (typeof(IStructuralComparable).IsAssignableFrom(nonNullableProviderType)) { - return new StructuralCurrentProviderValueComparer(propertyBase, converter); + return new StructuralEntryCurrentProviderValueComparer(propertyBase, converter); } if (typeof(IComparable).IsAssignableFrom(nonNullableProviderType)) { - return new CurrentProviderValueComparer(propertyBase, converter); + return new EntryCurrentProviderValueComparer(propertyBase, converter); } throw new InvalidOperationException( diff --git a/src/EFCore/ChangeTracking/Internal/CurrentValueComparer`.cs b/src/EFCore/ChangeTracking/Internal/CurrentValueComparer`.cs deleted file mode 100644 index 3ed9a16f554..00000000000 --- a/src/EFCore/ChangeTracking/Internal/CurrentValueComparer`.cs +++ /dev/null @@ -1,45 +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.Generic; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Update; - -namespace Microsoft.EntityFrameworkCore.ChangeTracking.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 class CurrentValueComparer : IComparer - { - private readonly IPropertyBase _property; - private readonly IComparer _underlyingComparer; - - /// - /// 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 CurrentValueComparer([NotNull] IPropertyBase property) - { - _property = property; - _underlyingComparer = Comparer.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 virtual int Compare(IUpdateEntry x, IUpdateEntry y) - => _underlyingComparer.Compare( - x.GetCurrentValue(_property), - y.GetCurrentValue(_property)); - } -} diff --git a/src/EFCore/ChangeTracking/Internal/CurrentProviderValueComparer.cs b/src/EFCore/ChangeTracking/Internal/EntryCurrentProviderValueComparer.cs similarity index 88% rename from src/EFCore/ChangeTracking/Internal/CurrentProviderValueComparer.cs rename to src/EFCore/ChangeTracking/Internal/EntryCurrentProviderValueComparer.cs index eb8b19e63a5..188e086ac25 100644 --- a/src/EFCore/ChangeTracking/Internal/CurrentProviderValueComparer.cs +++ b/src/EFCore/ChangeTracking/Internal/EntryCurrentProviderValueComparer.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal /// 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 CurrentProviderValueComparer : CurrentValueComparer + public class EntryCurrentProviderValueComparer : EntryCurrentValueComparer { private readonly ValueConverter _converter; @@ -24,7 +24,7 @@ public class CurrentProviderValueComparer : CurrentValueComparer /// 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 CurrentProviderValueComparer( + public EntryCurrentProviderValueComparer( [NotNull] IPropertyBase property, [NotNull] ValueConverter converter) : base(property) @@ -38,7 +38,7 @@ public CurrentProviderValueComparer( /// 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 override object GetCurrentValue(IUpdateEntry entry) - => _converter.ConvertToProvider(base.GetCurrentValue(entry)); + protected override object GetPropertyValue(IUpdateEntry entry) + => _converter.ConvertToProvider(base.GetPropertyValue(entry)); } } diff --git a/src/EFCore/ChangeTracking/Internal/CurrentValueComparer.cs b/src/EFCore/ChangeTracking/Internal/EntryCurrentValueComparer.cs similarity index 68% rename from src/EFCore/ChangeTracking/Internal/CurrentValueComparer.cs rename to src/EFCore/ChangeTracking/Internal/EntryCurrentValueComparer.cs index 0750e0c16e1..a5d2d5b5940 100644 --- a/src/EFCore/ChangeTracking/Internal/CurrentValueComparer.cs +++ b/src/EFCore/ChangeTracking/Internal/EntryCurrentValueComparer.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal /// 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 CurrentValueComparer : IComparer + public class EntryCurrentValueComparer : IComparer, IEqualityComparer { private readonly IPropertyBase _property; private readonly IComparer _underlyingComparer; @@ -26,7 +26,7 @@ public class CurrentValueComparer : IComparer /// 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 CurrentValueComparer([NotNull] IPropertyBase property) + public EntryCurrentValueComparer([NotNull] IPropertyBase property) : this(property, Comparer.Default) { } @@ -37,7 +37,7 @@ public CurrentValueComparer([NotNull] IPropertyBase property) /// 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. /// - protected CurrentValueComparer([NotNull] IPropertyBase property, [NotNull] IComparer underlyingComparer) + public EntryCurrentValueComparer([NotNull] IPropertyBase property, [NotNull] IComparer underlyingComparer) { _property = property; _underlyingComparer = underlyingComparer; @@ -49,7 +49,7 @@ protected CurrentValueComparer([NotNull] IPropertyBase property, [NotNull] IComp /// 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 virtual object GetCurrentValue([NotNull] IUpdateEntry entry) + protected virtual object GetPropertyValue([NotNull] IUpdateEntry entry) => entry.GetCurrentValue(_property); /// @@ -59,7 +59,7 @@ public virtual object GetCurrentValue([NotNull] IUpdateEntry entry) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual int Compare(IUpdateEntry x, IUpdateEntry y) - => Compare(x.GetCurrentValue(_property), y.GetCurrentValue(_property)); + => ComparePropertyValues(x.GetCurrentValue(_property), y.GetCurrentValue(_property)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -67,7 +67,25 @@ public virtual int Compare(IUpdateEntry x, IUpdateEntry y) /// 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. /// - protected virtual int Compare([CanBeNull] object x, [CanBeNull] object y) + protected virtual int ComparePropertyValues([CanBeNull] object x, [CanBeNull] object y) => _underlyingComparer.Compare(x, y); + + /// + /// 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 virtual bool Equals(IUpdateEntry x, IUpdateEntry y) + => Compare(x, y) == 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 virtual int GetHashCode(IUpdateEntry obj) + => GetPropertyValue(obj)?.GetHashCode() ?? 0; } } diff --git a/src/EFCore/ChangeTracking/Internal/IdentityMap.cs b/src/EFCore/ChangeTracking/Internal/IdentityMap.cs index 0b693377289..3394f85894f 100644 --- a/src/EFCore/ChangeTracking/Internal/IdentityMap.cs +++ b/src/EFCore/ChangeTracking/Internal/IdentityMap.cs @@ -6,7 +6,7 @@ using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Storage; diff --git a/src/EFCore/ChangeTracking/Internal/RelationshipsSnapshot.cs b/src/EFCore/ChangeTracking/Internal/RelationshipsSnapshot.cs index 582c711284b..8a57bfd3eb5 100644 --- a/src/EFCore/ChangeTracking/Internal/RelationshipsSnapshot.cs +++ b/src/EFCore/ChangeTracking/Internal/RelationshipsSnapshot.cs @@ -2,7 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; diff --git a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs index 4abedf7fdbd..a0ccdc785d5 100644 --- a/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/SnapshotFactoryFactory.cs @@ -7,7 +7,7 @@ using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query; diff --git a/src/EFCore/ChangeTracking/Internal/StructuralCurrentProviderValueComparer.cs b/src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentProviderValueComparer.cs similarity index 87% rename from src/EFCore/ChangeTracking/Internal/StructuralCurrentProviderValueComparer.cs rename to src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentProviderValueComparer.cs index 21ffc6a3aca..18bd30cc5f0 100644 --- a/src/EFCore/ChangeTracking/Internal/StructuralCurrentProviderValueComparer.cs +++ b/src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentProviderValueComparer.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal /// 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 StructuralCurrentProviderValueComparer : StructuralCurrentValueComparer + public class StructuralEntryCurrentProviderValueComparer : StructuralEntryCurrentValueComparer { private readonly ValueConverter _converter; @@ -24,7 +24,7 @@ public class StructuralCurrentProviderValueComparer : StructuralCurrentValueComp /// 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 StructuralCurrentProviderValueComparer( + public StructuralEntryCurrentProviderValueComparer( [NotNull] IPropertyBase property, [NotNull] ValueConverter converter) : base(property) @@ -38,7 +38,7 @@ public StructuralCurrentProviderValueComparer( /// 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 override object GetCurrentValue(IUpdateEntry entry) - => _converter.ConvertToProvider(base.GetCurrentValue(entry)); + protected override object GetPropertyValue(IUpdateEntry entry) + => _converter.ConvertToProvider(base.GetPropertyValue(entry)); } } diff --git a/src/EFCore/ChangeTracking/Internal/StructuralCurrentValueComparer.cs b/src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentValueComparer.cs similarity index 87% rename from src/EFCore/ChangeTracking/Internal/StructuralCurrentValueComparer.cs rename to src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentValueComparer.cs index e4167490fc7..dacce99d798 100644 --- a/src/EFCore/ChangeTracking/Internal/StructuralCurrentValueComparer.cs +++ b/src/EFCore/ChangeTracking/Internal/StructuralEntryCurrentValueComparer.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal /// 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 StructuralCurrentValueComparer : CurrentValueComparer + public class StructuralEntryCurrentValueComparer : EntryCurrentValueComparer { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -23,7 +23,7 @@ public class StructuralCurrentValueComparer : CurrentValueComparer /// 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 StructuralCurrentValueComparer([NotNull] IPropertyBase property) + public StructuralEntryCurrentValueComparer([NotNull] IPropertyBase property) : base(property, StructuralComparisons.StructuralComparer) { } @@ -36,14 +36,14 @@ public StructuralCurrentValueComparer([NotNull] IPropertyBase property) /// public override int Compare(IUpdateEntry x, IUpdateEntry y) { - var xValue = GetCurrentValue(x); - var yValue = GetCurrentValue(y); + var xValue = GetPropertyValue(x); + var yValue = GetPropertyValue(y); return xValue is Array xArray && yValue is Array yArray && xArray.Length != yArray.Length ? xArray.Length - yArray.Length - : base.Compare(xValue, yValue); + : base.ComparePropertyValues(xValue, yValue); } } } diff --git a/src/EFCore/Infrastructure/ReferenceEqualityComparer.cs b/src/EFCore/Infrastructure/ReferenceEqualityComparer.cs new file mode 100644 index 00000000000..6f4f2ec2f47 --- /dev/null +++ b/src/EFCore/Infrastructure/ReferenceEqualityComparer.cs @@ -0,0 +1,84 @@ +// 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.Runtime.CompilerServices; +using JetBrains.Annotations; + +namespace Microsoft.EntityFrameworkCore.Infrastructure +{ + /// + /// + /// An implementation of and to compare + /// objects by reference. + /// + /// + /// This comparer is used internally by EF when creating collection navigations. + /// It is advised that manually created collection navigations do likewise, or otherwise ensure that only + /// every entity instance compares different to another instance. + /// + /// + public sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer + { + private ReferenceEqualityComparer() + { + } + + /// + /// The singleton instance of the comparer to use. + /// + public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer(); + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public new bool Equals([CanBeNull] object x, [CanBeNull] object y) + => ReferenceEquals(x, y); + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode([NotNull] object obj) + => RuntimeHelpers.GetHashCode(obj); + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + bool IEqualityComparer.Equals(object x, object y) + => ReferenceEquals(x, y); + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + int IEqualityComparer.GetHashCode(object obj) + => RuntimeHelpers.GetHashCode(obj); + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + bool IEqualityComparer.Equals(object x, object y) + => ReferenceEquals(x, y); + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + int IEqualityComparer.GetHashCode(object obj) + => RuntimeHelpers.GetHashCode(obj); + } +} diff --git a/src/EFCore/Metadata/EntityTypePathComparer.cs b/src/EFCore/Metadata/EntityTypePathComparer.cs new file mode 100644 index 00000000000..f74b6dd6b48 --- /dev/null +++ b/src/EFCore/Metadata/EntityTypePathComparer.cs @@ -0,0 +1,123 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// An implementation of and to compare + /// instances by the paths needed to arrive at the entity type. This is typically + /// only useful for types in an aggregate. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + public sealed class EntityTypePathComparer : IComparer, IEqualityComparer + { + private EntityTypePathComparer() + { + } + + /// + /// The singleton instance of the comparer to use. + /// + public static readonly EntityTypePathComparer Instance = new EntityTypePathComparer(); + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. + public int Compare(IEntityType x, IEntityType y) + { + if (ReferenceEquals(x, y)) + { + return 0; + } + + if (x == null) + { + return -1; + } + + if (y == null) + { + return 1; + } + + var result = StringComparer.Ordinal.Compare(x.Name, y.Name); + if (result != 0) + { + return result; + } + + while (true) + { + var xDefiningNavigationName = x.DefiningNavigationName; + var yDefiningNavigationName = y.DefiningNavigationName; + + if (xDefiningNavigationName == null + && yDefiningNavigationName == null) + { + return StringComparer.Ordinal.Compare(x.Name, y.Name); + } + + if (xDefiningNavigationName == null) + { + return -1; + } + + if (yDefiningNavigationName == null) + { + return 1; + } + + result = StringComparer.Ordinal.Compare(xDefiningNavigationName, yDefiningNavigationName); + if (result != 0) + { + return result; + } + + x = x.DefiningEntityType; + y = y.DefiningEntityType; + } + } + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public bool Equals(IEntityType x, IEntityType y) + => Compare(x, y) == 0; + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(IEntityType obj) + { + var hash = new HashCode(); + while (true) + { + hash.Add(obj.Name, StringComparer.Ordinal); + var definingNavigationName = obj.DefiningNavigationName; + if (definingNavigationName == null) + { + return hash.ToHashCode(); + } + + hash.Add(definingNavigationName, StringComparer.Ordinal); + obj = obj.DefiningEntityType; + } + } + } +} diff --git a/src/EFCore/Metadata/ForeignKeyComparer.cs b/src/EFCore/Metadata/ForeignKeyComparer.cs new file mode 100644 index 00000000000..6107fa75949 --- /dev/null +++ b/src/EFCore/Metadata/ForeignKeyComparer.cs @@ -0,0 +1,79 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// An implementation of and to compare + /// instances. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + public sealed class ForeignKeyComparer : IEqualityComparer, IComparer + { + private ForeignKeyComparer() + { + } + + /// + /// The singleton instance of the comparer to use. + /// + public static readonly ForeignKeyComparer Instance = new ForeignKeyComparer(); + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. + public int Compare(IForeignKey x, IForeignKey y) + { + var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); + if (result != 0) + { + return result; + } + + result = PropertyListComparer.Instance.Compare(x.PrincipalKey.Properties, y.PrincipalKey.Properties); + if (result != 0) + { + return result; + } + + result = EntityTypePathComparer.Instance.Compare(x.PrincipalEntityType, y.PrincipalEntityType); + return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); + } + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public bool Equals(IForeignKey x, IForeignKey y) + => Compare(x, y) == 0; + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(IForeignKey obj) + { + var hashCode = new HashCode(); + hashCode.Add(obj.PrincipalKey.Properties, PropertyListComparer.Instance); + hashCode.Add(obj.Properties, PropertyListComparer.Instance); + hashCode.Add(obj.PrincipalEntityType, EntityTypePathComparer.Instance); + hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); + return hashCode.ToHashCode(); + } + } +} diff --git a/src/EFCore/Metadata/IndexComparer.cs b/src/EFCore/Metadata/IndexComparer.cs new file mode 100644 index 00000000000..e85abd1814e --- /dev/null +++ b/src/EFCore/Metadata/IndexComparer.cs @@ -0,0 +1,65 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// An implementation of and to compare + /// instances. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + public sealed class IndexComparer : IEqualityComparer, IComparer + { + private IndexComparer() + { + } + + /// + /// The singleton instance of the comparer to use. + /// + public static readonly IndexComparer Instance = new IndexComparer(); + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. + public int Compare(IIndex x, IIndex y) + { + var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); + return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); + } + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public bool Equals(IIndex x, IIndex y) + => Compare(x, y) == 0; + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(IIndex obj) + { + var hashCode = new HashCode(); + hashCode.Add(obj.Properties, PropertyListComparer.Instance); + hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); + return hashCode.ToHashCode(); + } + } +} diff --git a/src/EFCore/Metadata/Internal/EntityTypePathComparer.cs b/src/EFCore/Metadata/Internal/EntityTypePathComparer.cs deleted file mode 100644 index 0c7e20aa2a6..00000000000 --- a/src/EFCore/Metadata/Internal/EntityTypePathComparer.cs +++ /dev/null @@ -1,120 +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.Generic; - -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. - /// - // Sealed for perf - public sealed class EntityTypePathComparer : IComparer, IEqualityComparer - { - private EntityTypePathComparer() - { - } - - /// - /// 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 readonly EntityTypePathComparer Instance = new EntityTypePathComparer(); - - /// - /// 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 int Compare(IEntityType x, IEntityType y) - { - if (ReferenceEquals(x, y)) - { - return 0; - } - - if (x == null) - { - return -1; - } - - if (y == null) - { - return 1; - } - - var result = StringComparer.Ordinal.Compare(x.Name, y.Name); - if (result != 0) - { - return result; - } - - while (true) - { - var xDefiningNavigationName = x.DefiningNavigationName; - var yDefiningNavigationName = y.DefiningNavigationName; - - if (xDefiningNavigationName == null - && yDefiningNavigationName == null) - { - return StringComparer.Ordinal.Compare(x.Name, y.Name); - } - - if (xDefiningNavigationName == null) - { - return -1; - } - - if (yDefiningNavigationName == null) - { - return 1; - } - - result = StringComparer.Ordinal.Compare(xDefiningNavigationName, yDefiningNavigationName); - if (result != 0) - { - return result; - } - - x = x.DefiningEntityType; - y = y.DefiningEntityType; - } - } - - /// Determines whether the specified objects are equal. - /// The first object of type T to compare. - /// The second object of type T to compare. - /// true if the specified objects are equal; otherwise, false. - public bool Equals(IEntityType x, IEntityType y) => Compare(x, y) == 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 int GetHashCode(IEntityType entityType) - { - var hash = new HashCode(); - while (true) - { - hash.Add(entityType.Name, StringComparer.Ordinal); - var definingNavigationName = entityType.DefiningNavigationName; - if (definingNavigationName == null) - { - return hash.ToHashCode(); - } - - hash.Add(definingNavigationName, StringComparer.Ordinal); - entityType = entityType.DefiningEntityType; - } - } - } -} diff --git a/src/EFCore/Metadata/Internal/ForeignKeyComparer.cs b/src/EFCore/Metadata/Internal/ForeignKeyComparer.cs deleted file mode 100644 index 267e5294611..00000000000 --- a/src/EFCore/Metadata/Internal/ForeignKeyComparer.cs +++ /dev/null @@ -1,79 +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.Generic; - -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. - /// - // Sealed for perf - public sealed class ForeignKeyComparer : IEqualityComparer, IComparer - { - private ForeignKeyComparer() - { - } - - /// - /// 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 readonly ForeignKeyComparer Instance = new ForeignKeyComparer(); - - /// - /// 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 int Compare(IForeignKey x, IForeignKey y) - { - var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); - if (result != 0) - { - return result; - } - - result = PropertyListComparer.Instance.Compare(x.PrincipalKey.Properties, y.PrincipalKey.Properties); - if (result != 0) - { - return result; - } - - result = EntityTypePathComparer.Instance.Compare(x.PrincipalEntityType, y.PrincipalEntityType); - return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); - } - - /// - /// 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 bool Equals(IForeignKey x, IForeignKey y) - => Compare(x, y) == 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 int GetHashCode(IForeignKey obj) - { - var hashCode = new HashCode(); - hashCode.Add(obj.PrincipalKey.Properties, PropertyListComparer.Instance); - hashCode.Add(obj.Properties, PropertyListComparer.Instance); - hashCode.Add(obj.PrincipalEntityType, EntityTypePathComparer.Instance); - hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); - return hashCode.ToHashCode(); - } - } -} diff --git a/src/EFCore/Metadata/Internal/IndexComparer.cs b/src/EFCore/Metadata/Internal/IndexComparer.cs deleted file mode 100644 index d12ac985316..00000000000 --- a/src/EFCore/Metadata/Internal/IndexComparer.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; -using System.Collections.Generic; - -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. - /// - // Sealed for perf - public sealed class IndexComparer : IEqualityComparer, IComparer - { - private IndexComparer() - { - } - - /// - /// 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 readonly IndexComparer Instance = new IndexComparer(); - - /// - /// 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 int Compare(IIndex x, IIndex y) - { - var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); - return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); - } - - /// - /// 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 bool Equals(IIndex x, IIndex y) - => Compare(x, y) == 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 int GetHashCode(IIndex obj) - { - var hashCode = new HashCode(); - hashCode.Add(obj.Properties, PropertyListComparer.Instance); - hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); - return hashCode.ToHashCode(); - } - } -} diff --git a/src/EFCore/Metadata/Internal/KeyComparer.cs b/src/EFCore/Metadata/Internal/KeyComparer.cs deleted file mode 100644 index 647c72def3f..00000000000 --- a/src/EFCore/Metadata/Internal/KeyComparer.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; -using System.Collections.Generic; - -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. - /// - // Sealed for perf - public sealed class KeyComparer : IEqualityComparer, IComparer - { - private KeyComparer() - { - } - - /// - /// 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 readonly KeyComparer Instance = new KeyComparer(); - - /// - /// 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 int Compare(IKey x, IKey y) - { - var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); - return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); - } - - /// - /// 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 bool Equals(IKey x, IKey y) - => Compare(x, y) == 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 int GetHashCode(IKey obj) - { - var hashCode = new HashCode(); - hashCode.Add(obj.Properties, PropertyListComparer.Instance); - hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); - return hashCode.ToHashCode(); - } - } -} diff --git a/src/EFCore/Metadata/KeyComparer.cs b/src/EFCore/Metadata/KeyComparer.cs new file mode 100644 index 00000000000..bc6bbc133e5 --- /dev/null +++ b/src/EFCore/Metadata/KeyComparer.cs @@ -0,0 +1,65 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// + /// An implementation of and to compare + /// instances. + /// + /// + /// This type is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + public sealed class KeyComparer : IEqualityComparer, IComparer + { + private KeyComparer() + { + } + + /// + /// The singleton instance of the comparer to use. + /// + public static readonly KeyComparer Instance = new KeyComparer(); + + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. + public int Compare(IKey x, IKey y) + { + var result = PropertyListComparer.Instance.Compare(x.Properties, y.Properties); + return result != 0 ? result : EntityTypePathComparer.Instance.Compare(x.DeclaringEntityType, y.DeclaringEntityType); + } + + /// + /// Determines whether the specified objects are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// True if the specified objects are equal; otherwise, false. + public bool Equals(IKey x, IKey y) + => Compare(x, y) == 0; + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + public int GetHashCode(IKey obj) + { + var hashCode = new HashCode(); + hashCode.Add(obj.Properties, PropertyListComparer.Instance); + hashCode.Add(obj.DeclaringEntityType, EntityTypePathComparer.Instance); + return hashCode.ToHashCode(); + } + } +} diff --git a/src/Shared/ReferenceEqualityComparer.cs b/src/Shared/ReferenceEqualityComparer.cs deleted file mode 100644 index f40e15bbef3..00000000000 --- a/src/Shared/ReferenceEqualityComparer.cs +++ /dev/null @@ -1,27 +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.Runtime.CompilerServices; - -namespace Microsoft.EntityFrameworkCore.Internal -{ - // Sealed for perf - internal sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer - { - private ReferenceEqualityComparer() - { - } - - public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer(); - - bool IEqualityComparer.Equals(object x, object y) => ReferenceEquals(x, y); - - bool IEqualityComparer.Equals(object x, object y) => ReferenceEquals(x, y); - - int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); - - int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); - } -} diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs index 561ce519d43..1f8a8ba7da7 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs index 36e562d7af2..c5d1991eb6b 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs @@ -8,6 +8,7 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.TestModels.Northwind; using Microsoft.EntityFrameworkCore.TestUtilities; diff --git a/test/EFCore.Specification.Tests/TestUtilities/IndexComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs similarity index 89% rename from test/EFCore.Specification.Tests/TestUtilities/IndexComparer.cs rename to test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs index 6489453700d..b12d4686d3a 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/IndexComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs @@ -8,11 +8,11 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class IndexComparer : IEqualityComparer, IComparer + public class TestIndexComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; - public IndexComparer(bool compareAnnotations = true) + public TestIndexComparer(bool compareAnnotations = true) { _compareAnnotations = compareAnnotations; } diff --git a/test/EFCore.Specification.Tests/TestUtilities/KeyComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs similarity index 89% rename from test/EFCore.Specification.Tests/TestUtilities/KeyComparer.cs rename to test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs index bf965babb13..0af532b76c4 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/KeyComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs @@ -8,11 +8,11 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class KeyComparer : IEqualityComparer, IComparer + public class TestKeyComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; - public KeyComparer(bool compareAnnotations = true) + public TestKeyComparer(bool compareAnnotations = true) { _compareAnnotations = compareAnnotations; } diff --git a/test/EFCore.Tests/ChangeTracking/Internal/CurrentValueComparerTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/CurrentValueComparerTest.cs index 9c053a6491c..32c43c10cfe 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/CurrentValueComparerTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/CurrentValueComparerTest.cs @@ -15,30 +15,30 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal public class CurrentValueComparerTest { [ConditionalTheory] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.Id))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.Int))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.ULong))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.Id))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.Int))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.ULong))] [InlineData(typeof(CurrentProviderValueComparer), nameof(Godzilla.IntStruct))] - [InlineData(typeof(StructuralCurrentProviderValueComparer), nameof(Godzilla.BytesStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.ComparableIntStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.ComparableBytesStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.GenericComparableIntStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.GenericComparableBytesStruct))] - [InlineData(typeof(StructuralCurrentValueComparer), nameof(Godzilla.StructuralComparableBytesStruct))] + [InlineData(typeof(StructuralEntryCurrentProviderValueComparer), nameof(Godzilla.BytesStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.ComparableIntStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.ComparableBytesStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.GenericComparableIntStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.GenericComparableBytesStruct))] + [InlineData(typeof(StructuralEntryCurrentValueComparer), nameof(Godzilla.StructuralComparableBytesStruct))] [InlineData(typeof(NullableStructCurrentProviderValueComparer), nameof(Godzilla.NullableIntStruct))] - [InlineData(typeof(StructuralCurrentProviderValueComparer), nameof(Godzilla.NullableBytesStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableComparableIntStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableComparableBytesStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableGenericComparableIntStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableGenericComparableBytesStruct))] - [InlineData(typeof(StructuralCurrentValueComparer), nameof(Godzilla.NullableStructuralComparableBytesStruct))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableInt))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.NullableULong))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.String))] - [InlineData(typeof(StructuralCurrentValueComparer), nameof(Godzilla.Bytes))] + [InlineData(typeof(StructuralEntryCurrentProviderValueComparer), nameof(Godzilla.NullableBytesStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableComparableIntStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableComparableBytesStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableGenericComparableIntStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableGenericComparableBytesStruct))] + [InlineData(typeof(StructuralEntryCurrentValueComparer), nameof(Godzilla.NullableStructuralComparableBytesStruct))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableInt))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.NullableULong))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.String))] + [InlineData(typeof(StructuralEntryCurrentValueComparer), nameof(Godzilla.Bytes))] [InlineData(typeof(NullableClassCurrentProviderValueComparer), nameof(Godzilla.IntClass))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.ComparableIntClass))] - [InlineData(typeof(CurrentValueComparer), nameof(Godzilla.GenericComparableIntClass))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.ComparableIntClass))] + [InlineData(typeof(EntryCurrentValueComparer), nameof(Godzilla.GenericComparableIntClass))] public void Factory_creates_expected_comparer(Type expectedComparer, string property) { using var context = new GodzillaContext(); diff --git a/test/EFCore.Tests/ChangeTracking/ObservableHashSetTest.cs b/test/EFCore.Tests/ChangeTracking/ObservableHashSetTest.cs index e538704e084..46fa069fe02 100644 --- a/test/EFCore.Tests/ChangeTracking/ObservableHashSetTest.cs +++ b/test/EFCore.Tests/ChangeTracking/ObservableHashSetTest.cs @@ -7,6 +7,7 @@ using System.Collections.Specialized; using System.ComponentModel; using System.Linq; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Xunit; diff --git a/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs b/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs index c4d80579849..300636af592 100644 --- a/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs +++ b/test/EFCore.Tests/Extensions/QueryableExtensionsTest.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.TestModels.Northwind; diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs index b8b20b5ccf8..7fa1ced5ed8 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Diagnostics.Internal; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -61,13 +60,13 @@ protected void AssertEqual( protected void AssertEqual( IEnumerable expectedKeys, IEnumerable actualKeys, - KeyComparer keyComparer = null) + TestKeyComparer testKeyComparer = null) { - keyComparer ??= new KeyComparer(compareAnnotations: false); + testKeyComparer ??= new TestKeyComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedKeys, keyComparer), - new SortedSet(actualKeys, keyComparer), - keyComparer); + new SortedSet(expectedKeys, testKeyComparer), + new SortedSet(actualKeys, testKeyComparer), + testKeyComparer); } protected void AssertEqual( @@ -85,13 +84,13 @@ protected void AssertEqual( protected void AssertEqual( IEnumerable expectedIndexes, IEnumerable actualIndexes, - IndexComparer indexComparer = null) + TestIndexComparer testIndexComparer = null) { - indexComparer ??= new IndexComparer(compareAnnotations: false); + testIndexComparer ??= new TestIndexComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedIndexes, indexComparer), - new SortedSet(actualIndexes, indexComparer), - indexComparer); + new SortedSet(expectedIndexes, testIndexComparer), + new SortedSet(actualIndexes, testIndexComparer), + testIndexComparer); } protected virtual TestModelBuilder CreateModelBuilder()