From eda1513a893f0424c79204090c069eae378f63da Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Tue, 16 Mar 2021 13:45:22 -0700 Subject: [PATCH] Add a read-optimized implementation of relational metadata. Part of #8258 --- .../Storage/Internal/CosmosClientWrapper.cs | 2 +- .../Internal/SnapshotModelProcessor.cs | 2 +- .../RelationalEntityTypeExtensions.cs | 10 +- .../RelationalModelBuilderExtensions.cs | 2 +- .../Extensions/RelationalModelExtensions.cs | 9 +- .../RelationalPropertyBuilderExtensions.cs | 2 +- .../RelationalPropertyExtensions.cs | 17 +- .../RelationalModelValidator.cs | 2 +- .../SequenceUniquificationConvention.cs | 4 +- .../Metadata/ICheckConstraint.cs | 2 +- .../Metadata/IConventionCheckConstraint.cs | 6 +- .../Metadata/IConventionDbFunction.cs | 4 +- src/EFCore.Relational/Metadata/IDbFunction.cs | 3 +- .../Metadata/IDbFunctionParameter.cs | 6 +- .../Metadata/IMutableCheckConstraint.cs | 2 +- .../Metadata/IMutableDbFunction.cs | 4 +- .../Metadata/IReadOnlyCheckConstraint.cs | 2 +- .../Metadata/IReadOnlyDbFunction.cs | 2 +- .../Metadata/IReadOnlyDbFunctionParameter.cs | 6 +- .../Metadata/IReadOnlySequence.cs | 2 +- src/EFCore.Relational/Metadata/ISequence.cs | 2 +- .../Metadata/Internal/CheckConstraint.cs | 15 +- .../Metadata/Internal/DbFunction.cs | 39 +- .../Metadata/Internal/DbFunctionParameter.cs | 11 +- .../Metadata/Internal/FunctionMapping.cs | 2 +- .../Internal/IRelationalPropertyOverrides.cs | 42 ++ .../Metadata/Internal/IRuntimeDbFunction.cs | 26 ++ .../Internal/IRuntimeDbFunctionParameter.cs | 26 ++ .../Metadata/Internal/RelationalModel.cs | 25 +- .../Internal/RelationalPropertyOverrides.cs | 29 +- .../Metadata/Internal/Sequence.cs | 23 +- .../Metadata/Internal/StoreFunction.cs | 8 +- .../Internal/StoreFunctionParameter.cs | 2 +- .../Metadata/Internal/Table.cs | 2 +- .../Metadata/Internal/TableBase.cs | 4 +- .../Metadata/SlimCheckConstraint.cs | 86 ++++ .../Metadata/SlimDbFunction.cs | 258 +++++++++++ .../Metadata/SlimDbFunctionParameter.cs | 148 +++++++ .../SlimRelationalPropertyOverrides.cs | 63 +++ .../Metadata/SlimSequence.cs | 153 +++++++ .../Infrastructure/IReadOnlyAnnotatable.cs | 2 - .../Metadata/Builders/DiscriminatorBuilder.cs | 28 +- .../IConventionDiscriminatorBuilder.cs | 17 +- src/EFCore/Metadata/IConventionEntityType.cs | 14 +- src/EFCore/Metadata/IEntityType.cs | 5 +- src/EFCore/Metadata/IMutableEntityType.cs | 14 +- src/EFCore/Metadata/IPropertyBase.cs | 2 +- src/EFCore/Metadata/IReadOnlyEntityType.cs | 17 +- src/EFCore/Metadata/IReadOnlyForeignKey.cs | 2 +- src/EFCore/Metadata/IReadOnlyNavigation.cs | 2 +- src/EFCore/Metadata/IReadOnlyProperty.cs | 2 +- src/EFCore/Metadata/IReadOnlyPropertyBase.cs | 9 +- .../Metadata/IReadOnlySkipNavigation.cs | 2 +- src/EFCore/Metadata/IReadOnlyTypeBase.cs | 7 +- src/EFCore/Metadata/Internal/EntityType.cs | 46 +- src/EFCore/Metadata/Internal/ForeignKey.cs | 419 +++++++++--------- .../Internal/InternalEntityTypeBuilder.cs | 2 +- .../Internal/InternalForeignKeyBuilder.cs | 6 +- src/EFCore/Metadata/Internal/Key.cs | 32 +- src/EFCore/Metadata/Internal/Navigation.cs | 8 +- src/EFCore/Metadata/Internal/PropertyBase.cs | 1 - .../Metadata/Internal/ServiceProperty.cs | 44 +- src/EFCore/Metadata/SlimEntityType.cs | 43 +- src/EFCore/Metadata/SlimServiceProperty.cs | 7 + .../TestUtilities/CosmosTestStore.cs | 3 + .../Design/CSharpMigrationsGeneratorTest.cs | 2 +- .../Metadata/RelationalModelTest.cs | 5 +- ...exNavigationsSharedTypeQueryFixtureBase.cs | 6 +- .../Query/QueryBugsTest.cs | 2 +- .../Conventions/ConventionDispatcherTest.cs | 6 +- .../PropertyAttributeConventionTest.cs | 12 +- .../ClrCollectionAccessorFactoryTest.cs | 3 + .../Internal/ClrPropertyGetterFactoryTest.cs | 79 +--- .../Internal/ClrPropertySetterFactoryTest.cs | 79 +--- .../Internal/InternalForeignKeyBuilderTest.cs | 4 +- 75 files changed, 1394 insertions(+), 591 deletions(-) create mode 100644 src/EFCore.Relational/Metadata/Internal/IRelationalPropertyOverrides.cs create mode 100644 src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunction.cs create mode 100644 src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunctionParameter.cs create mode 100644 src/EFCore.Relational/Metadata/SlimCheckConstraint.cs create mode 100644 src/EFCore.Relational/Metadata/SlimDbFunction.cs create mode 100644 src/EFCore.Relational/Metadata/SlimDbFunctionParameter.cs create mode 100644 src/EFCore.Relational/Metadata/SlimRelationalPropertyOverrides.cs create mode 100644 src/EFCore.Relational/Metadata/SlimSequence.cs diff --git a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs index 39c5a0ab512..580119f4509 100644 --- a/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs +++ b/src/EFCore.Cosmos/Storage/Internal/CosmosClientWrapper.cs @@ -419,7 +419,7 @@ public virtual async Task DeleteItemOnceAsync( { var entry = parameters.Entry; var items = Client.GetDatabase(_databaseId).GetContainer(parameters.ContainerId); - + var itemRequestOptions = CreateItemRequestOptions(entry); var partitionKey = CreatePartitionKey(entry); diff --git a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs index 4535a67f50b..3b5351e2912 100644 --- a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs +++ b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs @@ -157,7 +157,7 @@ private void UpdateSequences(IReadOnlyModel model, string version) .Select(a => new Sequence(model, a.Name)); #pragma warning restore CS0618 // Type or member is obsolete - var sequencesDictionary = new SortedDictionary<(string, string?), Sequence>(); + var sequencesDictionary = new SortedDictionary<(string, string?), ISequence>(); foreach (var sequence in sequences) { sequencesDictionary[(sequence.Name, sequence.Schema)] = sequence; diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 1158a51048c..0d4d9a0f8b6 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -445,10 +445,10 @@ public static string GetDefaultSqlQueryName([NotNull] this IReadOnlyEntityType e /// The SQL string used to provide data for the entity type. public static string? GetSqlQuery([NotNull] this IReadOnlyEntityType entityType) { - var nameAnnotation = (string?)entityType[RelationalAnnotationNames.SqlQuery]; - if (nameAnnotation != null) + var queryAnnotation = (string?)entityType[RelationalAnnotationNames.SqlQuery]; + if (queryAnnotation != null) { - return nameAnnotation; + return queryAnnotation; } if (entityType.BaseType != null) @@ -706,14 +706,14 @@ public static IEnumerable GetCheckConstraints([NotNull /// /// The entity type to get the check constraints for. public static IEnumerable GetCheckConstraints([NotNull] this IMutableEntityType entityType) - => CheckConstraint.GetCheckConstraints(entityType); + => CheckConstraint.GetCheckConstraints(entityType).Cast(); /// /// Returns all contained in the entity type. /// /// The entity type to get the check constraints for. public static IEnumerable GetCheckConstraints([NotNull] this IConventionEntityType entityType) - => CheckConstraint.GetCheckConstraints(entityType); + => CheckConstraint.GetCheckConstraints(entityType).Cast(); /// /// Returns all contained in the entity type. diff --git a/src/EFCore.Relational/Extensions/RelationalModelBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalModelBuilderExtensions.cs index 9da71614a7b..b46d0fd4bb0 100644 --- a/src/EFCore.Relational/Extensions/RelationalModelBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalModelBuilderExtensions.cs @@ -217,7 +217,7 @@ private static Sequence HasSequence( Check.NotEmpty(name, nameof(name)); Check.NullButNotEmpty(schema, nameof(schema)); - var sequence = Sequence.FindSequence(model, name, schema); + var sequence = (Sequence?)Sequence.FindSequence(model, name, schema); if (sequence != null) { sequence.UpdateConfigurationSource(configurationSource); diff --git a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs index 6fb8833ece5..4f9c2a0ec70 100644 --- a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -257,14 +258,14 @@ public static IEnumerable GetSequences([NotNull] this IModel model) /// /// The model to get the sequences in. public static IEnumerable GetSequences([NotNull] this IMutableModel model) - => Sequence.GetSequences(Check.NotNull(model, nameof(model))); + => Sequence.GetSequences(Check.NotNull(model, nameof(model))).Cast(); /// /// Returns all sequences contained in the model. /// /// The model to get the sequences in. public static IEnumerable GetSequences([NotNull] this IConventionModel model) - => Sequence.GetSequences(Check.NotNull(model, nameof(model))); + => Sequence.GetSequences(Check.NotNull(model, nameof(model))).Cast(); /// /// Returns all sequences contained in the model. @@ -463,14 +464,14 @@ public static IEnumerable GetDbFunctions([NotNull] this IMo /// /// The model to get the functions in. public static IEnumerable GetDbFunctions([NotNull] this IMutableModel model) - => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))); + => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))).Cast(); /// /// Returns all functions contained in the model. /// /// The model to get the functions in. public static IEnumerable GetDbFunctions([NotNull] this IConventionModel model) - => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))); + => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))).Cast(); /// /// Returns all functions contained in the model. diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs index 2d1012ae997..0bfa9162c86 100644 --- a/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalPropertyBuilderExtensions.cs @@ -123,7 +123,7 @@ public static bool CanSetColumnName( in StoreObjectIdentifier storeObject, bool fromDataAnnotation = false) { - var overrides = RelationalPropertyOverrides.Find(propertyBuilder.Metadata, storeObject); + var overrides = (RelationalPropertyOverrides?)RelationalPropertyOverrides.Find(propertyBuilder.Metadata, storeObject); return overrides == null || (fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention) .Overrides(overrides.GetColumnNameConfigurationSource()) diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs index 3f3074f3c4e..5026f219634 100644 --- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs @@ -51,7 +51,7 @@ public static string GetColumnBaseName([NotNull] this IReadOnlyProperty property Check.NotNull(property, nameof(property)); var overrides = RelationalPropertyOverrides.Find(property, storeObject); - if (overrides?.GetColumnNameConfigurationSource() != null) + if (overrides?.ColumnNameOverriden == true) { return overrides.ColumnName; } @@ -276,7 +276,7 @@ public static void SetColumnName( public static ConfigurationSource? GetColumnNameConfigurationSource( [NotNull] this IConventionProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.Find(property, storeObject)?.GetColumnNameConfigurationSource(); + => ((RelationalPropertyOverrides?)RelationalPropertyOverrides.Find(property, storeObject))?.GetColumnNameConfigurationSource(); /// /// Returns the database type of the column to which the property is mapped, or if the database type @@ -1063,8 +1063,7 @@ public static void SetComment([NotNull] this IMutableProperty property, [CanBeNu /// The property. /// The for the column comment. public static ConfigurationSource? GetCommentConfigurationSource([NotNull] this IConventionProperty property) - => property.FindAnnotation(RelationalAnnotationNames.Comment) - ?.GetConfigurationSource(); + => property.FindAnnotation(RelationalAnnotationNames.Comment)?.GetConfigurationSource(); /// /// Returns the collation to be used for the column. @@ -1365,7 +1364,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IRea /// The identifier of the table-like store object containing the column. /// An object that stores property facet overrides. public static IMutableAnnotatable? FindOverrides([NotNull] this IMutableProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.Find(property, storeObject); + => (IMutableAnnotatable?)RelationalPropertyOverrides.Find(property, storeObject); /// /// @@ -1380,7 +1379,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IRea /// The identifier of the table-like store object containing the column. /// An object that stores property facet overrides. public static IConventionAnnotatable? FindOverrides([NotNull] this IConventionProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.Find(property, storeObject); + => (IConventionAnnotatable?)RelationalPropertyOverrides.Find(property, storeObject); /// /// @@ -1395,7 +1394,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IRea /// The identifier of the table-like store object containing the column. /// An object that stores property facet overrides. public static IAnnotatable? FindOverrides([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.Find(property, storeObject); + => (IAnnotatable?)RelationalPropertyOverrides.Find(property, storeObject); /// /// @@ -1412,7 +1411,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IRea public static IMutableAnnotatable GetOrCreateOverrides( [NotNull] this IMutableProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.GetOrCreate(property, storeObject); + => (IMutableAnnotatable)RelationalPropertyOverrides.GetOrCreate(property, storeObject); /// /// @@ -1429,6 +1428,6 @@ public static IMutableAnnotatable GetOrCreateOverrides( public static IConventionAnnotatable GetOrCreateOverrides( [NotNull] this IConventionProperty property, in StoreObjectIdentifier storeObject) - => RelationalPropertyOverrides.GetOrCreate(property, storeObject); + => (IConventionAnnotatable)RelationalPropertyOverrides.GetOrCreate(property, storeObject); } } diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index 9e35b2e015d..ee19cd9207b 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -1194,7 +1194,7 @@ protected virtual void ValidatePropertyOverrides( { foreach (var property in entityType.GetDeclaredProperties()) { - var tableOverrides = (SortedDictionary?) + var tableOverrides = (SortedDictionary?) property[RelationalAnnotationNames.RelationalOverrides]; if (tableOverrides == null) { diff --git a/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs b/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs index 40f08f8cdd4..273aec32e2f 100644 --- a/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/SequenceUniquificationConvention.cs @@ -41,7 +41,7 @@ public virtual void ProcessModelFinalizing( { var model = modelBuilder.Metadata; var modelSequences = - (SortedDictionary<(string Name, string Schema), Sequence>?)model[RelationalAnnotationNames.Sequences]; + (SortedDictionary<(string Name, string? Schema), ISequence>?)model[RelationalAnnotationNames.Sequences]; if (modelSequences != null) { @@ -55,7 +55,7 @@ public virtual void ProcessModelFinalizing( var newSequenceName = Uniquifier.Uniquify( sequence.Key.Name, modelSequences, sequenceName => (sequenceName, schemaName), maxLength); - Sequence.SetName((IMutableModel)model, sequence.Value, newSequenceName); + Sequence.SetName((IMutableModel)model, (Sequence)sequence.Value, newSequenceName); } } } diff --git a/src/EFCore.Relational/Metadata/ICheckConstraint.cs b/src/EFCore.Relational/Metadata/ICheckConstraint.cs index 58c07987a21..20ce8027402 100644 --- a/src/EFCore.Relational/Metadata/ICheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/ICheckConstraint.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface ICheckConstraint : IReadOnlyCheckConstraint, IAnnotatable { /// - /// Gets the in which this check constraint is defined. + /// Gets the entity type on which this check constraint is defined. /// new IEntityType EntityType { get; } diff --git a/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs b/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs index 9a25b92d6a1..fbae9a108ab 100644 --- a/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs @@ -9,14 +9,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface IConventionCheckConstraint : IReadOnlyCheckConstraint, IConventionAnnotatable { /// - /// Gets the in which this check constraint is defined. + /// Gets the entity type on which this check constraint is defined. /// new IConventionEntityType EntityType { get; } /// - /// Gets the configuration source for this . + /// Gets the configuration source for this check constraint. /// - /// The configuration source for . + /// The configuration source for this check constraint. ConfigurationSource GetConfigurationSource(); } } diff --git a/src/EFCore.Relational/Metadata/IConventionDbFunction.cs b/src/EFCore.Relational/Metadata/IConventionDbFunction.cs index dd50a7f789f..ee2b232e5c2 100644 --- a/src/EFCore.Relational/Metadata/IConventionDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IConventionDbFunction.cs @@ -11,8 +11,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relational database function in an in - /// the a form that can be mutated while the model is being built. + /// Represents a relational database function in a model in + /// the form that can be mutated while the model is being built. /// public interface IConventionDbFunction : IReadOnlyDbFunction, IConventionAnnotatable { diff --git a/src/EFCore.Relational/Metadata/IDbFunction.cs b/src/EFCore.Relational/Metadata/IDbFunction.cs index dbfb3969ef5..da5f177ab4d 100644 --- a/src/EFCore.Relational/Metadata/IDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IDbFunction.cs @@ -7,8 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relational database function in an in - /// the a form that can be mutated while the model is being built. + /// Represents a relational database function in a model. /// public interface IDbFunction : IReadOnlyDbFunction, IAnnotatable { diff --git a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs index c44a118cf34..9f07e0eb634 100644 --- a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Metadata { @@ -11,6 +10,11 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// public interface IDbFunctionParameter : IReadOnlyDbFunctionParameter, IAnnotatable { + /// + /// Gets the store type of this parameter. + /// + new string StoreType { get; } + /// /// Gets the function to which this parameter belongs. /// diff --git a/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs b/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs index 8c23cefd91e..9e7bbe3f659 100644 --- a/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface IMutableCheckConstraint : IReadOnlyCheckConstraint, IMutableAnnotatable { /// - /// Gets the in which this check constraint is defined. + /// Gets the entity type on which this check constraint is defined. /// new IMutableEntityType EntityType { get; } } diff --git a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs index da5864c9879..927659c1c72 100644 --- a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs @@ -10,8 +10,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relational database function in an in - /// the a form that can be mutated while the model is being built. + /// Represents a relational database function in an model in + /// the form that can be mutated while the model is being built. /// public interface IMutableDbFunction : IReadOnlyDbFunction, IMutableAnnotatable { diff --git a/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs b/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs index 7ae49905a1f..269587dd487 100644 --- a/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs @@ -16,7 +16,7 @@ public interface IReadOnlyCheckConstraint : IReadOnlyAnnotatable string Name { get; } /// - /// Gets the in which this check constraint is defined. + /// Gets the entity type on which this check constraint is defined. /// IReadOnlyEntityType EntityType { get; } diff --git a/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs b/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs index 41a1b920bc3..f04e55d8a06 100644 --- a/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relational database function in an . + /// Represents a relational database function in a model. /// public interface IReadOnlyDbFunction : IReadOnlyAnnotatable { diff --git a/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs index 9428223837d..f68bc6b68b9 100644 --- a/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a parameter. + /// Represents a function parameter. /// public interface IReadOnlyDbFunctionParameter : IReadOnlyAnnotatable { @@ -34,8 +34,8 @@ public interface IReadOnlyDbFunctionParameter : IReadOnlyAnnotatable string? StoreType { get; } /// - /// Gets the value which indicates whether parameter propagates nullability, meaning if it's value is null the database function itself - /// returns null. + /// Gets the value which indicates whether the parameter propagates nullability, + /// meaning if it's value is the database function itself returns . /// bool PropagatesNullability { get; } diff --git a/src/EFCore.Relational/Metadata/IReadOnlySequence.cs b/src/EFCore.Relational/Metadata/IReadOnlySequence.cs index 8e1312c2012..b3c97868956 100644 --- a/src/EFCore.Relational/Metadata/IReadOnlySequence.cs +++ b/src/EFCore.Relational/Metadata/IReadOnlySequence.cs @@ -23,7 +23,7 @@ public interface IReadOnlySequence : IReadOnlyAnnotatable string? Schema { get; } /// - /// Gets the in which this sequence is defined. + /// Gets the model in which this sequence is defined. /// IReadOnlyModel Model { get; } diff --git a/src/EFCore.Relational/Metadata/ISequence.cs b/src/EFCore.Relational/Metadata/ISequence.cs index 52487036f72..b7a8fb50152 100644 --- a/src/EFCore.Relational/Metadata/ISequence.cs +++ b/src/EFCore.Relational/Metadata/ISequence.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface ISequence : IReadOnlySequence, IAnnotatable { /// - /// Gets the in which this sequence is defined. + /// Gets the model in which this sequence is defined. /// new IModel Model { get; } } diff --git a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs index ca5941ff7f6..fd48115ff70 100644 --- a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs @@ -46,7 +46,7 @@ public CheckConstraint( var dataDictionary = GetConstraintsDictionary(EntityType); if (dataDictionary == null) { - dataDictionary = new Dictionary(); + dataDictionary = new Dictionary(); ((IMutableEntityType)EntityType).SetOrRemoveAnnotation(RelationalAnnotationNames.CheckConstraints, dataDictionary); } @@ -66,11 +66,11 @@ public CheckConstraint( /// 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 GetCheckConstraints([NotNull] IReadOnlyEntityType entityType) + public static IEnumerable GetCheckConstraints([NotNull] IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); - return GetConstraintsDictionary(entityType)?.Values ?? Enumerable.Empty(); + return GetConstraintsDictionary(entityType)?.Values ?? Enumerable.Empty(); } /// @@ -107,10 +107,11 @@ public static IEnumerable GetCheckConstraints([NotNull] IReadOn if (dataDictionary != null && dataDictionary.TryGetValue(name, out var constraint)) { - constraint.EnsureMutable(); + var checkConstraint = (CheckConstraint)constraint; + checkConstraint.EnsureMutable(); dataDictionary.Remove(name); - return constraint; + return checkConstraint; } return null; @@ -165,8 +166,8 @@ public virtual void UpdateConfigurationSource(ConfigurationSource configurationS _configurationSource = configurationSource.Max(_configurationSource); } - private static Dictionary? GetConstraintsDictionary(IReadOnlyEntityType entityType) - => (Dictionary?)entityType[RelationalAnnotationNames.CheckConstraints]; + private static Dictionary? GetConstraintsDictionary(IReadOnlyEntityType entityType) + => (Dictionary?)entityType[RelationalAnnotationNames.CheckConstraints]; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs index 847e84e41d4..3007681ba2b 100644 --- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs +++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs @@ -25,7 +25,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.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 DbFunction : ConventionAnnotatable, IMutableDbFunction, IConventionDbFunction, IDbFunction + public class DbFunction : ConventionAnnotatable, IMutableDbFunction, IConventionDbFunction, IRuntimeDbFunction { private readonly List _parameters; private string? _schema; @@ -188,10 +188,10 @@ public virtual void SetRemovedFromModel() /// 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 GetDbFunctions([NotNull] IReadOnlyModel model) - => ((SortedDictionary?)model[RelationalAnnotationNames.DbFunctions]) + public static IEnumerable GetDbFunctions([NotNull] IReadOnlyModel model) + => ((SortedDictionary?)model[RelationalAnnotationNames.DbFunctions]) ?.Values - ?? Enumerable.Empty(); + ?? Enumerable.Empty(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -199,8 +199,8 @@ public static IEnumerable GetDbFunctions([NotNull] IReadOnlyModel mo /// 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 DbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] MethodInfo methodInfo) - => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions + public static IReadOnlyDbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] MethodInfo methodInfo) + => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions && functions.TryGetValue(GetFunctionName(methodInfo, methodInfo.GetParameters()), out var dbFunction) ? dbFunction : null; @@ -211,8 +211,8 @@ public static IEnumerable GetDbFunctions([NotNull] IReadOnlyModel mo /// 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 DbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] string name) - => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions + public static IReadOnlyDbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] string name) + => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions && functions.TryGetValue(name, out var dbFunction) ? dbFunction : null; @@ -252,9 +252,9 @@ public static DbFunction AddDbFunction( return function; } - private static SortedDictionary GetOrCreateFunctions(IMutableModel model) - => (SortedDictionary)( - model[RelationalAnnotationNames.DbFunctions] ??= new SortedDictionary()); + private static SortedDictionary GetOrCreateFunctions(IMutableModel model) + => (SortedDictionary)( + model[RelationalAnnotationNames.DbFunctions] ??= new SortedDictionary()); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -266,15 +266,16 @@ private static SortedDictionary GetOrCreateFunctions(IMutabl [NotNull] IMutableModel model, [NotNull] MethodInfo methodInfo) { - if (model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions) + if (model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions) { var name = GetFunctionName(methodInfo, methodInfo.GetParameters()); if (functions.TryGetValue(name, out var function)) { + var dbFunction = (DbFunction)function; functions.Remove(name); - function.SetRemovedFromModel(); + dbFunction.SetRemovedFromModel(); - return function; + return dbFunction; } } @@ -291,11 +292,11 @@ private static SortedDictionary GetOrCreateFunctions(IMutabl [NotNull] IMutableModel model, [NotNull] string name) { - if (model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions + if (model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions && functions.TryGetValue(name, out var function)) { functions.Remove(name); - function.SetRemovedFromModel(); + ((DbFunction)function).SetRemovedFromModel(); } return null; @@ -763,5 +764,11 @@ bool IConventionDbFunction.SetIsNullable(bool nullable, bool fromDataAnnotation) /// IStoreFunction IDbFunction.StoreFunction => StoreFunction!; // Relational model creation ensures StoreFunction is populated + + IStoreFunction IRuntimeDbFunction.StoreFunction + { + get => StoreFunction!; + set => StoreFunction = value; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs index 77be9a33f9e..c55c9bb7138 100644 --- a/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs @@ -24,7 +24,7 @@ public class DbFunctionParameter : ConventionAnnotatable, IMutableDbFunctionParameter, IConventionDbFunctionParameter, - IDbFunctionParameter + IRuntimeDbFunctionParameter { private string? _storeType; private RelationalTypeMapping? _typeMapping; @@ -111,7 +111,7 @@ public virtual ConfigurationSource GetConfigurationSource() => Function.GetConfigurationSource(); /// - public virtual IStoreFunctionParameter StoreFunctionParameter { get; [param: NotNull] set; } = default!; + public virtual IStoreFunctionParameter StoreFunctionParameter { get; set; } = default!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -285,6 +285,13 @@ IDbFunction IDbFunctionParameter.Function RelationalTypeMapping? IConventionDbFunctionParameter.SetTypeMapping(RelationalTypeMapping? typeMapping, bool fromDataAnnotation) => SetTypeMapping(typeMapping, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// + string IDbFunctionParameter.StoreType + { + [DebuggerStepThrough] + get => StoreType!; + } + /// [DebuggerStepThrough] string? IConventionDbFunctionParameter.SetStoreType(string? storeType, bool fromDataAnnotation) diff --git a/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs b/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs index bacfa8b37b5..d71bc0dea16 100644 --- a/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/FunctionMapping.cs @@ -26,7 +26,7 @@ public class FunctionMapping : TableMappingBase, IFunctionMapping public FunctionMapping( [NotNull] IEntityType entityType, [NotNull] StoreFunction storeFunction, - [NotNull] DbFunction dbFunction, + [NotNull] IDbFunction dbFunction, bool includesDerivedTypes) : base(entityType, storeFunction, includesDerivedTypes) { diff --git a/src/EFCore.Relational/Metadata/Internal/IRelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/IRelationalPropertyOverrides.cs new file mode 100644 index 00000000000..a348f6dcc5a --- /dev/null +++ b/src/EFCore.Relational/Metadata/Internal/IRelationalPropertyOverrides.cs @@ -0,0 +1,42 @@ +// 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 Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +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 interface IRelationalPropertyOverrides : IAnnotatable + { + /// + /// 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. + /// + IProperty Property { get; } + + /// + /// 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. + /// + string? ColumnName { get; } + + /// + /// 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. + /// + bool ColumnNameOverriden { get; } + } +} diff --git a/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunction.cs b/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunction.cs new file mode 100644 index 00000000000..c159ea1bb9c --- /dev/null +++ b/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunction.cs @@ -0,0 +1,26 @@ +// 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. + +#nullable enable + +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 interface IRuntimeDbFunction : IDbFunction + { + /// + /// 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. + /// + new IStoreFunction StoreFunction { get; [param: NotNull] set; } + } +} diff --git a/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunctionParameter.cs new file mode 100644 index 00000000000..4ed5f10aa46 --- /dev/null +++ b/src/EFCore.Relational/Metadata/Internal/IRuntimeDbFunctionParameter.cs @@ -0,0 +1,26 @@ +// 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; + +#nullable enable + +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 interface IRuntimeDbFunctionParameter : IDbFunctionParameter + { + /// + /// 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. + /// + new IStoreFunctionParameter StoreFunctionParameter { get; [param: NotNull] set; } + } +} diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index e6314465a46..ecd84a8962e 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -122,6 +122,20 @@ public RelationalModel([NotNull] IModel model) public static IModel Add( [NotNull] IModel model, [CanBeNull] IRelationalAnnotationProvider? relationalAnnotationProvider) + { + model.AddRuntimeAnnotation(RelationalAnnotationNames.RelationalModel, Create(model, relationalAnnotationProvider)); + return model; + } + + /// + /// 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 IRelationalModel Create( + [NotNull] IModel model, + [CanBeNull] IRelationalAnnotationProvider? relationalAnnotationProvider) { var databaseModel = new RelationalModel(model); @@ -228,8 +242,7 @@ public static IModel Add( } databaseModel._isReadOnly = true; - model.AddRuntimeAnnotation(RelationalAnnotationNames.RelationalModel, databaseModel); - return model; + return databaseModel; } private static void AddDefaultMappings(RelationalModel databaseModel, IEntityType entityType) @@ -612,7 +625,7 @@ private static void AddMappedFunctions(RelationalModel databaseModel, IEntityTyp break; } - var dbFunction = (DbFunction)model.FindDbFunction(mappedFunctionName)!; + var dbFunction = (IRuntimeDbFunction)model.FindDbFunction(mappedFunctionName)!; var functionMapping = CreateFunctionMapping(entityType, mappedType, dbFunction, databaseModel, @default: true); mappedType = mappedType.BaseType; @@ -638,7 +651,7 @@ private static void AddMappedFunctions(RelationalModel databaseModel, IEntityTyp private static void AddTVFs(RelationalModel relationalModel) { var model = relationalModel.Model; - foreach (DbFunction function in model.GetDbFunctions()) + foreach (IRuntimeDbFunction function in model.GetDbFunctions()) { var entityType = function.IsScalar ? null @@ -671,7 +684,7 @@ private static void AddTVFs(RelationalModel relationalModel) private static FunctionMapping CreateFunctionMapping( IEntityType entityType, IEntityType mappedType, - DbFunction dbFunction, + IRuntimeDbFunction dbFunction, RelationalModel model, bool @default) { @@ -723,7 +736,7 @@ private static FunctionMapping CreateFunctionMapping( return functionMapping; } - private static StoreFunction GetOrCreateStoreFunction(DbFunction dbFunction, RelationalModel model) + private static StoreFunction GetOrCreateStoreFunction(IRuntimeDbFunction dbFunction, RelationalModel model) { var storeFunction = (StoreFunction?)dbFunction.StoreFunction; if (storeFunction == null) diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs index 3a4cd3cc99a..7d2a2f97e56 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.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.Collections.Generic; +using System.Diagnostics; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -13,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.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 RelationalPropertyOverrides : ConventionAnnotatable + public class RelationalPropertyOverrides : ConventionAnnotatable, IRelationalPropertyOverrides { private string? _columnName; @@ -75,6 +76,15 @@ public virtual string? ColumnName return columnName; } + /// + /// 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 ColumnNameOverriden + => _columnNameConfigurationSource != 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 @@ -90,9 +100,9 @@ public virtual string? ColumnName /// 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 RelationalPropertyOverrides? Find([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) + public static IRelationalPropertyOverrides? Find([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { - var tableOverrides = (SortedDictionary?) + var tableOverrides = (SortedDictionary?) property[RelationalAnnotationNames.RelationalOverrides]; return tableOverrides != null && tableOverrides.TryGetValue(storeObject, out var overrides) @@ -110,11 +120,11 @@ public static RelationalPropertyOverrides GetOrCreate( [NotNull] IMutableProperty property, in StoreObjectIdentifier storeObject) { - var tableOverrides = (SortedDictionary?) + var tableOverrides = (SortedDictionary?) property[RelationalAnnotationNames.RelationalOverrides]; if (tableOverrides == null) { - tableOverrides = new SortedDictionary(); + tableOverrides = new SortedDictionary(); property[RelationalAnnotationNames.RelationalOverrides] = tableOverrides; } @@ -124,7 +134,7 @@ public static RelationalPropertyOverrides GetOrCreate( tableOverrides.Add(storeObject, overrides); } - return overrides; + return (RelationalPropertyOverrides)overrides; } /// @@ -137,5 +147,12 @@ public static RelationalPropertyOverrides GetOrCreate( [NotNull] IConventionProperty property, in StoreObjectIdentifier storeObject) => GetOrCreate((IMutableProperty)property, storeObject); + + /// + IProperty IRelationalPropertyOverrides.Property + { + [DebuggerStepThrough] + get => (IProperty)Property; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/Sequence.cs b/src/EFCore.Relational/Metadata/Internal/Sequence.cs index 64694bb07ef..a1b6a9d614d 100644 --- a/src/EFCore.Relational/Metadata/Internal/Sequence.cs +++ b/src/EFCore.Relational/Metadata/Internal/Sequence.cs @@ -144,10 +144,10 @@ public Sequence([NotNull] IReadOnlyModel model, [NotNull] string annotationName) /// 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 GetSequences([NotNull] IReadOnlyModel model) - => ((SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]) + public static IEnumerable GetSequences([NotNull] IReadOnlyModel model) + => ((SortedDictionary<(string, string?), ISequence>?)model[RelationalAnnotationNames.Sequences]) ?.Values - ?? Enumerable.Empty(); + ?? Enumerable.Empty(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -155,9 +155,9 @@ public static IEnumerable GetSequences([NotNull] IReadOnlyModel model) /// 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 Sequence? FindSequence([NotNull] IReadOnlyModel model, [NotNull] string name, [CanBeNull] string? schema) + public static ISequence? FindSequence([NotNull] IReadOnlyModel model, [NotNull] string name, [CanBeNull] string? schema) { - var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; + var sequences = (SortedDictionary<(string, string?), ISequence>?)model[RelationalAnnotationNames.Sequences]; if (sequences == null || !sequences.TryGetValue((name, schema), out var sequence)) { @@ -180,10 +180,10 @@ public static Sequence AddSequence( ConfigurationSource configurationSource) { var sequence = new Sequence(name, schema, model, configurationSource); - var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; + var sequences = (SortedDictionary<(string, string?), ISequence>?)model[RelationalAnnotationNames.Sequences]; if (sequences == null) { - sequences = new SortedDictionary<(string, string?), Sequence>(); + sequences = new SortedDictionary<(string, string?), ISequence>(); model[RelationalAnnotationNames.Sequences] = sequences; } @@ -208,7 +208,7 @@ public static Sequence AddSequence( sequence.EnsureMutable(); - var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; + var sequences = (SortedDictionary<(string, string?), ISequence>?)model[RelationalAnnotationNames.Sequences]; var tuple = (sequence.Name, sequence.Schema); if (sequences == null || !sequences.ContainsKey(tuple)) @@ -233,17 +233,18 @@ public static Sequence AddSequence( /// public static Sequence? RemoveSequence([NotNull] IMutableModel model, [NotNull] string name, [CanBeNull] string? schema) { - var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; + var sequences = (SortedDictionary<(string, string?), ISequence>?)model[RelationalAnnotationNames.Sequences]; if (sequences == null || !sequences.TryGetValue((name, schema), out var sequence)) { return null; } + var mutableSequence = (Sequence)sequence; sequences.Remove((name, schema)); - sequence.SetRemovedFromModel(); + mutableSequence.SetRemovedFromModel(); - return sequence; + return mutableSequence; } /// diff --git a/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs b/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs index a01302e8fab..7fe77ae6ea2 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreFunction.cs @@ -23,17 +23,17 @@ public class StoreFunction : TableBase, IStoreFunction /// 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 StoreFunction([NotNull] DbFunction dbFunction, [NotNull] RelationalModel model) + public StoreFunction([NotNull] IRuntimeDbFunction dbFunction, [NotNull] RelationalModel model) : base(dbFunction.Name, dbFunction.Schema, model) { - DbFunctions = new SortedDictionary { { dbFunction.ModelName, dbFunction } }; + DbFunctions = new SortedDictionary { { dbFunction.ModelName, dbFunction } }; IsBuiltIn = dbFunction.IsBuiltIn; ReturnType = dbFunction.StoreType; Parameters = new StoreFunctionParameter[dbFunction.Parameters.Count]; for (var i = 0; i < dbFunction.Parameters.Count; i++) { - Parameters[i] = new StoreFunctionParameter(this, dbFunction.Parameters[i]); + Parameters[i] = new StoreFunctionParameter(this, (IRuntimeDbFunctionParameter)dbFunction.Parameters[i]); } dbFunction.StoreFunction = this; @@ -45,7 +45,7 @@ public StoreFunction([NotNull] DbFunction dbFunction, [NotNull] RelationalModel /// 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 SortedDictionary DbFunctions { get; } + public virtual SortedDictionary DbFunctions { get; } /// public virtual bool IsBuiltIn { get; } diff --git a/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs index cb263ab668c..6b04fdb5313 100644 --- a/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs @@ -24,7 +24,7 @@ public class StoreFunctionParameter : Annotatable, IStoreFunctionParameter /// public StoreFunctionParameter( [NotNull] StoreFunction function, - [NotNull] DbFunctionParameter parameter) + [NotNull] IRuntimeDbFunctionParameter parameter) { Function = function; Name = parameter.Name; diff --git a/src/EFCore.Relational/Metadata/Internal/Table.cs b/src/EFCore.Relational/Metadata/Internal/Table.cs index 080b8c470b2..813e6128880 100644 --- a/src/EFCore.Relational/Metadata/Internal/Table.cs +++ b/src/EFCore.Relational/Metadata/Internal/Table.cs @@ -28,7 +28,7 @@ public class Table : TableBase, ITable public Table([NotNull] string name, [CanBeNull] string? schema, [NotNull] RelationalModel model) : base(name, schema, model) { - Columns = new SortedDictionary(new ColumnNameComparer(this)); + Columns = new SortedDictionary(new ColumnNameComparer(this)); } /// diff --git a/src/EFCore.Relational/Metadata/Internal/TableBase.cs b/src/EFCore.Relational/Metadata/Internal/TableBase.cs index a3f8301cb26..0ebf04bca3a 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableBase.cs @@ -68,7 +68,7 @@ public TableBase([NotNull] string name, [CanBeNull] string? schema, [NotNull] Re /// 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 SortedSet EntityTypeMappings { get; } + public virtual SortedSet EntityTypeMappings { get; } = new(TableMappingBaseComparer.Instance); /// @@ -77,7 +77,7 @@ public TableBase([NotNull] string name, [CanBeNull] string? schema, [NotNull] Re /// 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 SortedDictionary Columns { get; [param: NotNull] protected set; } + public virtual SortedDictionary Columns { get; [param: NotNull] protected set; } = new(StringComparer.Ordinal); /// diff --git a/src/EFCore.Relational/Metadata/SlimCheckConstraint.cs b/src/EFCore.Relational/Metadata/SlimCheckConstraint.cs new file mode 100644 index 00000000000..c6e73743f34 --- /dev/null +++ b/src/EFCore.Relational/Metadata/SlimCheckConstraint.cs @@ -0,0 +1,86 @@ +// 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.Diagnostics; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a check constraint in the . + /// + public class SlimCheckConstraint : AnnotatableBase, ICheckConstraint + { + /// + /// 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. + /// + [EntityFrameworkInternal] + public SlimCheckConstraint( + [NotNull] string name, + [NotNull] SlimEntityType entityType, + [NotNull] string sql) + { + EntityType = entityType; + Name = name; + Sql = sql; + } + + /// + /// Gets the entity type on which this check constraint is defined. + /// + public virtual SlimEntityType EntityType { get; } + + /// + /// Gets the name of the check constraint in the database. + /// + public virtual string Name { get; } + + private string Sql { get; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + => ((ICheckConstraint)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + /// + /// 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. + /// + [EntityFrameworkInternal] + public virtual DebugView DebugView + => new( + () => ((ICheckConstraint)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((ICheckConstraint)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + + /// + IReadOnlyEntityType IReadOnlyCheckConstraint.EntityType + { + [DebuggerStepThrough] + get => EntityType; + } + + /// + IEntityType ICheckConstraint.EntityType + { + [DebuggerStepThrough] + get => EntityType; + } + + /// + string IReadOnlyCheckConstraint.Sql + { + [DebuggerStepThrough] + get => Sql; + } + } +} diff --git a/src/EFCore.Relational/Metadata/SlimDbFunction.cs b/src/EFCore.Relational/Metadata/SlimDbFunction.cs new file mode 100644 index 00000000000..1b83c7bc22a --- /dev/null +++ b/src/EFCore.Relational/Metadata/SlimDbFunction.cs @@ -0,0 +1,258 @@ +// 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.Diagnostics; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.Storage; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a relational database function in a model. + /// + public class SlimDbFunction : AnnotatableBase, IRuntimeDbFunction + { + private readonly List _parameters = new(); + private readonly MethodInfo? _methodInfo; + private readonly Type _returnType; + private readonly bool _isScalar; + private readonly bool _isAggregate; + private readonly bool _isNullable; + private readonly bool _isBuiltIn; + private readonly string _storeName; + private readonly string? _schema; + private readonly string? _storeType; + private readonly Func, SqlExpression>? _translation; + private RelationalTypeMapping? _typeMapping; + private IStoreFunction? _storeFunction; + + /// + /// 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. + /// + [EntityFrameworkInternal] + public SlimDbFunction( + [NotNull] string modelName, + [NotNull] SlimModel model, + [CanBeNull] MethodInfo? methodInfo, + [NotNull] Type returnType, + bool scalar, + bool aggregate, + bool nullable, + bool builtIn, + [NotNull] string storeName, + [CanBeNull] string? schema, + [CanBeNull] string? storeType, + [CanBeNull] RelationalTypeMapping? typeMapping = null, + [CanBeNull] Func, SqlExpression>? translation = null) + { + ModelName = modelName; + Model = model; + _returnType = returnType; + _methodInfo = methodInfo; + _isScalar = scalar; + _isAggregate = aggregate; + _isNullable = nullable; + _isBuiltIn = builtIn; + _storeName = storeName; + _schema = schema; + _storeType = storeType; + _typeMapping = typeMapping; + _translation = translation; + } + + /// + /// Gets the model in which this function is defined. + /// + public virtual SlimModel Model { get; } + + /// + /// Gets the name of the function in the model. + /// + public virtual string ModelName { get; } + + /// + /// Adds a parameter to the function. + /// + /// The parameter name. + /// The parameter type. + /// A value which indicates whether the parameter propagates nullability. + /// The store type of this parameter. + /// The for this parameter. + /// The new parameter. + public virtual SlimDbFunctionParameter AddParameter( + [NotNull] string name, + [NotNull] Type clrType, + bool propagatesNullability, + [NotNull] string storeType, + [CanBeNull] RelationalTypeMapping? typeMapping = null) + { + var slimFunctionParameter = new SlimDbFunctionParameter(this, + name, + clrType, + propagatesNullability, + storeType, + typeMapping); + + _parameters.Add(slimFunctionParameter); + return slimFunctionParameter; + } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + => ((IDbFunction)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + /// + /// 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. + /// + [EntityFrameworkInternal] + public virtual DebugView DebugView + => new( + () => ((IDbFunction)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((IDbFunction)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + + /// + IReadOnlyModel IReadOnlyDbFunction.Model + { + [DebuggerStepThrough] + get => Model; + } + + /// + IModel IDbFunction.Model + { + [DebuggerStepThrough] + get => Model; + } + + /// + IReadOnlyList IReadOnlyDbFunction.Parameters + { + [DebuggerStepThrough] + get => _parameters; + } + + /// + IReadOnlyList IDbFunction.Parameters + { + [DebuggerStepThrough] + get => _parameters; + } + + /// + MethodInfo? IReadOnlyDbFunction.MethodInfo + { + [DebuggerStepThrough] + get => _methodInfo; + } + + /// + Type IReadOnlyDbFunction.ReturnType + { + [DebuggerStepThrough] + get => _returnType; + } + + /// + bool IReadOnlyDbFunction.IsScalar + { + [DebuggerStepThrough] + get => _isScalar; + } + + /// + bool IReadOnlyDbFunction.IsAggregate + { + [DebuggerStepThrough] + get => _isAggregate; + } + + /// + bool IReadOnlyDbFunction.IsBuiltIn + { + [DebuggerStepThrough] + get => _isBuiltIn; + } + + /// + bool IReadOnlyDbFunction.IsNullable + { + [DebuggerStepThrough] + get => _isNullable; + } + + /// + IStoreFunction IDbFunction.StoreFunction + { + [DebuggerStepThrough] + get => _storeFunction!; + } + + IStoreFunction IRuntimeDbFunction.StoreFunction + { + get => _storeFunction!; + set => _storeFunction = value; + } + + /// + string IReadOnlyDbFunction.Name + { + [DebuggerStepThrough] + get => _storeName; + } + + /// + string? IReadOnlyDbFunction.Schema + { + [DebuggerStepThrough] + get => _schema; + } + + /// + string? IReadOnlyDbFunction.StoreType + { + [DebuggerStepThrough] + get => _storeType; + } + + /// + Func, SqlExpression>? IReadOnlyDbFunction.Translation + { + [DebuggerStepThrough] + get => _translation; + } + + /// + RelationalTypeMapping? IReadOnlyDbFunction.TypeMapping + { + [DebuggerStepThrough] + get => _isScalar + ? NonCapturingLazyInitializer.EnsureInitialized(ref _typeMapping, this, static dbFunction => + { + var relationalTypeMappingSource = + (IRelationalTypeMappingSource)((IModel)dbFunction.Model).GetModelDependencies().TypeMappingSource; + return !string.IsNullOrEmpty(dbFunction._storeType) + ? relationalTypeMappingSource.FindMapping(dbFunction._storeType)! + : relationalTypeMappingSource.FindMapping(dbFunction._returnType)!; + }) + : _typeMapping; + } + } +} diff --git a/src/EFCore.Relational/Metadata/SlimDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/SlimDbFunctionParameter.cs new file mode 100644 index 00000000000..332f3c5b956 --- /dev/null +++ b/src/EFCore.Relational/Metadata/SlimDbFunctionParameter.cs @@ -0,0 +1,148 @@ +// 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.Diagnostics; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Storage; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a function parameter. + /// + public class SlimDbFunctionParameter : AnnotatableBase, IRuntimeDbFunctionParameter + { + private readonly string _name; + private readonly Type _clrType; + private readonly bool _propagatesNullability; + private readonly string _storeType; + private IStoreFunctionParameter? _storeFunctionParameter; + private RelationalTypeMapping? _typeMapping; + + /// + /// 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. + /// + [EntityFrameworkInternal] + public SlimDbFunctionParameter( + [NotNull] SlimDbFunction function, + [NotNull] string name, + [NotNull] Type clrType, + bool propagatesNullability, + [NotNull] string storeType, + [CanBeNull] RelationalTypeMapping? typeMapping) + { + Function = function; + _name = name; + _clrType = clrType; + _propagatesNullability = propagatesNullability; + _storeType = storeType; + _typeMapping = typeMapping; + } + + /// + /// Gets the name of the function in the database. + /// + public virtual string Name + { + [DebuggerStepThrough] + get => _name; + } + + /// + /// Gets the function to which this parameter belongs. + /// + public virtual SlimDbFunction Function { get; } + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + => ((IDbFunctionParameter)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + /// + /// 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. + /// + [EntityFrameworkInternal] + public virtual DebugView DebugView + => new( + () => ((IDbFunctionParameter)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((IDbFunctionParameter)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + + /// + IReadOnlyDbFunction IReadOnlyDbFunctionParameter.Function + { + [DebuggerStepThrough] + get => Function; + } + + /// + IDbFunction IDbFunctionParameter.Function + { + [DebuggerStepThrough] + get => Function; + } + + /// + IStoreFunctionParameter IDbFunctionParameter.StoreFunctionParameter + { + [DebuggerStepThrough] + get => _storeFunctionParameter!; + } + + IStoreFunctionParameter IRuntimeDbFunctionParameter.StoreFunctionParameter + { + get => _storeFunctionParameter!; + set => _storeFunctionParameter = value; + } + + /// + Type IReadOnlyDbFunctionParameter.ClrType + { + [DebuggerStepThrough] + get => _clrType; + } + + /// + string? IReadOnlyDbFunctionParameter.StoreType + { + [DebuggerStepThrough] + get => _storeType; + } + + /// + string IDbFunctionParameter.StoreType + { + [DebuggerStepThrough] + get => _storeType; + } + + /// + bool IReadOnlyDbFunctionParameter.PropagatesNullability + { + [DebuggerStepThrough] + get => _propagatesNullability; + } + + /// + RelationalTypeMapping? IReadOnlyDbFunctionParameter.TypeMapping + => NonCapturingLazyInitializer.EnsureInitialized(ref _typeMapping, this, static parameter => + { + var relationalTypeMappingSource = + (IRelationalTypeMappingSource)((IModel)parameter.Function.Model).GetModelDependencies().TypeMappingSource; + return relationalTypeMappingSource.FindMapping(parameter._storeType)!; + }); + } +} diff --git a/src/EFCore.Relational/Metadata/SlimRelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/SlimRelationalPropertyOverrides.cs new file mode 100644 index 00000000000..196a0bcc7cf --- /dev/null +++ b/src/EFCore.Relational/Metadata/SlimRelationalPropertyOverrides.cs @@ -0,0 +1,63 @@ +// 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.Diagnostics; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents property facet overrides for a particular table-like store object. + /// + public class SlimRelationalPropertyOverrides : AnnotatableBase, IRelationalPropertyOverrides + { + /// + /// 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. + /// + [EntityFrameworkInternal] + public SlimRelationalPropertyOverrides( + [NotNull] SlimProperty property, + [CanBeNull] string? columnName, + bool columnNameOverriden) + { + Property = property; + if (columnNameOverriden) + { + SetAnnotation(RelationalAnnotationNames.ColumnName, columnName); + } + } + + /// + /// Gets the property for which the overrides are applied. + /// + public virtual SlimProperty Property { get; } + + /// + IProperty IRelationalPropertyOverrides.Property + { + [DebuggerStepThrough] + get => Property; + } + + /// + string? IRelationalPropertyOverrides.ColumnName + { + [DebuggerStepThrough] + get => (string?)this[RelationalAnnotationNames.ColumnName]; + } + + /// + bool IRelationalPropertyOverrides.ColumnNameOverriden + { + [DebuggerStepThrough] + get => FindAnnotation(RelationalAnnotationNames.ColumnName) != null; + } + } +} diff --git a/src/EFCore.Relational/Metadata/SlimSequence.cs b/src/EFCore.Relational/Metadata/SlimSequence.cs new file mode 100644 index 00000000000..a9a04870eec --- /dev/null +++ b/src/EFCore.Relational/Metadata/SlimSequence.cs @@ -0,0 +1,153 @@ +// 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.Diagnostics; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a database sequence in the model. + /// + public class SlimSequence : AnnotatableBase, ISequence + { + private readonly string? _schema; + private readonly Type _type; + private readonly long _startValue; + private readonly int _incrementBy; + private readonly long? _minValue; + private readonly long? _maxValue; + private readonly bool _isCyclic; + + /// + /// 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. + /// + [EntityFrameworkInternal] + public SlimSequence( + [NotNull] string name, + [CanBeNull] string? schema, + [NotNull] SlimModel model, + [NotNull] Type type, + long startValue, + int incrementBy, + bool cyclic, + long? minValue = null, + long? maxValue = null) + { + Model = model; + Name = name; + _schema = schema; + _type = type; + _startValue = startValue; + _incrementBy = incrementBy; + _isCyclic = cyclic; + _minValue = minValue; + _maxValue = maxValue; + } + + /// + /// Gets the model in which this sequence is defined. + /// + public virtual SlimModel Model { get; } + + /// + /// Gets the name of the sequence in the database. + /// + public virtual string Name { get; } + + /// + /// Gets the database schema that contains the sequence. + /// + public virtual string? Schema + => _schema; + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + public override string ToString() + => ((ISequence)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + /// + /// 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. + /// + [EntityFrameworkInternal] + public virtual DebugView DebugView + => new( + () => ((ISequence)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((ISequence)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + + /// + IReadOnlyModel IReadOnlySequence.Model + { + [DebuggerStepThrough] + get => Model; + } + + /// + IModel ISequence.Model + { + [DebuggerStepThrough] + get => Model; + } + + /// + long IReadOnlySequence.StartValue + { + [DebuggerStepThrough] + get => _startValue; + } + + /// + int IReadOnlySequence.IncrementBy + { + [DebuggerStepThrough] + get => _incrementBy; + } + + /// + long? IReadOnlySequence.MinValue + { + [DebuggerStepThrough] + get => _minValue; + } + + /// + long? IReadOnlySequence.MaxValue + { + [DebuggerStepThrough] + get => _maxValue; + } + + /// + Type IReadOnlySequence.Type + { + [DebuggerStepThrough] + get => _type; + } + + /// + Type IReadOnlySequence.ClrType + { + [DebuggerStepThrough] + get => _type; + } + + /// + bool IReadOnlySequence.IsCyclic + { + [DebuggerStepThrough] + get => _isCyclic; + } + } +} diff --git a/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs b/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs index 3ff4a127684..610659ce690 100644 --- a/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs +++ b/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs @@ -1,12 +1,10 @@ // 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 System.Text; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Infrastructure diff --git a/src/EFCore/Metadata/Builders/DiscriminatorBuilder.cs b/src/EFCore/Metadata/Builders/DiscriminatorBuilder.cs index 97eac79f7f5..2b5eabb67eb 100644 --- a/src/EFCore/Metadata/Builders/DiscriminatorBuilder.cs +++ b/src/EFCore/Metadata/Builders/DiscriminatorBuilder.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; +using System.Diagnostics; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -138,7 +139,8 @@ public DiscriminatorBuilder([NotNull] IMutableEntityType entityType) } else { - if (!entityTypeBuilder.CanSetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, configurationSource)) + if (!((IConventionDiscriminatorBuilder)this).CanSetValue( + entityTypeBuilder.Metadata, value, configurationSource == ConfigurationSource.DataAnnotation)) { return null; } @@ -151,21 +153,32 @@ public DiscriminatorBuilder([NotNull] IMutableEntityType entityType) } /// + IConventionEntityType IConventionDiscriminatorBuilder.EntityType + { + [DebuggerStepThrough] + get => EntityTypeBuilder.Metadata; + } + + /// + [DebuggerStepThrough] IConventionDiscriminatorBuilder? IConventionDiscriminatorBuilder.IsComplete(bool complete, bool fromDataAnnotation) => IsComplete(complete, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// + [DebuggerStepThrough] bool IConventionDiscriminatorBuilder.CanSetIsComplete(bool complete, bool fromDataAnnotation) => ((IConventionEntityTypeBuilder)EntityTypeBuilder).CanSetAnnotation( CoreAnnotationNames.DiscriminatorMappingComplete, fromDataAnnotation); /// + [DebuggerStepThrough] IConventionDiscriminatorBuilder? IConventionDiscriminatorBuilder.HasValue(object? value, bool fromDataAnnotation) => HasValue( EntityTypeBuilder, value, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// + [DebuggerStepThrough] IConventionDiscriminatorBuilder? IConventionDiscriminatorBuilder.HasValue( IConventionEntityType entityType, object? value, @@ -178,19 +191,6 @@ bool IConventionDiscriminatorBuilder.CanSetIsComplete(bool complete, bool fromDa bool IConventionDiscriminatorBuilder.CanSetValue(object? value, bool fromDataAnnotation) => ((IConventionDiscriminatorBuilder)this).CanSetValue(EntityTypeBuilder.Metadata, value, fromDataAnnotation); - /// - bool IConventionDiscriminatorBuilder.CanSetValue(IConventionEntityType entityType, object? value, bool fromDataAnnotation) - { - IConventionEntityType baseEntityType = EntityTypeBuilder.Metadata; - if (!baseEntityType.IsAssignableFrom(entityType) - && !entityType.Builder.CanSetBaseType(baseEntityType, fromDataAnnotation)) - { - return false; - } - - return entityType.Builder.CanSetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation); - } - #region Hidden System.Object members /// diff --git a/src/EFCore/Metadata/Builders/IConventionDiscriminatorBuilder.cs b/src/EFCore/Metadata/Builders/IConventionDiscriminatorBuilder.cs index e7f0ae47fe3..ed9aa44eb8e 100644 --- a/src/EFCore/Metadata/Builders/IConventionDiscriminatorBuilder.cs +++ b/src/EFCore/Metadata/Builders/IConventionDiscriminatorBuilder.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Builders { @@ -10,6 +11,11 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders /// public interface IConventionDiscriminatorBuilder { + /// + /// Gets the entity type on which the discriminator is being configured. + /// + IConventionEntityType EntityType { get; } + /// /// Configures if the discriminator mapping is complete. /// @@ -61,6 +67,15 @@ public interface IConventionDiscriminatorBuilder /// The discriminator value. /// Indicates whether the configuration was specified using a data annotation. /// if the discriminator value can be set from this configuration source. - bool CanSetValue([NotNull] IConventionEntityType entityType, [CanBeNull] object? value, bool fromDataAnnotation = false); + bool CanSetValue([NotNull] IConventionEntityType entityType, [CanBeNull] object? value, bool fromDataAnnotation = false) + { + if (!EntityType.IsAssignableFrom(entityType) + && !entityType.Builder.CanSetBaseType(EntityType, fromDataAnnotation)) + { + return false; + } + + return entityType.Builder.CanSetAnnotation(CoreAnnotationNames.DiscriminatorValue, value, fromDataAnnotation); + } } } diff --git a/src/EFCore/Metadata/IConventionEntityType.cs b/src/EFCore/Metadata/IConventionEntityType.cs index eba84f7fa53..b68062c61f2 100644 --- a/src/EFCore/Metadata/IConventionEntityType.cs +++ b/src/EFCore/Metadata/IConventionEntityType.cs @@ -625,12 +625,12 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) => ((IReadOnlyEntityType)this).GetNavigations().Cast(); /// - /// Adds a new skip navigation properties to this entity type. + /// Adds a new skip navigation property to this entity type. /// /// The name of the skip navigation property to add. /// /// - /// The corresponding CLR type member or for a shadow property. + /// The corresponding CLR type member or for a shadow navigation. /// /// /// An indexer with a parameter and return type can be used. @@ -959,7 +959,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// Finds matching properties on the given entity type. Returns if any property is not found. /// /// - /// This API only finds scalar properties and does not find navigation properties. + /// This API only finds scalar properties and does not find navigation or service properties. /// /// /// The property names. @@ -1033,23 +1033,23 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) IConventionProperty? RemoveProperty([NotNull] IReadOnlyProperty property); /// - /// Adds a to this entity type. + /// Adds a service property to this entity type. /// /// The or of the property to add. /// Indicates whether the configuration was specified using a data annotation. - /// The newly created property. + /// The newly created service property. IConventionServiceProperty AddServiceProperty([NotNull] MemberInfo memberInfo, bool fromDataAnnotation = false); /// /// - /// Gets the with a given name. + /// Gets the service property with a given name. /// Returns if no property with the given name is defined. /// /// /// This API only finds service properties and does not find scalar or navigation properties. /// /// - /// The name of the property. + /// The name of the service property. /// The service property, or if none is found. new IConventionServiceProperty? FindServiceProperty([NotNull] string name); diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs index d86d0efa56b..be01e08e722 100644 --- a/src/EFCore/Metadata/IEntityType.cs +++ b/src/EFCore/Metadata/IEntityType.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Metadata @@ -532,14 +531,14 @@ public interface IEntityType : IReadOnlyEntityType, ITypeBase /// /// - /// Gets the with a given name. + /// Gets the service property with a given name. /// Returns if no property with the given name is defined. /// /// /// This API only finds service properties and does not find scalar or navigation properties. /// /// - /// The name of the property. + /// The name of the service property. /// The service property, or if none is found. new IServiceProperty? FindServiceProperty([NotNull] string name); diff --git a/src/EFCore/Metadata/IMutableEntityType.cs b/src/EFCore/Metadata/IMutableEntityType.cs index 327fb2bd74e..691d152df51 100644 --- a/src/EFCore/Metadata/IMutableEntityType.cs +++ b/src/EFCore/Metadata/IMutableEntityType.cs @@ -506,12 +506,12 @@ IMutableForeignKey AddForeignKey( => ((IReadOnlyEntityType)this).GetNavigations().Cast(); /// - /// Adds a new skip navigation properties to this entity type. + /// Adds a new skip navigation property to this entity type. /// /// The name of the skip navigation property to add. /// /// - /// The corresponding CLR type member or for a shadow property. + /// The corresponding CLR type member or for a shadow navigation. /// /// /// An indexer with a parameter and return type can be used. @@ -746,7 +746,7 @@ IMutableIndex AddIndex([NotNull] IMutableProperty property, [NotNull] string nam /// Finds matching properties on the given entity type. Returns if any property is not found. /// /// - /// This API only finds scalar properties and does not find navigation properties. + /// This API only finds scalar properties and does not find navigation or service properties. /// /// /// The property names. @@ -876,22 +876,22 @@ IMutableProperty AddIndexerProperty( IMutableProperty? RemoveProperty([NotNull] IReadOnlyProperty property); /// - /// Adds a to this entity type. + /// Adds a service property to this entity type. /// /// The or of the property to add. - /// The newly created property. + /// The newly created service property. IMutableServiceProperty AddServiceProperty([NotNull] MemberInfo memberInfo); /// /// - /// Gets the with a given name. + /// Gets the service property with a given name. /// Returns if no property with the given name is defined. /// /// /// This API only finds service properties and does not find scalar or navigation properties. /// /// - /// The name of the property. + /// The name of the service property. /// The service property, or if none is found. new IMutableServiceProperty? FindServiceProperty([NotNull] string name); diff --git a/src/EFCore/Metadata/IPropertyBase.cs b/src/EFCore/Metadata/IPropertyBase.cs index f5000be7093..5cda61cd0bb 100644 --- a/src/EFCore/Metadata/IPropertyBase.cs +++ b/src/EFCore/Metadata/IPropertyBase.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Base type for navigations and properties. + /// Base interface for navigations and properties. /// public interface IPropertyBase : IReadOnlyPropertyBase, IAnnotatable { diff --git a/src/EFCore/Metadata/IReadOnlyEntityType.cs b/src/EFCore/Metadata/IReadOnlyEntityType.cs index 27f33ee5d46..cce54d011cc 100644 --- a/src/EFCore/Metadata/IReadOnlyEntityType.cs +++ b/src/EFCore/Metadata/IReadOnlyEntityType.cs @@ -23,7 +23,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface IReadOnlyEntityType : IReadOnlyTypeBase { /// - /// Gets the base type of this entity type. Returns if this is not a derived type in an inheritance hierarchy. + /// Gets the base type of this entity type. Returns if this is not a + /// derived type in an inheritance hierarchy. /// IReadOnlyEntityType? BaseType { get; } @@ -82,15 +83,7 @@ public interface IReadOnlyEntityType : IReadOnlyTypeBase /// Returns the name of the property that will be used for storing a discriminator value. /// /// The name of the property that will be used for storing a discriminator value. - string? GetDiscriminatorPropertyName() - { - if (BaseType != null) - { - return GetRootType().GetDiscriminatorPropertyName(); - } - - return (string?)this[CoreAnnotationNames.DiscriminatorProperty]; - } + string? GetDiscriminatorPropertyName(); /// /// Returns the value indicating whether the discriminator mapping is complete for this entity type. @@ -748,14 +741,14 @@ IReadOnlyProperty GetProperty([NotNull] string name) /// /// - /// Gets the with a given name. + /// Gets the service property with a given name. /// Returns if no property with the given name is defined. /// /// /// This API only finds service properties and does not find scalar or navigation properties. /// /// - /// The name of the property. + /// The name of the service property. /// The service property, or if none is found. IReadOnlyServiceProperty? FindServiceProperty([NotNull] string name); diff --git a/src/EFCore/Metadata/IReadOnlyForeignKey.cs b/src/EFCore/Metadata/IReadOnlyForeignKey.cs index 43cb6ec8543..4759bdc8d72 100644 --- a/src/EFCore/Metadata/IReadOnlyForeignKey.cs +++ b/src/EFCore/Metadata/IReadOnlyForeignKey.cs @@ -69,7 +69,7 @@ public interface IReadOnlyForeignKey : IReadOnlyAnnotatable bool IsRequiredDependent { get; } /// - /// Gets or sets a value indicating whether this relationship defines an ownership. + /// Gets a value indicating whether this relationship defines an ownership. /// If , the dependent entity must always be accessed via the navigation from the principal entity. /// bool IsOwnership { get; } diff --git a/src/EFCore/Metadata/IReadOnlyNavigation.cs b/src/EFCore/Metadata/IReadOnlyNavigation.cs index 5d32e0aa2a4..23913f1d031 100644 --- a/src/EFCore/Metadata/IReadOnlyNavigation.cs +++ b/src/EFCore/Metadata/IReadOnlyNavigation.cs @@ -163,7 +163,7 @@ string ToDebugString(MetadataDebugStringOptions options, int indent = 0) } if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 - && ((Annotatable)this).IsReadOnly) + && ((AnnotatableBase)this).IsReadOnly) { var indexes = ((INavigation)this).GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); diff --git a/src/EFCore/Metadata/IReadOnlyProperty.cs b/src/EFCore/Metadata/IReadOnlyProperty.cs index 9313b7fcfd7..89bf010d2cf 100644 --- a/src/EFCore/Metadata/IReadOnlyProperty.cs +++ b/src/EFCore/Metadata/IReadOnlyProperty.cs @@ -387,7 +387,7 @@ string ToDebugString(MetadataDebugStringOptions options, int indent = 0) } if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 - && ((Annotatable)this).IsReadOnly) + && ((AnnotatableBase)this).IsReadOnly) { var indexes = ((IProperty)this).GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); diff --git a/src/EFCore/Metadata/IReadOnlyPropertyBase.cs b/src/EFCore/Metadata/IReadOnlyPropertyBase.cs index 4a49432a2d7..c55a1378392 100644 --- a/src/EFCore/Metadata/IReadOnlyPropertyBase.cs +++ b/src/EFCore/Metadata/IReadOnlyPropertyBase.cs @@ -71,14 +71,9 @@ bool IsIndexerProperty() && propertyInfo == DeclaringType.FindIndexerPropertyInfo(); /// - /// - /// Gets the being used for this property. - /// indicates that the default property access mode is being used. - /// + /// Gets the being used for this property-like object. /// /// The access mode being used. - PropertyAccessMode GetPropertyAccessMode() - => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] - ?? PropertyAccessMode.PreferField); + PropertyAccessMode GetPropertyAccessMode(); } } diff --git a/src/EFCore/Metadata/IReadOnlySkipNavigation.cs b/src/EFCore/Metadata/IReadOnlySkipNavigation.cs index 9911d789067..4721b39559d 100644 --- a/src/EFCore/Metadata/IReadOnlySkipNavigation.cs +++ b/src/EFCore/Metadata/IReadOnlySkipNavigation.cs @@ -106,7 +106,7 @@ string ToDebugString(MetadataDebugStringOptions options, int indent = 0) } if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 - && ((Annotatable)this).IsReadOnly) + && ((AnnotatableBase)this).IsReadOnly) { var indexes = ((ISkipNavigation)this).GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs index c4e742b4af0..bb6b0aa70b5 100644 --- a/src/EFCore/Metadata/IReadOnlyTypeBase.cs +++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs @@ -37,17 +37,18 @@ public interface IReadOnlyTypeBase : IReadOnlyAnnotatable Type ClrType { get; } /// - /// Gets whether this entity type can share its ClrType with other entities. + /// Gets a value indicating whether this entity type can share its with other entities. /// bool HasSharedClrType { get; } /// - /// Gets whether this entity type has an indexer which is able to contain arbitrary properties. + /// Gets a value indicating whether this entity type has an indexer which is able to contain arbitrary properties + /// and a method that can be used to determine whether a given indexer property contains a value. /// bool IsPropertyBag { get; } /// - /// Returns a value indicating whether this entity type represents an abstract type. + /// Gets a value indicating whether this entity type represents an abstract type. /// /// if the type is abstract, otherwise. [DebuggerStepThrough] diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index 357a0ee79e2..4843660a253 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -59,8 +59,6 @@ private readonly SortedDictionary _serviceProperties private EntityType? _baseType; private ChangeTrackingStrategy? _changeTrackingStrategy; private InternalEntityTypeBuilder? _builder; - private InstantiationBinding? _constructorBinding; - private InstantiationBinding? _serviceOnlyConstructorBinding; private ConfigurationSource? _primaryKeyConfigurationSource; private ConfigurationSource? _isKeylessConfigurationSource; @@ -72,6 +70,10 @@ private readonly SortedDictionary _serviceProperties // Warning: Never access these fields directly as access needs to be thread-safe private PropertyCounts? _counts; + // _serviceOnlyConstructorBinding needs to be set as well whenever _constructorBinding is set + private InstantiationBinding? _constructorBinding; + private InstantiationBinding? _serviceOnlyConstructorBinding; + private Func? _relationshipSnapshotFactory; private Func? _originalValuesFactory; private Func? _temporaryValuesFactory; @@ -639,16 +641,7 @@ public virtual IEnumerable FindMembersInHierarchy([NotNull] string /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual Key? FindPrimaryKey() - => _baseType?.FindPrimaryKey() ?? FindDeclaredPrimaryKey(); - - /// - /// 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 Key? FindDeclaredPrimaryKey() - => _primaryKey; + => _baseType?.FindPrimaryKey() ?? _primaryKey; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1376,7 +1369,7 @@ public virtual IEnumerable FindForeignKeysInHierarchy( /// public virtual IEnumerable GetReferencingForeignKeys() => _baseType != null - ? DeclaredReferencingForeignKeys?.Count == 0 + ? (DeclaredReferencingForeignKeys?.Count ?? 0) == 0 ? _baseType.GetReferencingForeignKeys() : _baseType.GetReferencingForeignKeys().Concat(GetDeclaredReferencingForeignKeys()) : GetDeclaredReferencingForeignKeys(); @@ -1390,17 +1383,6 @@ public virtual IEnumerable GetReferencingForeignKeys() public virtual IEnumerable GetDeclaredReferencingForeignKeys() => DeclaredReferencingForeignKeys ?? Enumerable.Empty(); - /// - /// 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 IEnumerable GetDerivedReferencingForeignKeys() - => _directlyDerivedTypes.Count == 0 - ? Enumerable.Empty() - : GetDerivedTypes().SelectMany(et => et.GetDeclaredReferencingForeignKeys()); - private SortedSet? DeclaredReferencingForeignKeys { get; set; } #endregion @@ -1889,7 +1871,7 @@ public virtual IEnumerable GetSkipNavigations() /// public virtual IEnumerable GetReferencingSkipNavigations() => _baseType != null - ? DeclaredReferencingSkipNavigations?.Count == 0 + ? (DeclaredReferencingSkipNavigations?.Count ?? 0) == 0 ? _baseType.GetReferencingSkipNavigations() : _baseType.GetReferencingSkipNavigations().Concat(GetDeclaredReferencingSkipNavigations()) : GetDeclaredReferencingSkipNavigations(); @@ -3323,6 +3305,20 @@ private void CheckDiscriminatorProperty(Property? property) } } + /// + /// Returns the name of the property that will be used for storing a discriminator value. + /// + /// The name of the property that will be used for storing a discriminator value. + public virtual string? GetDiscriminatorPropertyName() + { + if (BaseType != null) + { + return ((IReadOnlyEntityType)this).GetRootType().GetDiscriminatorPropertyName(); + } + + return (string?)this[CoreAnnotationNames.DiscriminatorProperty]; + } + /// /// 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/ForeignKey.cs b/src/EFCore/Metadata/Internal/ForeignKey.cs index ac6bd4c12e1..22a6a9e5d7f 100644 --- a/src/EFCore/Metadata/Internal/ForeignKey.cs +++ b/src/EFCore/Metadata/Internal/ForeignKey.cs @@ -379,7 +379,7 @@ public virtual void UpdateDependentToPrincipalConfigurationSource(ConfigurationS /// 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 Navigation? HasPrincipalToDependent( + public virtual Navigation? SetPrincipalToDependent( [CanBeNull] string? name, ConfigurationSource configurationSource) => Navigation(MemberIdentity.Create(name), configurationSource, pointsToPrincipal: false); @@ -390,7 +390,7 @@ public virtual void UpdateDependentToPrincipalConfigurationSource(ConfigurationS /// 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 Navigation? HasPrincipalToDependent( + public virtual Navigation? SetPrincipalToDependent( [CanBeNull] MemberInfo? property, ConfigurationSource configurationSource) => Navigation(MemberIdentity.Create(property), configurationSource, pointsToPrincipal: false); @@ -501,9 +501,10 @@ public virtual void UpdatePrincipalToDependentConfigurationSource(ConfigurationS { Check.DebugAssert(oldNavigation.Name != null, "oldNavigation.Name is null"); + string? removedNavigationName = null; if (pointsToPrincipal) { - DeclaringEntityType.Model.ConventionDispatcher.OnNavigationRemoved( + removedNavigationName = DeclaringEntityType.Model.ConventionDispatcher.OnNavigationRemoved( DeclaringEntityType.Builder, PrincipalEntityType.Builder, oldNavigation.Name, @@ -511,12 +512,17 @@ public virtual void UpdatePrincipalToDependentConfigurationSource(ConfigurationS } else { - DeclaringEntityType.Model.ConventionDispatcher.OnNavigationRemoved( + removedNavigationName = DeclaringEntityType.Model.ConventionDispatcher.OnNavigationRemoved( PrincipalEntityType.Builder, DeclaringEntityType.Builder, oldNavigation.Name, oldNavigation.GetIdentifyingMemberInfo()); } + + if (navigation == null) + { + return oldNavigation.Name == removedNavigationName ? oldNavigation : null; + } } if (navigation != null) @@ -524,7 +530,7 @@ public virtual void UpdatePrincipalToDependentConfigurationSource(ConfigurationS navigation = (Navigation?)DeclaringEntityType.Model.ConventionDispatcher.OnNavigationAdded(navigation.Builder)?.Metadata; } - return navigation ?? oldNavigation; + return navigation; } /// @@ -589,8 +595,7 @@ public virtual bool IsUnique : oldUnique; } - private static bool DefaultIsUnique - => false; + private const bool DefaultIsUnique = false; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -699,8 +704,7 @@ public virtual bool IsRequiredDependent : oldRequired; } - private bool DefaultIsRequiredDependent - => false; + private const bool DefaultIsRequiredDependent = false; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -824,8 +828,7 @@ public virtual bool IsOwnership : oldIsOwnership; } - private static bool DefaultIsOwnership - => false; + private const bool DefaultIsOwnership = false; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -927,6 +930,199 @@ public virtual Func DependentsMapFactory } } + /// + /// 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 DebugView DebugView + => new( + () => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + + /// + /// 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 override string ToString() + => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + private void Validate( + IReadOnlyList properties, + Key principalKey, + EntityType declaringEntityType, + EntityType principalEntityType) + { + for (var i = 0; i < properties.Count; i++) + { + var property = properties[i]; + for (var j = i + 1; j < properties.Count; j++) + { + if (property == properties[j]) + { + throw new InvalidOperationException(CoreStrings.DuplicatePropertyInForeignKey(properties.Format(), property.Name)); + } + } + + var actualProperty = declaringEntityType.FindProperty(property.Name); + if (actualProperty?.DeclaringEntityType.IsAssignableFrom(property.DeclaringEntityType) != true + || !property.IsInModel) + { + throw new InvalidOperationException( + CoreStrings.ForeignKeyPropertiesWrongEntity( + properties.Format(), declaringEntityType.DisplayName())); + } + } + + AreCompatible( + principalEntityType, + dependentEntityType: declaringEntityType, + navigationToPrincipal: null, + navigationToDependent: null, + dependentProperties: properties, + principalProperties: principalKey.Properties, + unique: null, + shouldThrow: true); + + var duplicateForeignKey = declaringEntityType.FindForeignKeysInHierarchy( + properties, principalKey, principalEntityType).FirstOrDefault(); + if (duplicateForeignKey != null) + { + throw new InvalidOperationException( + CoreStrings.DuplicateForeignKey( + properties.Format(), + declaringEntityType.DisplayName(), + duplicateForeignKey.DeclaringEntityType.DisplayName(), + principalKey.Properties.Format(), + principalEntityType.DisplayName())); + } + + if (principalEntityType.Model != declaringEntityType.Model) + { + throw new InvalidOperationException( + CoreStrings.EntityTypeModelMismatch( + declaringEntityType.DisplayName(), principalEntityType.DisplayName())); + } + } + + /// + /// 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 AreCompatible( + [NotNull] EntityType principalEntityType, + [NotNull] EntityType dependentEntityType, + [CanBeNull] MemberInfo? navigationToPrincipal, + [CanBeNull] MemberInfo? navigationToDependent, + [CanBeNull] IReadOnlyList? dependentProperties, + [CanBeNull] IReadOnlyList? principalProperties, + bool? unique, + bool shouldThrow) + { + Check.NotNull(principalEntityType, nameof(principalEntityType)); + Check.NotNull(dependentEntityType, nameof(dependentEntityType)); + + if (navigationToPrincipal != null + && !Internal.Navigation.IsCompatible( + navigationToPrincipal.Name, + navigationToPrincipal, + dependentEntityType, + principalEntityType, + shouldBeCollection: false, + shouldThrow: shouldThrow)) + { + return false; + } + + if (navigationToDependent != null + && !Internal.Navigation.IsCompatible( + navigationToDependent.Name, + navigationToDependent, + principalEntityType, + dependentEntityType, + shouldBeCollection: !unique, + shouldThrow: shouldThrow)) + { + return false; + } + + return principalProperties == null + || dependentProperties == null + || AreCompatible( + principalProperties, + dependentProperties, + principalEntityType, + dependentEntityType, + shouldThrow); + } + + /// + /// 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 AreCompatible( + [NotNull] IReadOnlyList principalProperties, + [NotNull] IReadOnlyList dependentProperties, + [NotNull] IReadOnlyEntityType principalEntityType, + [NotNull] IReadOnlyEntityType dependentEntityType, + bool shouldThrow) + { + Check.NotNull(principalProperties, nameof(principalProperties)); + Check.NotNull(dependentProperties, nameof(dependentProperties)); + Check.NotNull(principalEntityType, nameof(principalEntityType)); + Check.NotNull(dependentEntityType, nameof(dependentEntityType)); + + if (!ArePropertyCountsEqual(principalProperties, dependentProperties)) + { + if (shouldThrow) + { + throw new InvalidOperationException( + CoreStrings.ForeignKeyCountMismatch( + dependentProperties.Format(), + dependentEntityType.DisplayName(), + principalProperties.Format(), + principalEntityType.DisplayName())); + } + + return false; + } + + if (!ArePropertyTypesCompatible(principalProperties, dependentProperties)) + { + if (shouldThrow) + { + throw new InvalidOperationException( + CoreStrings.ForeignKeyTypeMismatch( + dependentProperties.Format(includeTypes: true), + dependentEntityType.DisplayName(), + principalProperties.Format(includeTypes: true), + principalEntityType.DisplayName())); + } + + return false; + } + + return true; + } + + private static bool ArePropertyCountsEqual( + IReadOnlyList principalProperties, + IReadOnlyList dependentProperties) + => principalProperties.Count == dependentProperties.Count; + + private static bool ArePropertyTypesCompatible( + IReadOnlyList principalProperties, + IReadOnlyList dependentProperties) + => principalProperties.Select(p => p.ClrType.UnwrapNullableType()).SequenceEqual( + dependentProperties.Select(p => p.ClrType.UnwrapNullableType())); + /// /// 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 @@ -1111,7 +1307,7 @@ void IMutableForeignKey.SetProperties(IReadOnlyList properties /// [DebuggerStepThrough] IMutableNavigation? IMutableForeignKey.SetPrincipalToDependent(string name) - => HasPrincipalToDependent(name, ConfigurationSource.Explicit); + => SetPrincipalToDependent(name, ConfigurationSource.Explicit); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1121,7 +1317,7 @@ void IMutableForeignKey.SetProperties(IReadOnlyList properties /// [DebuggerStepThrough] IMutableNavigation? IMutableForeignKey.SetPrincipalToDependent(MemberInfo property) - => HasPrincipalToDependent(property, ConfigurationSource.Explicit); + => SetPrincipalToDependent(property, ConfigurationSource.Explicit); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1314,12 +1510,12 @@ IReadOnlyList IConventionForeignKey.SetProperties( /// [DebuggerStepThrough] IConventionNavigation? IConventionForeignKey.SetPrincipalToDependent(string? name, bool fromDataAnnotation) - => HasPrincipalToDependent(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + => SetPrincipalToDependent(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// [DebuggerStepThrough] IConventionNavigation? IConventionForeignKey.SetPrincipalToDependent(MemberInfo? property, bool fromDataAnnotation) - => HasPrincipalToDependent(property, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + => SetPrincipalToDependent(property, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// [DebuggerStepThrough] @@ -1355,198 +1551,5 @@ IEnumerable IReadOnlyForeignKey.GetReferencingSkipNavig [DebuggerStepThrough] IDependentKeyValueFactory? IForeignKey.GetDependentKeyValueFactory() => (IDependentKeyValueFactory?)DependentKeyValueFactory; - - /// - /// 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 DebugView DebugView - => new( - () => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), - () => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); - - /// - /// 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 override string ToString() - => ((IReadOnlyForeignKey)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); - - private void Validate( - IReadOnlyList properties, - Key principalKey, - EntityType declaringEntityType, - EntityType principalEntityType) - { - for (var i = 0; i < properties.Count; i++) - { - var property = properties[i]; - for (var j = i + 1; j < properties.Count; j++) - { - if (property == properties[j]) - { - throw new InvalidOperationException(CoreStrings.DuplicatePropertyInForeignKey(properties.Format(), property.Name)); - } - } - - var actualProperty = declaringEntityType.FindProperty(property.Name); - if (actualProperty?.DeclaringEntityType.IsAssignableFrom(property.DeclaringEntityType) != true - || !property.IsInModel) - { - throw new InvalidOperationException( - CoreStrings.ForeignKeyPropertiesWrongEntity( - properties.Format(), declaringEntityType.DisplayName())); - } - } - - AreCompatible( - principalEntityType, - dependentEntityType: declaringEntityType, - navigationToPrincipal: null, - navigationToDependent: null, - dependentProperties: properties, - principalProperties: principalKey.Properties, - unique: null, - shouldThrow: true); - - var duplicateForeignKey = declaringEntityType.FindForeignKeysInHierarchy( - properties, principalKey, principalEntityType).FirstOrDefault(); - if (duplicateForeignKey != null) - { - throw new InvalidOperationException( - CoreStrings.DuplicateForeignKey( - properties.Format(), - declaringEntityType.DisplayName(), - duplicateForeignKey.DeclaringEntityType.DisplayName(), - principalKey.Properties.Format(), - principalEntityType.DisplayName())); - } - - if (principalEntityType.Model != declaringEntityType.Model) - { - throw new InvalidOperationException( - CoreStrings.EntityTypeModelMismatch( - declaringEntityType.DisplayName(), principalEntityType.DisplayName())); - } - } - - /// - /// 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 AreCompatible( - [NotNull] EntityType principalEntityType, - [NotNull] EntityType dependentEntityType, - [CanBeNull] MemberInfo? navigationToPrincipal, - [CanBeNull] MemberInfo? navigationToDependent, - [CanBeNull] IReadOnlyList? dependentProperties, - [CanBeNull] IReadOnlyList? principalProperties, - bool? unique, - bool shouldThrow) - { - Check.NotNull(principalEntityType, nameof(principalEntityType)); - Check.NotNull(dependentEntityType, nameof(dependentEntityType)); - - if (navigationToPrincipal != null - && !Internal.Navigation.IsCompatible( - navigationToPrincipal.Name, - navigationToPrincipal, - dependentEntityType, - principalEntityType, - shouldBeCollection: false, - shouldThrow: shouldThrow)) - { - return false; - } - - if (navigationToDependent != null - && !Internal.Navigation.IsCompatible( - navigationToDependent.Name, - navigationToDependent, - principalEntityType, - dependentEntityType, - shouldBeCollection: !unique, - shouldThrow: shouldThrow)) - { - return false; - } - - return principalProperties == null - || dependentProperties == null - || AreCompatible( - principalProperties, - dependentProperties, - principalEntityType, - dependentEntityType, - shouldThrow); - } - - /// - /// 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 AreCompatible( - [NotNull] IReadOnlyList principalProperties, - [NotNull] IReadOnlyList dependentProperties, - [NotNull] IReadOnlyEntityType principalEntityType, - [NotNull] IReadOnlyEntityType dependentEntityType, - bool shouldThrow) - { - Check.NotNull(principalProperties, nameof(principalProperties)); - Check.NotNull(dependentProperties, nameof(dependentProperties)); - Check.NotNull(principalEntityType, nameof(principalEntityType)); - Check.NotNull(dependentEntityType, nameof(dependentEntityType)); - - if (!ArePropertyCountsEqual(principalProperties, dependentProperties)) - { - if (shouldThrow) - { - throw new InvalidOperationException( - CoreStrings.ForeignKeyCountMismatch( - dependentProperties.Format(), - dependentEntityType.DisplayName(), - principalProperties.Format(), - principalEntityType.DisplayName())); - } - - return false; - } - - if (!ArePropertyTypesCompatible(principalProperties, dependentProperties)) - { - if (shouldThrow) - { - throw new InvalidOperationException( - CoreStrings.ForeignKeyTypeMismatch( - dependentProperties.Format(includeTypes: true), - dependentEntityType.DisplayName(), - principalProperties.Format(includeTypes: true), - principalEntityType.DisplayName())); - } - - return false; - } - - return true; - } - - private static bool ArePropertyCountsEqual( - IReadOnlyList principalProperties, - IReadOnlyList dependentProperties) - => principalProperties.Count == dependentProperties.Count; - - private static bool ArePropertyTypesCompatible( - IReadOnlyList principalProperties, - IReadOnlyList dependentProperties) - => principalProperties.Select(p => p.ClrType.UnwrapNullableType()).SequenceEqual( - dependentProperties.Select(p => p.ClrType.UnwrapNullableType())); } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index 8dc7c30bfc9..d528bb6f271 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -394,7 +394,7 @@ private static (InternalKeyBuilder, ConfigurationSource?) DetachKey(Key keyToDet foreach (var foreignKey in Metadata.GetForeignKeys()) { - foreignKey.HasPrincipalToDependent((string?)null, configurationSource); + foreignKey.SetPrincipalToDependent((string?)null, configurationSource); } foreach (var key in Metadata.GetKeys().ToList()) diff --git a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs index 2ea9985c929..5fd71ed3cde 100644 --- a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs @@ -397,7 +397,7 @@ public InternalForeignKeyBuilder( { if (navigationToDependent != null) { - Metadata.HasPrincipalToDependent((string?)null, configurationSource); + Metadata.SetPrincipalToDependent((string?)null, configurationSource); } var navigationProperty = navigationToPrincipal.Value.MemberInfo; @@ -440,11 +440,11 @@ public InternalForeignKeyBuilder( if (navigationProperty != null) { - Metadata.HasPrincipalToDependent(navigationProperty, configurationSource); + Metadata.SetPrincipalToDependent(navigationProperty, configurationSource); } else { - Metadata.HasPrincipalToDependent(navigationToDependentName, configurationSource); + Metadata.SetPrincipalToDependent(navigationToDependentName, configurationSource); } } diff --git a/src/EFCore/Metadata/Internal/Key.cs b/src/EFCore/Metadata/Internal/Key.cs index 0cea31df4a3..789280d53b3 100644 --- a/src/EFCore/Metadata/Internal/Key.cs +++ b/src/EFCore/Metadata/Internal/Key.cs @@ -248,9 +248,10 @@ IReadOnlyList IMutableKey.Properties /// 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. /// - IMutableEntityType IMutableKey.DeclaringEntityType + IReadOnlyList IConventionKey.Properties { - [DebuggerStepThrough] get => DeclaringEntityType; + [DebuggerStepThrough] + get => Properties; } /// @@ -259,9 +260,10 @@ IMutableEntityType IMutableKey.DeclaringEntityType /// 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. /// - IConventionKeyBuilder IConventionKey.Builder + IReadOnlyList IKey.Properties { - [DebuggerStepThrough] get => Builder; + [DebuggerStepThrough] + get => Properties; } /// @@ -270,10 +272,9 @@ IConventionKeyBuilder IConventionKey.Builder /// 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. /// - IConventionAnnotatableBuilder IConventionAnnotatable.Builder + IMutableEntityType IMutableKey.DeclaringEntityType { - [DebuggerStepThrough] - get => Builder; + [DebuggerStepThrough] get => DeclaringEntityType; } /// @@ -282,9 +283,10 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder /// 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. /// - IReadOnlyList IConventionKey.Properties + IConventionEntityType IConventionKey.DeclaringEntityType { - [DebuggerStepThrough] get => Properties; + [DebuggerStepThrough] + get => DeclaringEntityType; } /// @@ -293,10 +295,10 @@ IReadOnlyList IConventionKey.Properties /// 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. /// - IReadOnlyList IKey.Properties + IEntityType IKey.DeclaringEntityType { [DebuggerStepThrough] - get => Properties; + get => DeclaringEntityType; } /// @@ -305,9 +307,9 @@ IReadOnlyList IKey.Properties /// 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. /// - IConventionEntityType IConventionKey.DeclaringEntityType + IConventionKeyBuilder IConventionKey.Builder { - [DebuggerStepThrough] get => DeclaringEntityType; + [DebuggerStepThrough] get => Builder; } /// @@ -316,10 +318,10 @@ IConventionEntityType IConventionKey.DeclaringEntityType /// 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. /// - IEntityType IKey.DeclaringEntityType + IConventionAnnotatableBuilder IConventionAnnotatable.Builder { [DebuggerStepThrough] - get => DeclaringEntityType; + get => Builder; } /// diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs index 576e1b7673b..4f7e9305d62 100644 --- a/src/EFCore/Metadata/Internal/Navigation.cs +++ b/src/EFCore/Metadata/Internal/Navigation.cs @@ -43,6 +43,7 @@ public Navigation( Check.NotNull(foreignKey, nameof(foreignKey)); ForeignKey = foreignKey; + ClrType = this.GetIdentifyingMemberInfo()?.GetMemberType() ?? typeof(object); _builder = new InternalNavigationBuilder(this, foreignKey.DeclaringEntityType.Model.Builder); } @@ -53,8 +54,7 @@ public Navigation( /// 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 Type ClrType - => this.GetIdentifyingMemberInfo()?.GetMemberType() ?? typeof(object); + public override Type ClrType { get; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -263,7 +263,7 @@ public virtual Navigation? Inverse /// public virtual Navigation? SetInverse([CanBeNull] string? inverseName, ConfigurationSource configurationSource) => IsOnDependent - ? ForeignKey.HasPrincipalToDependent(inverseName, configurationSource) + ? ForeignKey.SetPrincipalToDependent(inverseName, configurationSource) : ForeignKey.SetDependentToPrincipal(inverseName, configurationSource); /// @@ -274,7 +274,7 @@ public virtual Navigation? Inverse /// public virtual Navigation? SetInverse([CanBeNull] MemberInfo? inverse, ConfigurationSource configurationSource) => IsOnDependent - ? ForeignKey.HasPrincipalToDependent(inverse, configurationSource) + ? ForeignKey.SetPrincipalToDependent(inverse, configurationSource) : ForeignKey.SetDependentToPrincipal(inverse, configurationSource); /// diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index 9eac902c40c..c51e337104c 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.cs @@ -13,7 +13,6 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Update; using Microsoft.EntityFrameworkCore.Utilities; -using CA = System.Diagnostics.CodeAnalysis; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs index 1c3bb825204..ee9a08d95f8 100644 --- a/src/EFCore/Metadata/Internal/ServiceProperty.cs +++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs @@ -169,6 +169,26 @@ public virtual ServiceParameterBinding? ParameterBinding private void UpdateParameterBindingConfigurationSource(ConfigurationSource configurationSource) => _parameterBindingConfigurationSource = configurationSource.Max(_parameterBindingConfigurationSource); + /// + /// 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 override string ToString() + => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + + /// + /// 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 DebugView DebugView + => new( + () => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + /// /// 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 @@ -232,24 +252,10 @@ IEntityType IServiceProperty.DeclaringEntityType get => 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 override string ToString() - => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); - - /// - /// 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 DebugView DebugView - => new( - () => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.ShortDefault), - () => ((IServiceProperty)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); + /// + [DebuggerStepThrough] + PropertyAccessMode IReadOnlyPropertyBase.GetPropertyAccessMode() + => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] + ?? PropertyAccessMode.PreferField); } } diff --git a/src/EFCore/Metadata/SlimEntityType.cs b/src/EFCore/Metadata/SlimEntityType.cs index e2904b8347b..a27e87c8730 100644 --- a/src/EFCore/Metadata/SlimEntityType.cs +++ b/src/EFCore/Metadata/SlimEntityType.cs @@ -530,11 +530,30 @@ public virtual SlimIndex AddIndex( return index; } - private SlimIndex? FindIndex([NotNull] IReadOnlyList properties) - => FindDeclaredIndex(properties) ?? _baseType?.FindIndex(properties); + /// + /// + /// Gets the unnamed index defined on the given properties. Returns if no such index is defined. + /// + /// + /// Named indexes will not be returned even if the list of properties matches. + /// + /// + /// The properties to find the index on. + /// The index, or if none is found. + public virtual SlimIndex? FindIndex([NotNull] IReadOnlyList properties) + => _unnamedIndexes.TryGetValue(properties, out var index) + ? index + : _baseType?.FindIndex(properties); - private SlimIndex? FindIndex([NotNull] string name) - => FindDeclaredIndex(name) ?? _baseType?.FindIndex(name); + /// + /// Gets the index with the given name. Returns if no such index exists. + /// + /// The name of the index. + /// The index, or if none is found. + public virtual SlimIndex? FindIndex([NotNull] string name) + => _namedIndexes.TryGetValue(name, out var index) + ? index + : _baseType?.FindIndex(name); private IEnumerable GetDeclaredIndexes() => _namedIndexes.Count == 0 @@ -546,16 +565,6 @@ private IEnumerable GetDerivedIndexes() ? Enumerable.Empty() : GetDerivedTypes().SelectMany(et => et.GetDeclaredIndexes()); - private SlimIndex? FindDeclaredIndex([NotNull] IReadOnlyList properties) - => _unnamedIndexes.TryGetValue(properties, out var index) - ? index - : null; - - private SlimIndex? FindDeclaredIndex([NotNull] string name) - => _namedIndexes.TryGetValue(name, out var index) - ? index - : null; - private IEnumerable GetIndexes() => _baseType != null ? _namedIndexes.Count == 0 && _unnamedIndexes.Count == 0 @@ -1354,11 +1363,5 @@ IEnumerable IEntityType.GetServiceProperties() IEnumerable> IReadOnlyEntityType.GetSeedData(bool providerValues) => throw new InvalidOperationException(CoreStrings.SlimModelMissingData); - - private static IEnumerable ToEnumerable(T? element) - where T : class - => element == null - ? Enumerable.Empty() - : new[] { element }; } } diff --git a/src/EFCore/Metadata/SlimServiceProperty.cs b/src/EFCore/Metadata/SlimServiceProperty.cs index b529e760d5b..91a8f29ede1 100644 --- a/src/EFCore/Metadata/SlimServiceProperty.cs +++ b/src/EFCore/Metadata/SlimServiceProperty.cs @@ -7,6 +7,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Metadata @@ -99,5 +100,11 @@ IEntityType IServiceProperty.DeclaringEntityType [DebuggerStepThrough] get => DeclaringEntityType; } + + /// + [DebuggerStepThrough] + PropertyAccessMode IReadOnlyPropertyBase.GetPropertyAccessMode() + => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] + ?? PropertyAccessMode.PreferField); } } diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs index 234d7b704a3..0344cfcb431 100644 --- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs +++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs @@ -470,6 +470,9 @@ public IEnumerable GetDerivedTypes() public IEnumerable GetDirectlyDerivedTypes() => throw new NotImplementedException(); + public string GetDiscriminatorPropertyName() + => throw new NotImplementedException(); + public IEnumerable GetForeignKeyProperties() => throw new NotImplementedException(); diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index 3d3867c8cac..ed06d07b56f 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -507,7 +507,7 @@ protected override void Down(MigrationBuilder migrationBuilder) var modelBuilder = new ModelBuilder(); modelBuilder.HasAnnotation("Some:EnumValue", RegexOptions.Multiline); - modelBuilder.HasAnnotation(RelationalAnnotationNames.DbFunctions, new SortedDictionary()); + modelBuilder.HasAnnotation(RelationalAnnotationNames.DbFunctions, new SortedDictionary()); modelBuilder.Entity( "T1", eb => { diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs index fd6e63794b0..9e860d98a36 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs @@ -37,9 +37,6 @@ public void Can_use_relational_model_with_tables(bool useExplicitMapping, Mappin { var model = CreateTestModel(mapToTables: useExplicitMapping, mapping: mapping); - var m = model.Model.ToDebugString(MetadataDebugStringOptions.LongDefault); - var s = model.ToDebugString(MetadataDebugStringOptions.LongDefault); - Assert.Equal(9, model.Model.GetEntityTypes().Count()); Assert.Equal(mapping == Mapping.TPH || !useExplicitMapping ? 3 : 5, model.Tables.Count()); Assert.Empty(model.Views); @@ -905,7 +902,7 @@ public void Can_use_relational_model_with_functions() Assert.Same(tvfFunction, tvfDbFunction.StoreFunction); Assert.Same(model.Model.GetDbFunctions().Single(f => f.Parameters.Count() == 1), tvfDbFunction); Assert.Same(tvfFunction.Parameters.Single(), tvfDbFunction.Parameters.Single().StoreFunctionParameter); - Assert.Same(tvfDbFunction.Parameters.Single(), tvfFunction.Parameters.Single().DbFunctionParameters.Single()); + Assert.Equal(tvfDbFunction.Parameters.Single().Name, tvfFunction.Parameters.Single().DbFunctionParameters.Single().Name); } private static IRelationalModel Finalize(ModelBuilder modelBuilder) diff --git a/test/EFCore.Specification.Tests/Query/ComplexNavigationsSharedTypeQueryFixtureBase.cs b/test/EFCore.Specification.Tests/Query/ComplexNavigationsSharedTypeQueryFixtureBase.cs index 7cd335a7a2f..b51535a638c 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexNavigationsSharedTypeQueryFixtureBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexNavigationsSharedTypeQueryFixtureBase.cs @@ -53,7 +53,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con { level2Fk = (ForeignKey)level2.AddForeignKey(level2.FindProperty(nameof(Level2.Id)), level1.FindPrimaryKey(), level1); level2Fk.IsUnique = true; - level2Fk.HasPrincipalToDependent(nameof(Level1.OneToOne_Required_PK1), ConfigurationSource.Explicit); + level2Fk.SetPrincipalToDependent(nameof(Level1.OneToOne_Required_PK1), ConfigurationSource.Explicit); level2Fk.SetDependentToPrincipal(nameof(Level2.OneToOne_Required_PK_Inverse2), ConfigurationSource.Explicit); level2Fk.DeleteBehavior = DeleteBehavior.Restrict; level2Fk = (ForeignKey)batch.Run(level2Fk); @@ -146,7 +146,7 @@ protected virtual void Configure(OwnedNavigationBuilder l2) { level3Fk = (ForeignKey)level3.AddForeignKey(level3.FindProperty(nameof(Level3.Id)), level2.FindPrimaryKey(), level2); level3Fk.IsUnique = true; - level3Fk.HasPrincipalToDependent(nameof(Level2.OneToOne_Required_PK2), ConfigurationSource.Explicit); + level3Fk.SetPrincipalToDependent(nameof(Level2.OneToOne_Required_PK2), ConfigurationSource.Explicit); level3Fk.SetDependentToPrincipal(nameof(Level3.OneToOne_Required_PK_Inverse3), ConfigurationSource.Explicit); level3Fk.DeleteBehavior = DeleteBehavior.Restrict; level3Fk = (ForeignKey)batch.Run(level3Fk); @@ -197,7 +197,7 @@ protected virtual void Configure(OwnedNavigationBuilder l3) { level4Fk = (ForeignKey)level4.AddForeignKey(level4.FindProperty(nameof(Level4.Id)), level3.FindPrimaryKey(), level3); level4Fk.IsUnique = true; - level4Fk.HasPrincipalToDependent(nameof(Level3.OneToOne_Required_PK3), ConfigurationSource.Explicit); + level4Fk.SetPrincipalToDependent(nameof(Level3.OneToOne_Required_PK3), ConfigurationSource.Explicit); level4Fk.SetDependentToPrincipal(nameof(Level4.OneToOne_Required_PK_Inverse4), ConfigurationSource.Explicit); level4Fk.DeleteBehavior = DeleteBehavior.Restrict; level4Fk = (ForeignKey)batch.Run(level4Fk); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index 5fb050f6617..f92d7bd2be9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -9195,7 +9195,7 @@ public int Id class SingleThreadSynchronizationContext22841 : SynchronizationContext, IDisposable { - private CancellationTokenSource _cancellationTokenSource; + private readonly CancellationTokenSource _cancellationTokenSource; readonly BlockingCollection<(SendOrPostCallback callback, object state)> _tasks = new(); internal Thread Thread { get; } diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs index 8e2a14a3ac9..1c932f7efb2 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs @@ -1733,7 +1733,7 @@ public void OnNavigationAdded_calls_conventions_in_order(bool useBuilder, bool u Assert.Equal(!useScope, result == null); - result = fk.HasPrincipalToDependent(Order.OrderDetailsProperty, ConfigurationSource.Explicit); + result = fk.SetPrincipalToDependent(Order.OrderDetailsProperty, ConfigurationSource.Explicit); Assert.Equal(!useScope, result == null); } @@ -1926,7 +1926,9 @@ public void OnNavigationRemoved_calls_conventions_in_order(bool useBuilder, bool } else { - Assert.NotNull(relationshipBuilder.Metadata.SetDependentToPrincipal((string)null, ConfigurationSource.Convention)); + var result = relationshipBuilder.Metadata.SetDependentToPrincipal((string)null, ConfigurationSource.Convention); + + Assert.Equal(!useScope, result == null); } if (useScope) diff --git a/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs index 209813aec81..05f78f8e689 100644 --- a/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/PropertyAttributeConventionTest.cs @@ -162,12 +162,12 @@ public void KeyAttribute_sets_primary_key_for_single_property() var propertyBuilder = entityTypeBuilder.Property(typeof(int), "MyPrimaryKey", ConfigurationSource.Explicit); - Assert.Null(entityTypeBuilder.Metadata.FindDeclaredPrimaryKey()); + Assert.Null(entityTypeBuilder.Metadata.FindPrimaryKey()); RunConvention(propertyBuilder); - Assert.Equal(1, entityTypeBuilder.Metadata.FindDeclaredPrimaryKey().Properties.Count); - Assert.Equal("MyPrimaryKey", entityTypeBuilder.Metadata.FindDeclaredPrimaryKey().Properties[0].Name); + Assert.Equal(1, entityTypeBuilder.Metadata.FindPrimaryKey().Properties.Count); + Assert.Equal("MyPrimaryKey", entityTypeBuilder.Metadata.FindPrimaryKey().Properties[0].Name); } [ConditionalFact] @@ -175,15 +175,15 @@ public void KeyAttribute_throws_when_setting_composite_primary_key() { var entityTypeBuilder = CreateInternalEntityTypeBuilder(); - Assert.Null(entityTypeBuilder.Metadata.FindDeclaredPrimaryKey()); + Assert.Null(entityTypeBuilder.Metadata.FindPrimaryKey()); var idPropertyBuilder = entityTypeBuilder.Property(typeof(int), "Id", ConfigurationSource.Explicit); var myPrimaryKeyPropertyBuilder = entityTypeBuilder.Property(typeof(int), "MyPrimaryKey", ConfigurationSource.Explicit); RunConvention(idPropertyBuilder); - Assert.Equal(1, entityTypeBuilder.Metadata.FindDeclaredPrimaryKey().Properties.Count); - Assert.Equal("Id", entityTypeBuilder.Metadata.FindDeclaredPrimaryKey().Properties[0].Name); + Assert.Equal(1, entityTypeBuilder.Metadata.FindPrimaryKey().Properties.Count); + Assert.Equal("Id", entityTypeBuilder.Metadata.FindPrimaryKey().Properties[0].Name); RunConvention(myPrimaryKeyPropertyBuilder); diff --git a/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs index 1cc55e5cb08..527fa30ef1b 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs @@ -75,6 +75,9 @@ public IComparer GetCurrentValueComparer() public IClrCollectionAccessor GetCollectionAccessor() => throw new NotImplementedException(); + public PropertyAccessMode GetPropertyAccessMode() + => throw new NotImplementedException(); + public Type CollectionType { get; } } diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs index eb0dc43afd9..afe6148377f 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs @@ -50,99 +50,64 @@ public IComparer GetCurrentValueComparer() => throw new NotImplementedException(); public CoreTypeMapping FindTypeMapping() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetMaxLength() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetPrecision() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetScale() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool? IsUnicode() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public PropertySaveBehavior GetBeforeSaveBehavior() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public PropertySaveBehavior GetAfterSaveBehavior() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public Func GetValueGeneratorFactory() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueConverter GetValueConverter() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public Type GetProviderClrType() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueComparer GetValueComparer() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueComparer GetKeyValueComparer() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsForeignKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingForeignKeys() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsIndex() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingIndexes() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public IReadOnlyKey FindContainingPrimaryKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingKeys() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); + + public PropertyAccessMode GetPropertyAccessMode() + => throw new NotImplementedException(); public string Name { get; } public ITypeBase DeclaringType { get; } diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs index 7f08fd40196..6f4b6a65298 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs @@ -65,99 +65,64 @@ public IComparer GetCurrentValueComparer() => throw new NotImplementedException(); public CoreTypeMapping FindTypeMapping() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetMaxLength() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetPrecision() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public int? GetScale() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool? IsUnicode() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public PropertySaveBehavior GetBeforeSaveBehavior() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public PropertySaveBehavior GetAfterSaveBehavior() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public Func GetValueGeneratorFactory() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueConverter GetValueConverter() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public Type GetProviderClrType() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueComparer GetValueComparer() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public ValueComparer GetKeyValueComparer() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsForeignKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingForeignKeys() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsIndex() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingIndexes() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public IReadOnlyKey FindContainingPrimaryKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); public bool IsKey() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); IEnumerable IReadOnlyProperty.GetContainingKeys() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); + + public PropertyAccessMode GetPropertyAccessMode() + => throw new NotImplementedException(); } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs index 847e28b5491..3755cad038e 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs @@ -105,7 +105,7 @@ public void Existing_facets_are_configured_explicitly() foreignKey.UpdatePrincipalEndConfigurationSource(ConfigurationSource.Explicit); foreignKey.SetDependentToPrincipal(Order.CustomerProperty, ConfigurationSource.Explicit); - foreignKey.HasPrincipalToDependent(Customer.OrdersProperty, ConfigurationSource.Explicit); + foreignKey.SetPrincipalToDependent(Customer.OrdersProperty, ConfigurationSource.Explicit); foreignKey.IsUnique = false; foreignKey.IsRequired = false; foreignKey.IsRequiredDependent = false; @@ -775,7 +775,7 @@ public void Can_only_override_existing_conflicting_navigations_explicitly() principalEntityBuilder.Metadata, ConfigurationSource.Explicit, ConfigurationSource.Explicit); - existingForeignKey.HasPrincipalToDependent(Customer.OrdersProperty, ConfigurationSource.Explicit); + existingForeignKey.SetPrincipalToDependent(Customer.OrdersProperty, ConfigurationSource.Explicit); existingForeignKey.SetDependentToPrincipal(Order.CustomerProperty, ConfigurationSource.Explicit); Assert.Equal(ConfigurationSource.Explicit, existingForeignKey.GetDependentToPrincipalConfigurationSource()); Assert.Equal(ConfigurationSource.Explicit, existingForeignKey.GetPrincipalToDependentConfigurationSource());