diff --git a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs index 45df40bf3e2..a7cc3574088 100644 --- a/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs +++ b/src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for Cosmos metadata. + /// Entity type extension methods for Cosmos metadata. /// public static class CosmosEntityTypeExtensions { @@ -21,13 +21,13 @@ public static class CosmosEntityTypeExtensions /// /// The entity type to get the container name for. /// The name of the container to which the entity type is mapped. - public static string? GetContainer([NotNull] this IEntityType entityType) + public static string? GetContainer([NotNull] this IReadOnlyEntityType entityType) => entityType.BaseType != null ? entityType.GetRootType().GetContainer() : (string?)entityType[CosmosAnnotationNames.ContainerName] ?? GetDefaultContainer(entityType); - private static string? GetDefaultContainer(IEntityType entityType) + private static string? GetDefaultContainer(IReadOnlyEntityType entityType) => entityType.IsOwned() ? null : entityType.Model.GetDefaultContainer() @@ -72,11 +72,11 @@ public static void SetContainer( /// /// The entity type to get the containing property name for. /// The name of the parent property to which the entity type is mapped. - public static string? GetContainingPropertyName([NotNull] this IEntityType entityType) + public static string? GetContainingPropertyName([NotNull] this IReadOnlyEntityType entityType) => entityType[CosmosAnnotationNames.PropertyName] as string ?? GetDefaultContainingPropertyName(entityType); - private static string? GetDefaultContainingPropertyName(IEntityType entityType) + private static string? GetDefaultContainingPropertyName(IReadOnlyEntityType entityType) => entityType.FindOwnership()?.PrincipalToDependent!.Name; /// @@ -118,7 +118,7 @@ public static void SetContainingPropertyName( /// /// The entity type to get the partition key property name for. /// The name of the partition key property. - public static string? GetPartitionKeyPropertyName([NotNull] this IEntityType entityType) + public static string? GetPartitionKeyPropertyName([NotNull] this IReadOnlyEntityType entityType) => entityType[CosmosAnnotationNames.PartitionKeyName] as string; /// @@ -156,15 +156,15 @@ public static void SetPartitionKeyPropertyName( ?.GetConfigurationSource(); /// - /// Returns the name of the property that is used to store the etag. + /// Returns the name of the property that is used to store the ETag. /// /// The entity type to get the etag property name for. /// The name of the etag property. - public static string? GetETagPropertyName([NotNull] this IEntityType entityType) + public static string? GetETagPropertyName([NotNull] this IReadOnlyEntityType entityType) => entityType[CosmosAnnotationNames.ETagName] as string; /// - /// Sets the name of the property that is used to store the etag key. + /// Sets the name of the property that is used to store the ETag key. /// /// The entity type to set the etag property name for. /// The name to set. @@ -174,9 +174,9 @@ public static void SetETagPropertyName([NotNull] this IMutableEntityType entityT Check.NullButNotEmpty(name, nameof(name))); /// - /// Sets the name of the property that is used to store the etag. + /// Sets the name of the property that is used to store the ETag. /// - /// The entity type to set the etag property name for. + /// The entity type to set the ETag property name for. /// The name to set. /// Indicates whether the configuration was specified using a data annotation. public static void SetETagPropertyName( @@ -198,15 +198,23 @@ public static void SetETagPropertyName( ?.GetConfigurationSource(); /// - /// Gets the on this entity that is mapped to cosmos etag, if it exists. + /// Gets the property on this entity that is mapped to cosmos ETag, if it exists. /// - /// The entity type to get the etag property for. - /// The mapped to etag, or null if no property is mapped to etag. - public static IProperty? GetETagProperty([NotNull] this IEntityType entityType) + /// The entity type to get the ETag property for. + /// The property mapped to ETag, or if no property is mapped to ETag. + public static IReadOnlyProperty? GetETagProperty([NotNull] this IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); var etagPropertyName = entityType.GetETagPropertyName(); return !string.IsNullOrEmpty(etagPropertyName) ? entityType.FindProperty(etagPropertyName) : null; } + + /// + /// Gets the property on this entity that is mapped to cosmos ETag, if it exists. + /// + /// The entity type to get the ETag property for. + /// The property mapped to etag, or if no property is mapped to ETag. + public static IProperty? GetETagProperty([NotNull] this IEntityType entityType) + => (IProperty?)((IReadOnlyEntityType)entityType).GetETagProperty(); } } diff --git a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs index 80b4de7d1a2..e4579226e5d 100644 --- a/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs +++ b/src/EFCore.Cosmos/Extensions/CosmosModelExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for Cosmos metadata. + /// Model extension methods for Cosmos metadata. /// public static class CosmosModelExtensions { @@ -21,7 +21,7 @@ public static class CosmosModelExtensions /// /// The model. /// The default container name. - public static string? GetDefaultContainer([NotNull] this IModel model) + public static string? GetDefaultContainer([NotNull] this IReadOnlyModel model) => (string?)model[CosmosAnnotationNames.ContainerName]; /// diff --git a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs index 4fb7a1a0f88..b62728b94f8 100644 --- a/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs +++ b/src/EFCore.Cosmos/Extensions/CosmosPropertyExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for Cosmos metadata. + /// Property extension methods for Cosmos metadata. /// public static class CosmosPropertyExtensions { @@ -21,11 +21,11 @@ public static class CosmosPropertyExtensions /// /// The property. /// Returns the property name that the property is mapped to when targeting Cosmos. - public static string GetJsonPropertyName([NotNull] this IProperty property) + public static string GetJsonPropertyName([NotNull] this IReadOnlyProperty property) => (string?)property[CosmosAnnotationNames.PropertyName] ?? GetDefaultJsonPropertyName(property); - private static string GetDefaultJsonPropertyName(IProperty property) + private static string GetDefaultJsonPropertyName(IReadOnlyProperty property) { var entityType = property.DeclaringEntityType; var ownership = entityType.FindOwnership(); diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs index e901135d272..a07a63542a1 100644 --- a/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs +++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosEntityTypeExtensions.cs @@ -22,7 +22,7 @@ public static class CosmosEntityTypeExtensions /// 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 IsDocumentRoot([NotNull] this IEntityType entityType) + public static bool IsDocumentRoot([NotNull] this IReadOnlyEntityType entityType) => entityType.BaseType?.IsDocumentRoot() ?? (!entityType.IsOwned() || entityType[CosmosAnnotationNames.ContainerName] != null); diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosNavigationExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosNavigationExtensions.cs index 18f8de69351..e802105e741 100644 --- a/src/EFCore.Cosmos/Metadata/Internal/CosmosNavigationExtensions.cs +++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosNavigationExtensions.cs @@ -22,7 +22,7 @@ public static class CosmosNavigationExtensions /// 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 IsEmbedded([NotNull] this INavigation navigation) + public static bool IsEmbedded([NotNull] this IReadOnlyNavigation navigation) => !navigation.IsOnDependent && !navigation.ForeignKey.DeclaringEntityType.IsDocumentRoot(); } diff --git a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs index c6c696422d8..89fe14458ba 100644 --- a/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs +++ b/src/EFCore.Cosmos/Metadata/Internal/CosmosPropertyExtensions.cs @@ -23,13 +23,13 @@ public static class CosmosPropertyExtensions /// 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 IsOrdinalKeyProperty([NotNull] this IProperty property) + public static bool IsOrdinalKeyProperty([NotNull] this IReadOnlyProperty property) { Check.DebugAssert( property.DeclaringEntityType.IsOwned(), $"Expected {property.DeclaringEntityType.DisplayName()} to be owned."); Check.DebugAssert(property.GetJsonPropertyName().Length == 0, $"Expected {property.Name} to be non-persisted."); - return property.FindContainingPrimaryKey() is IKey key + return property.FindContainingPrimaryKey() is IReadOnlyKey key && key.Properties.Count > 1 && !property.IsForeignKey() && property.ClrType == typeof(int) diff --git a/src/EFCore.Design/Metadata/Internal/ScaffoldingEntityTypeAnnotations.cs b/src/EFCore.Design/Metadata/Internal/ScaffoldingEntityTypeAnnotations.cs index f3f676955b9..ed01c7dceb1 100644 --- a/src/EFCore.Design/Metadata/Internal/ScaffoldingEntityTypeAnnotations.cs +++ b/src/EFCore.Design/Metadata/Internal/ScaffoldingEntityTypeAnnotations.cs @@ -20,7 +20,7 @@ public static class ScaffoldingEntityTypeAnnotations /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static string GetDbSetName([NotNull] this IEntityType entityType) + public static string GetDbSetName([NotNull] this IReadOnlyEntityType entityType) => (string)entityType[ScaffoldingAnnotationNames.DbSetName] ?? entityType.Name; diff --git a/src/EFCore.Design/Metadata/Internal/ScaffoldingModelExtensions.cs b/src/EFCore.Design/Metadata/Internal/ScaffoldingModelExtensions.cs index 0dda926192c..d2fba9b1699 100644 --- a/src/EFCore.Design/Metadata/Internal/ScaffoldingModelExtensions.cs +++ b/src/EFCore.Design/Metadata/Internal/ScaffoldingModelExtensions.cs @@ -21,7 +21,16 @@ public static class ScaffoldingModelExtensions /// 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 IDictionary GetEntityTypeErrors([NotNull] this IModel model) + public static IReadOnlyDictionary GetEntityTypeErrors([NotNull] this IReadOnlyModel model) + => (IReadOnlyDictionary)model[ScaffoldingAnnotationNames.EntityTypeErrors] ?? new Dictionary(); + + /// + /// 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 IDictionary GetOrCreateEntityTypeErrors([NotNull] this IReadOnlyModel model) { var errors = (IDictionary)model[ScaffoldingAnnotationNames.EntityTypeErrors]; if (errors == null) @@ -50,7 +59,7 @@ public static void SetEntityTypeErrors([NotNull] this IMutableModel model, [NotN /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static string GetDatabaseName([NotNull] this IModel model) + public static string GetDatabaseName([NotNull] this IReadOnlyModel model) => (string)model[ScaffoldingAnnotationNames.DatabaseName]; /// diff --git a/src/EFCore.Design/Metadata/Internal/ScaffoldingPropertyExtensions.cs b/src/EFCore.Design/Metadata/Internal/ScaffoldingPropertyExtensions.cs index 68ba429e131..16657b13951 100644 --- a/src/EFCore.Design/Metadata/Internal/ScaffoldingPropertyExtensions.cs +++ b/src/EFCore.Design/Metadata/Internal/ScaffoldingPropertyExtensions.cs @@ -19,7 +19,7 @@ public static class ScaffoldingPropertyExtensions /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static int GetColumnOrdinal([NotNull] this IProperty property) + public static int GetColumnOrdinal([NotNull] this IReadOnlyProperty property) => (int?)property[ScaffoldingAnnotationNames.ColumnOrdinal] ?? -1; /// diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 8a2903667de..549d77d732f 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -232,7 +232,11 @@ protected virtual void GenerateEntityType( GenerateProperties(builderName, entityType.GetDeclaredProperties(), stringBuilder); - GenerateKeys(builderName, entityType.GetDeclaredKeys(), entityType.FindDeclaredPrimaryKey(), stringBuilder); + GenerateKeys( + builderName, + entityType.GetDeclaredKeys(), + entityType.BaseType == null ? entityType.FindPrimaryKey() : null, + stringBuilder); GenerateIndexes(builderName, entityType.GetDeclaredIndexes(), stringBuilder); diff --git a/src/EFCore.Design/Migrations/Internal/ISnapshotModelProcessor.cs b/src/EFCore.Design/Migrations/Internal/ISnapshotModelProcessor.cs index 6f1cee89f3a..4accc11bd28 100644 --- a/src/EFCore.Design/Migrations/Internal/ISnapshotModelProcessor.cs +++ b/src/EFCore.Design/Migrations/Internal/ISnapshotModelProcessor.cs @@ -20,6 +20,6 @@ public interface ISnapshotModelProcessor /// 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. /// - IModel Process([CanBeNull] IModel model); + IModel Process([CanBeNull] IReadOnlyModel model); } } diff --git a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs index 608f971b7a5..ad05924cd0e 100644 --- a/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs +++ b/src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs @@ -51,7 +51,7 @@ public SnapshotModelProcessor( /// 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 IModel Process(IModel model) + public virtual IModel Process(IReadOnlyModel model) { if (model == null) { @@ -85,10 +85,10 @@ public virtual IModel Process(IModel model) model = mutableModel.FinalizeModel(); } - return _modelRuntimeInitializer.Initialize(model, validationLogger: null); + return _modelRuntimeInitializer.Initialize((IModel)model, validationLogger: null); } - private void ProcessCollection(IEnumerable metadata, string version) + private void ProcessCollection(IEnumerable metadata, string version) { foreach (var element in metadata) { @@ -96,9 +96,9 @@ private void ProcessCollection(IEnumerable metadata, string versio } } - private void ProcessElement(IEntityType entityType, string version) + private void ProcessElement(IReadOnlyEntityType entityType, string version) { - ProcessElement((IAnnotatable)entityType, version); + ProcessElement((IReadOnlyAnnotatable)entityType, version); if ((version.StartsWith("2.0", StringComparison.Ordinal) || version.StartsWith("2.1", StringComparison.Ordinal)) @@ -109,7 +109,7 @@ private void ProcessElement(IEntityType entityType, string version) } } - private void ProcessElement(IAnnotatable metadata, string version) + private void ProcessElement(IReadOnlyAnnotatable metadata, string version) { if (version.StartsWith("1.", StringComparison.Ordinal) && metadata is IMutableAnnotatable mutableMetadata) @@ -141,7 +141,7 @@ private void ProcessElement(IAnnotatable metadata, string version) } } - private void UpdateSequences(IModel model, string version) + private void UpdateSequences(IReadOnlyModel model, string version) { if ((!version.StartsWith("1.", StringComparison.Ordinal) && !version.StartsWith("2.", StringComparison.Ordinal) diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs index effcf385165..0b37923dcc8 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs @@ -183,12 +183,13 @@ private void GenerateDbSets(IModel model) private void GenerateEntityTypeErrors(IModel model) { - foreach (var entityTypeError in model.GetEntityTypeErrors()) + var errors = model.GetEntityTypeErrors(); + foreach (var entityTypeError in errors) { _sb.AppendLine($"// {entityTypeError.Value} Please see the warning messages."); } - if (model.GetEntityTypeErrors().Count > 0) + if (errors.Count > 0) { _sb.AppendLine(); } @@ -454,11 +455,11 @@ private void GenerateKey(IKey key, IEntityType entityType, bool useDataAnnotatio if (key.Properties.Count == 1 && annotations.Count == 0) { - if (key is Key concreteKey - && key.Properties.SequenceEqual( + if (key is IConventionKey conventionKey + && conventionKey.Properties.SequenceEqual( KeyDiscoveryConvention.DiscoverKeyProperties( - concreteKey.DeclaringEntityType, - concreteKey.DeclaringEntityType.GetProperties()))) + conventionKey.DeclaringEntityType, + conventionKey.DeclaringEntityType.GetProperties()))) { return; } diff --git a/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs b/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs index 138e6d75093..b74d10f8ca5 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CandidateNamingService.cs @@ -43,7 +43,7 @@ public virtual string GenerateCandidateIdentifier(DatabaseColumn originalColumn) /// 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 string GetDependentEndCandidateNavigationPropertyName(IForeignKey foreignKey) + public virtual string GetDependentEndCandidateNavigationPropertyName(IReadOnlyForeignKey foreignKey) { Check.NotNull(foreignKey, nameof(foreignKey)); @@ -59,7 +59,7 @@ public virtual string GetDependentEndCandidateNavigationPropertyName(IForeignKey /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual string GetPrincipalEndCandidateNavigationPropertyName( - IForeignKey foreignKey, + IReadOnlyForeignKey foreignKey, string dependentEndNavigationPropertyName) { Check.NotNull(foreignKey, nameof(foreignKey)); @@ -109,7 +109,7 @@ private static string GenerateCandidateIdentifier(string originalIdentifier) return candidateStringBuilder.ToString(); } - private static string FindCandidateNavigationName(IEnumerable properties) + private static string FindCandidateNavigationName(IEnumerable properties) { if (!properties.Any()) { diff --git a/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs b/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs index 6897c0f62b4..ff77da82006 100644 --- a/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs +++ b/src/EFCore.Design/Scaffolding/Internal/ICandidateNamingService.cs @@ -37,7 +37,7 @@ public interface ICandidateNamingService /// 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 GetDependentEndCandidateNavigationPropertyName([NotNull] IForeignKey foreignKey); + string GetDependentEndCandidateNavigationPropertyName([NotNull] IReadOnlyForeignKey foreignKey); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -46,7 +46,7 @@ public interface ICandidateNamingService /// doing so can result in application failures when updating to a new Entity Framework Core release. /// string GetPrincipalEndCandidateNavigationPropertyName( - [NotNull] IForeignKey foreignKey, + [NotNull] IReadOnlyForeignKey foreignKey, [NotNull] string dependentEndNavigationPropertyName); } } diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs index aeb096c05e1..f8659453cae 100644 --- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs +++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs @@ -108,7 +108,7 @@ public virtual IModel Create(DatabaseModel databaseModel, ModelReverseEngineerOp VisitDatabaseModel(modelBuilder, databaseModel); - return modelBuilder.Model; + return modelBuilder.FinalizeModel(); } /// @@ -359,7 +359,7 @@ protected virtual EntityTypeBuilder VisitTable([NotNull] ModelBuilder modelBuild var model = modelBuilder.Model; model.RemoveEntityType(entityTypeName); - model.GetEntityTypeErrors().Add(entityTypeName, errorMessage); + model.GetOrCreateEntityTypeErrors().Add(entityTypeName, errorMessage); return null; } } @@ -928,7 +928,7 @@ protected virtual void AddNavigationProperties([NotNull] IMutableForeignKey fore } // Stores the names of the EntityType itself and its Properties, but does not include any Navigation Properties - private readonly Dictionary> _entityTypeAndPropertyIdentifiers = + private readonly Dictionary> _entityTypeAndPropertyIdentifiers = new(); /// @@ -937,7 +937,7 @@ protected virtual void AddNavigationProperties([NotNull] IMutableForeignKey fore /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - protected virtual List ExistingIdentifiers([NotNull] IEntityType entityType) + protected virtual List ExistingIdentifiers([NotNull] IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); diff --git a/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs b/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs index 50108930b58..0d9ba49ec46 100644 --- a/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs +++ b/src/EFCore.Design/Scaffolding/Internal/ReverseEngineerScaffolder.cs @@ -124,7 +124,6 @@ public virtual ScaffoldedModel ScaffoldModel( } var model = _factory.Create(databaseModel, modelOptions); - if (model == null) { throw new InvalidOperationException( diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs index 123181eabfc..178d6cadfcd 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for the in-memory provider. + /// Extension methods for for the in-memory provider. /// public static class InMemoryEntityTypeExtensions { @@ -22,7 +22,7 @@ public static class InMemoryEntityTypeExtensions /// /// The entity type to get the in-memory query for. /// The LINQ query used as the default source. - public static LambdaExpression? GetInMemoryQuery([NotNull] this IEntityType entityType) + public static LambdaExpression? GetInMemoryQuery([NotNull] this IReadOnlyEntityType entityType) #pragma warning disable EF1001 // Internal EF Core API usage. #pragma warning disable CS0612 // Type or member is obsolete => (LambdaExpression?)Check.NotNull(entityType, nameof(entityType))[CoreAnnotationNames.DefiningQuery]; diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs index 60bf77b3c4f..c3b65ba8a92 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryExpression.cs @@ -291,7 +291,7 @@ public ShaperRemappingExpressionVisitor(IDictionary GetAllPropertiesInHierarchy(IEntityType entityType) => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredProperties); + .SelectMany(t => t.GetDeclaredProperties()); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -914,7 +914,7 @@ EntityProjectionExpression CopyEntityProjectionToOuter(EntityProjectionExpressio // Also lift nested entity projections foreach (var navigation in entityProjection.EntityType.GetAllBaseTypes() .Concat(entityProjection.EntityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredNavigations)) + .SelectMany(t => t.GetDeclaredNavigations())) { var boundEntityShaperExpression = entityProjection.BindNavigation(navigation); if (boundEntityShaperExpression != null) @@ -1165,7 +1165,7 @@ EntityProjectionExpression CopyEntityProjectionToOuter(EntityProjectionExpressio // Also lift nested entity projections foreach (var navigation in entityProjection.EntityType.GetAllBaseTypes() .Concat(entityProjection.EntityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredNavigations)) + .SelectMany(t => t.GetDeclaredNavigations())) { var boundEntityShaperExpression = entityProjection.BindNavigation(navigation); if (boundEntityShaperExpression != null) diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs index b5b0b144eea..d9015ca89ce 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs @@ -73,7 +73,7 @@ public virtual void ProcessModelFinalizing( throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName())); } - var proxyType = _proxyFactory.CreateProxyType(_options, entityType); + var proxyType = _proxyFactory.CreateProxyType(_options, (IEntityType)entityType); // WARNING: This code is EF internal; it should not be copied. See #10789 #14554 #pragma warning disable EF1001 // Internal EF Core API usage. @@ -211,7 +211,7 @@ void UpdateConstructorBindings(string bindingAnnotationName, InstantiationBindin new ContextParameterBinding(typeof(DbContext)), new EntityTypeParameterBinding(), new DependencyInjectionParameterBinding( - typeof(ILazyLoader), typeof(ILazyLoader), serviceProperty), + typeof(ILazyLoader), typeof(ILazyLoader), (IPropertyBase)serviceProperty), new ObjectArrayParameterBinding(binding.ParameterBindings) }, proxyType)); diff --git a/src/EFCore.Relational/Diagnostics/SequenceEventData.cs b/src/EFCore.Relational/Diagnostics/SequenceEventData.cs index 93f2df5ab92..8bc8591e908 100644 --- a/src/EFCore.Relational/Diagnostics/SequenceEventData.cs +++ b/src/EFCore.Relational/Diagnostics/SequenceEventData.cs @@ -23,7 +23,7 @@ public class SequenceEventData : EventData public SequenceEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] ISequence sequence) + [NotNull] IReadOnlySequence sequence) : base(eventDefinition, messageGenerator) { Sequence = sequence; @@ -32,6 +32,6 @@ public SequenceEventData( /// /// The sequence. /// - public virtual ISequence Sequence { get; } + public virtual IReadOnlySequence Sequence { get; } } } diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index f746f0a3d6b..65c93b13b24 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for relational database metadata. + /// Entity type extension methods for relational database metadata. /// public static class RelationalEntityTypeExtensions { @@ -26,7 +26,7 @@ public static class RelationalEntityTypeExtensions /// /// The entity type to get the table name for. /// The name of the table to which the entity type is mapped. - public static string? GetTableName([NotNull] this IEntityType entityType) + public static string? GetTableName([NotNull] this IReadOnlyEntityType entityType) { var nameAnnotation = entityType.FindAnnotation(RelationalAnnotationNames.TableName); if (nameAnnotation != null) @@ -55,7 +55,7 @@ public static class RelationalEntityTypeExtensions /// The entity type to get the table name for. /// A value indicating whether the name should be truncated to the max identifier length. /// The default name of the table to which the entity type would be mapped. - public static string? GetDefaultTableName([NotNull] this IEntityType entityType, bool truncate = true) + public static string? GetDefaultTableName([NotNull] this IReadOnlyEntityType entityType, bool truncate = true) { var ownership = entityType.FindOwnership(); if (ownership != null @@ -126,7 +126,7 @@ public static void SetTableName([NotNull] this IMutableEntityType entityType, [C /// /// The entity type to get the schema for. /// The database schema that contains the mapped table. - public static string? GetSchema([NotNull] this IEntityType entityType) + public static string? GetSchema([NotNull] this IReadOnlyEntityType entityType) { var schemaAnnotation = entityType.FindAnnotation(RelationalAnnotationNames.Schema); if (schemaAnnotation != null) @@ -144,7 +144,7 @@ public static void SetTableName([NotNull] this IMutableEntityType entityType, [C /// /// The entity type to get the schema for. /// The default database schema to which the entity type would be mapped. - public static string? GetDefaultSchema([NotNull] this IEntityType entityType) + public static string? GetDefaultSchema([NotNull] this IReadOnlyEntityType entityType) { var ownership = entityType.FindOwnership(); if (ownership != null) @@ -211,7 +211,7 @@ public static void SetSchema([NotNull] this IMutableEntityType entityType, [CanB /// /// The entity type to get the table name for. /// The name of the table to which the entity type is mapped prepended by the schema. - public static string? GetSchemaQualifiedTableName([NotNull] this IEntityType entityType) + public static string? GetSchemaQualifiedTableName([NotNull] this IReadOnlyEntityType entityType) { var tableName = entityType.GetTableName(); if (tableName == null) @@ -229,7 +229,7 @@ public static void SetSchema([NotNull] this IMutableEntityType entityType, [CanB /// /// The entity type to get the table name for. /// The name of the table to which the entity type is mapped prepended by the schema. - public static string? GetSchemaQualifiedViewName([NotNull] this IEntityType entityType) + public static string? GetSchemaQualifiedViewName([NotNull] this IReadOnlyEntityType entityType) { var viewName = entityType.GetViewName(); if (viewName == null) @@ -247,7 +247,8 @@ public static void SetSchema([NotNull] this IMutableEntityType entityType, [CanB /// The entity type to get the table mappings for. /// The tables to which the entity type is mapped. public static IEnumerable GetDefaultMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultMappings) + => (IEnumerable?)entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.DefaultMappings) ?? Array.Empty(); /// @@ -256,7 +257,8 @@ public static IEnumerable GetDefaultMappings([NotNull] this I /// The entity type to get the table mappings for. /// The tables to which the entity type is mapped. public static IEnumerable GetTableMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings) + => (IEnumerable?)entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.TableMappings) ?? Array.Empty(); /// @@ -264,7 +266,7 @@ public static IEnumerable GetTableMappings([NotNull] this IEntity /// /// The entity type to get the view name for. /// The name of the view to which the entity type is mapped. - public static string? GetViewName([NotNull] this IEntityType entityType) + public static string? GetViewName([NotNull] this IReadOnlyEntityType entityType) { var nameAnnotation = (string?)entityType[RelationalAnnotationNames.ViewName]; if (nameAnnotation != null) @@ -291,7 +293,7 @@ public static IEnumerable GetTableMappings([NotNull] this IEntity /// /// The entity type to get the table name for. /// The default name of the table to which the entity type would be mapped. - public static string? GetDefaultViewName([NotNull] this IEntityType entityType) + public static string? GetDefaultViewName([NotNull] this IReadOnlyEntityType entityType) { var ownership = entityType.FindOwnership(); return ownership != null @@ -344,7 +346,7 @@ public static void SetViewName([NotNull] this IMutableEntityType entityType, [Ca /// /// The entity type to get the view schema for. /// The database schema that contains the mapped view. - public static string? GetViewSchema([NotNull] this IEntityType entityType) + public static string? GetViewSchema([NotNull] this IReadOnlyEntityType entityType) { var schemaAnnotation = entityType.FindAnnotation(RelationalAnnotationNames.ViewSchema); if (schemaAnnotation != null) @@ -362,7 +364,7 @@ public static void SetViewName([NotNull] this IMutableEntityType entityType, [Ca /// /// The entity type to get the view schema for. /// The default database schema to which the entity type would be mapped. - public static string? GetDefaultViewSchema([NotNull] this IEntityType entityType) + public static string? GetDefaultViewSchema([NotNull] this IReadOnlyEntityType entityType) { var ownership = entityType.FindOwnership(); if (ownership != null @@ -419,7 +421,8 @@ public static void SetViewSchema([NotNull] this IMutableEntityType entityType, [ /// The entity type to get the view mappings for. /// The views to which the entity type is mapped. public static IEnumerable GetViewMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewMappings) + => (IEnumerable?)entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.ViewMappings) ?? Array.Empty(); /// @@ -428,7 +431,7 @@ public static IEnumerable GetViewMappings([NotNull] this IEntityTy /// /// The entity type. /// Gets the default SQL query name. - public static string GetDefaultSqlQueryName([NotNull] this IEntityType entityType) + public static string GetDefaultSqlQueryName([NotNull] this IReadOnlyEntityType entityType) => entityType.Name + "." + SqlQueryExtensions.DefaultQueryNameBase; /// @@ -436,7 +439,7 @@ public static string GetDefaultSqlQueryName([NotNull] this IEntityType entityTyp /// /// The entity type. /// The SQL string used to provide data for the entity type. - public static string? GetSqlQuery([NotNull] this IEntityType entityType) + public static string? GetSqlQuery([NotNull] this IReadOnlyEntityType entityType) { var nameAnnotation = (string?)entityType[RelationalAnnotationNames.SqlQuery]; if (nameAnnotation != null) @@ -493,7 +496,8 @@ public static void SetSqlQuery([NotNull] this IMutableEntityType entityType, [Ca /// The entity type to get the function mappings for. /// The functions to which the entity type is mapped. public static IEnumerable GetSqlQueryMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.SqlQueryMappings) + => (IEnumerable?)entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.SqlQueryMappings) ?? Array.Empty(); /// @@ -501,7 +505,7 @@ public static IEnumerable GetSqlQueryMappings([NotNull] this I /// /// The entity type to get the function name for. /// The name of the function to which the entity type is mapped. - public static string? GetFunctionName([NotNull] this IEntityType entityType) + public static string? GetFunctionName([NotNull] this IReadOnlyEntityType entityType) { var nameAnnotation = (string?)entityType[RelationalAnnotationNames.FunctionName]; if (nameAnnotation != null) @@ -558,20 +562,21 @@ public static void SetFunctionName([NotNull] this IMutableEntityType entityType, /// The entity type to get the function mappings for. /// The functions to which the entity type is mapped. public static IEnumerable GetFunctionMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.FunctionMappings) + => (IEnumerable?)entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.FunctionMappings) ?? Array.Empty(); /// - /// Finds an with the given name. + /// Finds an with the given name. /// /// The entity type to find the check constraint for. /// The check constraint name. /// - /// The or if no check constraint with the + /// The or if no check constraint with the /// given name in the given entity type was found. /// - public static ICheckConstraint? FindCheckConstraint( - [NotNull] this IEntityType entityType, + public static IReadOnlyCheckConstraint? FindCheckConstraint( + [NotNull] this IReadOnlyEntityType entityType, [NotNull] string name) { Check.NotEmpty(name, nameof(name)); @@ -607,6 +612,20 @@ public static IEnumerable GetFunctionMappings([NotNull] this I [NotNull] string name) => (IConventionCheckConstraint?)((IEntityType)entityType).FindCheckConstraint(name); + /// + /// Finds an with the given name. + /// + /// The entity type to find the check constraint for. + /// The check constraint name. + /// + /// The or if no check constraint with the + /// given name in the given entity type was found. + /// + public static ICheckConstraint? FindCheckConstraint( + [NotNull] this IEntityType entityType, + [NotNull] string name) + => (ICheckConstraint?)((IReadOnlyEntityType)entityType).FindCheckConstraint(name); + /// /// Creates a new check constraint with the given name on entity type. Throws an exception /// if a check constraint with the same name exists on the same entity type. @@ -672,10 +691,10 @@ public static IConventionCheckConstraint AddCheckConstraint( => CheckConstraint.RemoveCheckConstraint((IMutableEntityType)entityType, Check.NotEmpty(name, nameof(name))); /// - /// Returns all contained in the entity type. + /// Returns all contained in the entity type. /// /// The entity type to get the check constraints for. - public static IEnumerable GetCheckConstraints([NotNull] this IEntityType entityType) + public static IEnumerable GetCheckConstraints([NotNull] this IReadOnlyEntityType entityType) => CheckConstraint.GetCheckConstraints(entityType); /// @@ -692,12 +711,19 @@ public static IEnumerable GetCheckConstraints([NotNull] public static IEnumerable GetCheckConstraints([NotNull] this IConventionEntityType entityType) => CheckConstraint.GetCheckConstraints(entityType); + /// + /// Returns all contained in the entity type. + /// + /// The entity type to get the check constraints for. + public static IEnumerable GetCheckConstraints([NotNull] this IEntityType entityType) + => CheckConstraint.GetCheckConstraints(entityType); + /// /// Returns the comment for the table this entity is mapped to. /// /// The entity type. /// The comment for the table this entity is mapped to. - public static string? GetComment([NotNull] this IEntityType entityType) + public static string? GetComment([NotNull] this IReadOnlyEntityType entityType) => (string?)entityType[RelationalAnnotationNames.Comment]; /// @@ -740,8 +766,8 @@ public static void SetComment([NotNull] this IMutableEntityType entityType, [Can /// /// The entity type. /// The identifier of the store object. - public static IEnumerable FindRowInternalForeignKeys( - [NotNull] this IEntityType entityType, + public static IEnumerable FindRowInternalForeignKeys( + [NotNull] this IReadOnlyEntityType entityType, StoreObjectIdentifier storeObject) { var primaryKey = entityType.FindPrimaryKey(); @@ -823,7 +849,7 @@ public static IEnumerable FindRowInternalForeignKeys( /// /// The entity type. /// A value indicating whether the associated table is ignored by Migrations. - public static bool IsTableExcludedFromMigrations([NotNull] this IEntityType entityType) + public static bool IsTableExcludedFromMigrations([NotNull] this IReadOnlyEntityType entityType) { var excluded = (bool?)entityType[RelationalAnnotationNames.IsTableExcludedFromMigrations]; if (excluded != null) diff --git a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs index f38ce583a8a..05ca9084255 100644 --- a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for relational database metadata. + /// Foregn key extension methods for relational database metadata. /// public static class RelationalForeignKeyExtensions { @@ -24,7 +24,7 @@ public static class RelationalForeignKeyExtensions /// /// The foreign key. /// The foreign key constraint name. - public static string GetConstraintName([NotNull] this IForeignKey foreignKey) + public static string GetConstraintName([NotNull] this IReadOnlyForeignKey foreignKey) { var annotation = foreignKey.FindAnnotation(RelationalAnnotationNames.Name); return annotation != null @@ -40,7 +40,7 @@ public static string GetConstraintName([NotNull] this IForeignKey foreignKey) /// The identifier of the principal store object. /// The foreign key constraint name. public static string? GetConstraintName( - [NotNull] this IForeignKey foreignKey, + [NotNull] this IReadOnlyForeignKey foreignKey, in StoreObjectIdentifier storeObject, in StoreObjectIdentifier principalStoreObject) { @@ -55,7 +55,7 @@ public static string GetConstraintName([NotNull] this IForeignKey foreignKey) /// /// The foreign key. /// The default constraint name that would be used for this foreign key. - public static string GetDefaultName([NotNull] this IForeignKey foreignKey) + public static string GetDefaultName([NotNull] this IReadOnlyForeignKey foreignKey) { var tableName = foreignKey.DeclaringEntityType.GetTableName(); var schema = foreignKey.DeclaringEntityType.GetSchema(); @@ -81,7 +81,7 @@ public static string GetDefaultName([NotNull] this IForeignKey foreignKey) /// The identifier of the principal store object. /// The default constraint name that would be used for this foreign key. public static string? GetDefaultName( - [NotNull] this IForeignKey foreignKey, + [NotNull] this IReadOnlyForeignKey foreignKey, in StoreObjectIdentifier storeObject, in StoreObjectIdentifier principalStoreObject) { @@ -99,7 +99,7 @@ public static string GetDefaultName([NotNull] this IForeignKey foreignKey) // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IForeignKey? linkedForeignKey = null; + IReadOnlyForeignKey? linkedForeignKey = null; foreach (var otherForeignKey in rootForeignKey.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())) @@ -190,7 +190,8 @@ public static void SetConstraintName([NotNull] this IMutableForeignKey foreignKe /// The foreign key. /// The foreign key constraints to which the foreign key is mapped. public static IEnumerable GetMappedConstraints([NotNull] this IForeignKey foreignKey) - => (IEnumerable?)foreignKey.FindRuntimeAnnotationValue(RelationalAnnotationNames.ForeignKeyMappings) + => (IEnumerable?)foreignKey.FindRuntimeAnnotationValue( + RelationalAnnotationNames.ForeignKeyMappings) ?? Enumerable.Empty(); /// @@ -205,8 +206,8 @@ public static IEnumerable GetMappedConstraints([NotNull] /// The foreign key. /// The identifier of the containing store object. /// The foreign key if found, or if none was found. - public static IForeignKey? FindSharedObjectRootForeignKey( - [NotNull] this IForeignKey foreignKey, + public static IReadOnlyForeignKey? FindSharedObjectRootForeignKey( + [NotNull] this IReadOnlyForeignKey foreignKey, in StoreObjectIdentifier storeObject) { Check.NotNull(foreignKey, nameof(foreignKey)); @@ -220,7 +221,7 @@ public static IEnumerable GetMappedConstraints([NotNull] // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IForeignKey? linkedForeignKey = null; + IReadOnlyForeignKey? linkedForeignKey = null; foreach (var otherForeignKey in rootForeignKey.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())) diff --git a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs index afd84227752..d85316a4d62 100644 --- a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for relational database metadata. + /// Index extension methods for relational database metadata. /// public static class RelationalIndexExtensions { @@ -25,7 +25,7 @@ public static class RelationalIndexExtensions /// /// The index. /// The name of the index in the database. - public static string GetDatabaseName([NotNull] this IIndex index) + public static string GetDatabaseName([NotNull] this IReadOnlyIndex index) => (string?)index[RelationalAnnotationNames.Name] ?? index.Name ?? index.GetDefaultDatabaseName(); @@ -45,7 +45,7 @@ public static string GetName([NotNull] this IIndex index) /// The index. /// The identifier of the store object. /// The name of the index in the database. - public static string? GetDatabaseName([NotNull] this IIndex index, in StoreObjectIdentifier storeObject) + public static string? GetDatabaseName([NotNull] this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) => (string?)index[RelationalAnnotationNames.Name] ?? index.Name ?? index.GetDefaultDatabaseName(storeObject); @@ -55,7 +55,7 @@ public static string GetName([NotNull] this IIndex index) /// /// The index. /// The default name that would be used for this index. - public static string GetDefaultDatabaseName([NotNull] this IIndex index) + public static string GetDefaultDatabaseName([NotNull] this IReadOnlyIndex index) { var tableName = index.DeclaringEntityType.GetTableName(); var schema = index.DeclaringEntityType.GetSchema(); @@ -84,7 +84,7 @@ public static string GetDefaultName([NotNull] this IIndex index) /// The index. /// The identifier of the store object. /// The default name that would be used for this index. - public static string? GetDefaultDatabaseName([NotNull] this IIndex index, in StoreObjectIdentifier storeObject) + public static string? GetDefaultDatabaseName([NotNull] this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) { var columnNames = index.Properties.GetColumnNames(storeObject); if (columnNames == null) @@ -98,7 +98,7 @@ public static string GetDefaultName([NotNull] this IIndex index) // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IIndex? linkedIndex = null; + IReadOnlyIndex? linkedIndex = null; foreach (var otherIndex in rootIndex.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetIndexes())) @@ -208,7 +208,7 @@ public static void SetName([NotNull] this IConventionIndex index, [CanBeNull] st /// /// The index. /// The index filter expression. - public static string? GetFilter([NotNull] this IIndex index) + public static string? GetFilter([NotNull] this IReadOnlyIndex index) => (string?)index.FindAnnotation(RelationalAnnotationNames.Filter)?.Value; /// @@ -217,7 +217,7 @@ public static void SetName([NotNull] this IConventionIndex index, [CanBeNull] st /// The index. /// The identifier of the containing store object. /// The index filter expression. - public static string? GetFilter([NotNull] this IIndex index, in StoreObjectIdentifier storeObject) + public static string? GetFilter([NotNull] this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) { var annotation = index.FindAnnotation(RelationalAnnotationNames.Filter); if (annotation != null) @@ -270,7 +270,8 @@ public static void SetFilter([NotNull] this IMutableIndex index, [CanBeNull] str /// The index. /// The table indexes to which the index is mapped. public static IEnumerable GetMappedTableIndexes([NotNull] this IIndex index) - => (IEnumerable?)index.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableIndexMappings) + => (IEnumerable?)index.FindRuntimeAnnotationValue( + RelationalAnnotationNames.TableIndexMappings) ?? Enumerable.Empty(); /// @@ -285,7 +286,7 @@ public static IEnumerable GetMappedTableIndexes([NotNull] this IInd /// The index. /// The identifier of the containing store object. /// The index found, or if none was found. - public static IIndex? FindSharedObjectRootIndex([NotNull] this IIndex index, in StoreObjectIdentifier storeObject) + public static IReadOnlyIndex? FindSharedObjectRootIndex([NotNull] this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) { Check.NotNull(index, nameof(index)); @@ -296,7 +297,7 @@ public static IEnumerable GetMappedTableIndexes([NotNull] this IInd // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IIndex? linkedIndex = null; + IReadOnlyIndex? linkedIndex = null; foreach (var otherIndex in rootIndex.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetIndexes())) diff --git a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs index 400589cbc17..83621765740 100644 --- a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for relational database metadata. + /// Key extension methods for relational database metadata. /// public static class RelationalKeyExtensions { @@ -24,7 +24,7 @@ public static class RelationalKeyExtensions /// /// The key. /// The key constraint name for this key. - public static string? GetName([NotNull] this IKey key) + public static string? GetName([NotNull] this IReadOnlyKey key) => key.GetName(StoreObjectIdentifier.Table(key.DeclaringEntityType.GetTableName()!, key.DeclaringEntityType.GetSchema())); /// @@ -33,7 +33,7 @@ public static class RelationalKeyExtensions /// The key. /// The identifier of the containing store object. /// The key constraint name for this key. - public static string? GetName([NotNull] this IKey key, in StoreObjectIdentifier storeObject) + public static string? GetName([NotNull] this IReadOnlyKey key, in StoreObjectIdentifier storeObject) => (string?)key[RelationalAnnotationNames.Name] ?? key.GetDefaultName(storeObject); @@ -42,7 +42,7 @@ public static class RelationalKeyExtensions /// /// The key. /// The default key constraint name that would be used for this key. - public static string GetDefaultName([NotNull] this IKey key) + public static string GetDefaultName([NotNull] this IReadOnlyKey key) { var tableName = key.DeclaringEntityType.GetTableName(); var name = key.IsPrimaryKey() @@ -63,7 +63,7 @@ public static string GetDefaultName([NotNull] this IKey key) /// The key. /// The identifier of the containing store object. /// The default key constraint name that would be used for this key. - public static string? GetDefaultName([NotNull] this IKey key, in StoreObjectIdentifier storeObject) + public static string? GetDefaultName([NotNull] this IReadOnlyKey key, in StoreObjectIdentifier storeObject) { string? name; if (key.IsPrimaryKey()) @@ -105,7 +105,7 @@ public static string GetDefaultName([NotNull] this IKey key) // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IKey? linkedKey = null; + IReadOnlyKey? linkedKey = null; foreach (var otherKey in rootKey.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetKeys())) @@ -184,7 +184,8 @@ public static void SetName([NotNull] this IMutableKey key, [CanBeNull] string? n /// The key. /// The unique constraints to which the key is mapped. public static IEnumerable GetMappedConstraints([NotNull] this IKey key) - => (IEnumerable?)key.FindRuntimeAnnotationValue(RelationalAnnotationNames.UniqueConstraintMappings) + => (IEnumerable?)key.FindRuntimeAnnotationValue( + RelationalAnnotationNames.UniqueConstraintMappings) ?? Enumerable.Empty(); /// @@ -199,7 +200,7 @@ public static IEnumerable GetMappedConstraints([NotNull] this /// The key. /// The identifier of the containing store object. /// The key found, or if none was found. - public static IKey? FindSharedObjectRootKey([NotNull] this IKey key, in StoreObjectIdentifier storeObject) + public static IReadOnlyKey? FindSharedObjectRootKey([NotNull] this IReadOnlyKey key, in StoreObjectIdentifier storeObject) { Check.NotNull(key, nameof(key)); @@ -210,7 +211,7 @@ public static IEnumerable GetMappedConstraints([NotNull] this // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IKey? linkedKey = null; + IReadOnlyKey? linkedKey = null; foreach (var otherKey in rootKey.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetKeys())) diff --git a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs index d50758ca98d..fe1f413bfc0 100644 --- a/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalModelExtensions.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Relational-specific extension methods for and extension methods for . + /// Relational-specific model extension methods. /// public static class RelationalModelExtensions { @@ -88,7 +88,7 @@ public static string ToDebugString( /// /// The model to get the default schema for. /// The default schema. - public static string? GetDefaultSchema([NotNull] this IModel model) + public static string? GetDefaultSchema([NotNull] this IReadOnlyModel model) => (string?)Check.NotNull(model, nameof(model))[RelationalAnnotationNames.DefaultSchema]; /// @@ -148,7 +148,7 @@ public static IRelationalModel GetRelationalModel([NotNull] this IModel model) /// /// The model to get the maximum identifier length for. /// The maximum identifier length. - public static int GetMaxIdentifierLength([NotNull] this IModel model) + public static int GetMaxIdentifierLength([NotNull] this IReadOnlyModel model) => (int?)Check.NotNull(model, nameof(model))[RelationalAnnotationNames.MaxIdentifierLength] ?? short.MaxValue; /// @@ -182,50 +182,69 @@ public static void SetMaxIdentifierLength([NotNull] this IMutableModel model, in => model.FindAnnotation(RelationalAnnotationNames.MaxIdentifierLength)?.GetConfigurationSource(); /// - /// Finds an with the given name. + /// Finds a sequence with the given name. /// /// The model to find the sequence in. /// The sequence name. /// The schema that contains the sequence. /// - /// The or if no sequence with the given name in + /// The sequence or if no sequence with the given name in /// the given schema was found. /// - public static ISequence? FindSequence([NotNull] this IModel model, [NotNull] string name, [CanBeNull] string? schema = null) + public static IReadOnlySequence? FindSequence( + [NotNull] this IReadOnlyModel model, + [NotNull] string name, + [CanBeNull] string? schema = null) => Sequence.FindSequence( Check.NotNull(model, nameof(model)), Check.NotEmpty(name, nameof(name)), Check.NullButNotEmpty(schema, nameof(schema))); /// - /// Finds an with the given name. + /// Finds a sequence with the given name. /// /// The model to find the sequence in. /// The sequence name. /// The schema that contains the sequence. /// - /// The or if no sequence with the given name in + /// The sequence or if no sequence with the given name in /// the given schema was found. /// public static IMutableSequence? FindSequence( [NotNull] this IMutableModel model, [NotNull] string name, [CanBeNull] string? schema = null) - => (IMutableSequence?)((IModel)model).FindSequence(name, schema); + => (IMutableSequence?)((IReadOnlyModel)model).FindSequence(name, schema); /// - /// Finds an with the given name. + /// Finds a sequence with the given name. /// /// The model to find the sequence in. /// The sequence name. /// The schema that contains the sequence. /// - /// The or if no sequence with the given name in + /// The sequence or if no sequence with the given name in /// the given schema was found. /// public static IConventionSequence? FindSequence( [NotNull] this IConventionModel model, [NotNull] string name, [CanBeNull] string? schema = null) - => (IConventionSequence?)((IModel)model).FindSequence(name, schema); + => (IConventionSequence?)((IReadOnlyModel)model).FindSequence(name, schema); + + /// + /// Finds a sequence with the given name. + /// + /// The model to find the sequence in. + /// The sequence name. + /// The schema that contains the sequence. + /// + /// The sequence or if no sequence with the given name in + /// the given schema was found. + /// + public static ISequence? FindSequence( + [NotNull] this IModel model, + [NotNull] string name, + [CanBeNull] string? schema = null) + => (ISequence?)((IReadOnlyModel)model).FindSequence(name, schema); /// /// Either returns the existing with the given name in the given schema @@ -292,84 +311,109 @@ public static IMutableSequence AddSequence( => Sequence.RemoveSequence((IMutableModel)Check.NotNull(model, nameof(model)), name, schema); /// - /// Returns all s contained in the model. + /// Returns all sequences contained in the model. /// /// The model to get the sequences in. public static IEnumerable GetSequences([NotNull] this IModel model) => Sequence.GetSequences(Check.NotNull(model, nameof(model))); /// - /// Returns all s contained in the model. + /// Returns all sequences contained in the model. /// /// The model to get the sequences in. public static IEnumerable GetSequences([NotNull] this IMutableModel model) => Sequence.GetSequences(Check.NotNull(model, nameof(model))); /// - /// Returns all s contained in the model. + /// 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))); /// - /// Finds a that is mapped to the method represented by the given . + /// Returns all sequences contained in the model. + /// + /// The model to get the sequences in. + public static IEnumerable GetSequences([NotNull] this IReadOnlyModel model) + => Sequence.GetSequences(Check.NotNull(model, nameof(model))); + + /// + /// Finds a function that is mapped to the method represented by the given . /// /// The model to find the function in. /// The for the method that is mapped to the function. - /// The or if the method is not mapped. - public static IDbFunction? FindDbFunction([NotNull] this IModel model, [NotNull] MethodInfo method) + /// The function or if the method is not mapped. + public static IReadOnlyDbFunction? FindDbFunction([NotNull] this IReadOnlyModel model, [NotNull] MethodInfo method) => DbFunction.FindDbFunction( Check.NotNull(model, nameof(model)), Check.NotNull(method, nameof(method))); /// - /// Finds a that is mapped to the method represented by the given . + /// Finds a function that is mapped to the method represented by the given . /// /// The model to find the function in. /// The for the method that is mapped to the function. - /// The or if the method is not mapped. + /// The function or if the method is not mapped. public static IMutableDbFunction? FindDbFunction([NotNull] this IMutableModel model, [NotNull] MethodInfo method) - => (IMutableDbFunction?)((IModel)model).FindDbFunction(method); + => (IMutableDbFunction?)((IReadOnlyModel)model).FindDbFunction(method); /// - /// Finds a that is mapped to the method represented by the given . + /// Finds a function that is mapped to the method represented by the given . /// /// The model to find the function in. /// The for the method that is mapped to the function. - /// The or if the method is not mapped. + /// The function or if the method is not mapped. public static IConventionDbFunction? FindDbFunction([NotNull] this IConventionModel model, [NotNull] MethodInfo method) - => (IConventionDbFunction?)((IModel)model).FindDbFunction(method); + => (IConventionDbFunction?)((IReadOnlyModel)model).FindDbFunction(method); /// - /// Finds an that is mapped to the method represented by the given name. + /// Finds a function that is mapped to the method represented by the given . + /// + /// The model to find the function in. + /// The for the method that is mapped to the function. + /// The function or if the method is not mapped. + public static IDbFunction? FindDbFunction([NotNull] this IModel model, [NotNull] MethodInfo method) + => (IDbFunction?)((IReadOnlyModel)model).FindDbFunction(method); + + /// + /// Finds a function that is mapped to the method represented by the given name. /// /// The model to find the function in. /// The model name of the function. - /// The or if the method is not mapped. - public static IDbFunction? FindDbFunction([NotNull] this IModel model, [NotNull] string name) + /// The function or if the method is not mapped. + public static IReadOnlyDbFunction? FindDbFunction([NotNull] this IReadOnlyModel model, [NotNull] string name) => DbFunction.FindDbFunction( Check.NotNull(model, nameof(model)), Check.NotNull(name, nameof(name))); /// - /// Finds an that is mapped to the method represented by the given name. + /// Finds a function that is mapped to the method represented by the given name. /// /// The model to find the function in. /// The model name of the function. - /// The or if the method is not mapped. + /// The function or if the method is not mapped. public static IMutableDbFunction? FindDbFunction([NotNull] this IMutableModel model, [NotNull] string name) => (IMutableDbFunction?)((IModel)model).FindDbFunction(name); /// - /// Finds an that is mapped to the method represented by the given name. + /// Finds a function that is mapped to the method represented by the given name. /// /// The model to find the function in. /// The model name of the function. - /// The or if the method is not mapped. + /// The function or if the method is not mapped. public static IConventionDbFunction? FindDbFunction([NotNull] this IConventionModel model, [NotNull] string name) => (IConventionDbFunction?)((IModel)model).FindDbFunction(name); + /// + /// Finds a function that is mapped to the method represented by the given name. + /// + /// The model to find the function in. + /// The model name of the function. + /// The function or if the method is not mapped. + public static IDbFunction? FindDbFunction([NotNull] this IModel model, [NotNull] string name) + => (IDbFunction?)((IReadOnlyModel)model).FindDbFunction(name); + /// /// Creates an mapped to the given method. /// @@ -381,12 +425,12 @@ public static IMutableDbFunction AddDbFunction([NotNull] this IMutableModel mode model, Check.NotNull(methodInfo, nameof(methodInfo)), ConfigurationSource.Explicit)!; /// - /// Creates an mapped to the given method. + /// Creates a function mapped to the given method. /// /// The model to add the function to. /// The for the method that is mapped to the function. /// Indicates whether the configuration was specified using a data annotation. - /// The new . + /// The new function. public static IConventionDbFunction AddDbFunction( [NotNull] this IConventionModel model, [NotNull] MethodInfo methodInfo, @@ -396,12 +440,12 @@ public static IConventionDbFunction AddDbFunction( fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - /// Creates an . + /// Creates a function. /// /// The model to add the function to. /// The model name of the function. /// The function return type. - /// The new . + /// The new function. public static IMutableDbFunction AddDbFunction( [NotNull] this IMutableModel model, [NotNull] string name, @@ -410,13 +454,13 @@ public static IMutableDbFunction AddDbFunction( model, Check.NotNull(name, nameof(name)), returnType, ConfigurationSource.Explicit); /// - /// Creates an . + /// Creates a function. /// /// The model to add the function to. /// The model name of the function. /// The function return type. /// Indicates whether the configuration was specified using a data annotation. - /// The new . + /// The new function. public static IConventionDbFunction AddDbFunction( [NotNull] this IConventionModel model, [NotNull] string name, @@ -429,76 +473,83 @@ public static IConventionDbFunction AddDbFunction( fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - /// Removes the that is mapped to the method represented by the given + /// Removes the function that is mapped to the method represented by the given /// . /// /// The model to find the function in. /// The for the method that is mapped to the function. - /// The removed or if the method is not mapped. + /// The removed function or if the method is not mapped. public static IMutableDbFunction? RemoveDbFunction([NotNull] this IMutableModel model, [NotNull] MethodInfo method) => DbFunction.RemoveDbFunction( Check.NotNull(model, nameof(model)), Check.NotNull(method, nameof(method))); /// - /// Removes the that is mapped to the method represented by the given + /// Removes the function that is mapped to the method represented by the given /// . /// /// The model to find the function in. /// The for the method that is mapped to the function. - /// The removed or if the method is not mapped. + /// The removed function or if the method is not mapped. public static IConventionDbFunction? RemoveDbFunction([NotNull] this IConventionModel model, [NotNull] MethodInfo method) => (IConventionDbFunction?)((IMutableModel)model).RemoveDbFunction(method); /// - /// Removes the that is mapped to the method represented by the given + /// Removes the function that is mapped to the method represented by the given /// . /// /// The model to find the function in. /// The model name of the function. - /// The removed or if the method is not mapped. + /// The removed function or if the method is not mapped. public static IMutableDbFunction? RemoveDbFunction([NotNull] this IMutableModel model, [NotNull] string name) => DbFunction.RemoveDbFunction( Check.NotNull(model, nameof(model)), Check.NotNull(name, nameof(name))); /// - /// Removes the that is mapped to the method represented by the given + /// Removes the function that is mapped to the method represented by the given /// . /// /// The model to find the function in. /// The model name of the function. - /// The removed or if the method is not mapped. + /// The removed function or if the method is not mapped. public static IConventionDbFunction? RemoveDbFunction([NotNull] this IConventionModel model, [NotNull] string name) => (IConventionDbFunction?)((IMutableModel)model).RemoveDbFunction(name); /// - /// Returns all s contained in the model. + /// Returns all functions contained in the model. /// /// The model to get the functions in. - public static IEnumerable GetDbFunctions([NotNull] this IModel model) + public static IEnumerable GetDbFunctions([NotNull] this IModel model) => DbFunction.GetDbFunctions(Check.NotNull(model, nameof(model))); /// - /// Returns all s contained in the model. + /// Returns all functions contained in the model. /// /// The model to get the functions in. public static IEnumerable GetDbFunctions([NotNull] this IMutableModel model) => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))); /// - /// Returns all s contained in the model. + /// 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))); + /// + /// Returns all functions contained in the model. + /// + /// The model to get the functions in. + public static IEnumerable GetDbFunctions([NotNull] this IReadOnlyModel model) + => DbFunction.GetDbFunctions((Model)Check.NotNull(model, nameof(model))); + /// /// Returns the database collation. /// /// The model to get the collation for. /// The collation. - public static string? GetCollation([NotNull] this IModel model) + public static string? GetCollation([NotNull] this IReadOnlyModel model) => (string?)model[RelationalAnnotationNames.Collation]; /// diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs index fac0596b325..493d0a8231e 100644 --- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for relational database metadata. + /// Property extension methods for relational database metadata. /// public static class RelationalPropertyExtensions { @@ -39,7 +39,7 @@ public static string GetColumnName([NotNull] this IProperty property) /// /// The property. /// The the base name of the column to which the property would be mapped. - public static string GetColumnBaseName([NotNull] this IProperty property) + public static string GetColumnBaseName([NotNull] this IReadOnlyProperty property) => (string?)property.FindAnnotation(RelationalAnnotationNames.ColumnName)?.Value ?? property.GetDefaultColumnBaseName(); /// @@ -48,7 +48,7 @@ public static string GetColumnBaseName([NotNull] this IProperty property) /// The property. /// The identifier of the table-like store object containing the column. /// The name of the column to which the property is mapped. - public static string? GetColumnName([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetColumnName([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -110,7 +110,7 @@ public static string GetDefaultColumnName([NotNull] this IProperty property) /// /// The property. /// The default base column name to which the property would be mapped. - public static string GetDefaultColumnBaseName([NotNull] this IProperty property) + public static string GetDefaultColumnBaseName([NotNull] this IReadOnlyProperty property) => Uniquifier.Truncate(property.Name, property.DeclaringEntityType.Model.GetMaxIdentifierLength()); /// @@ -119,7 +119,7 @@ public static string GetDefaultColumnBaseName([NotNull] this IProperty property) /// The property. /// The identifier of the table-like store object containing the column. /// The default column name to which the property would be mapped. - public static string GetDefaultColumnName([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string GetDefaultColumnName([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var sharedTablePrincipalPrimaryKeyProperty = FindSharedObjectRootPrimaryKeyProperty(property, storeObject); if (sharedTablePrincipalPrimaryKeyProperty != null) @@ -285,7 +285,7 @@ public static void SetColumnName( /// /// The property. /// The database type of the column to which the property is mapped. - public static string? GetColumnType([NotNull] this IProperty property) + public static string? GetColumnType([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -299,7 +299,7 @@ public static void SetColumnName( /// The property. /// The identifier of the table-like store object containing the column. /// The database type of the column to which the property is mapped. - public static string? GetColumnType([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetColumnType([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.ColumnType); if (annotation != null) @@ -310,7 +310,7 @@ public static void SetColumnName( return GetDefaultColumnType(property, storeObject); } - private static string? GetDefaultColumnType(IProperty property, in StoreObjectIdentifier storeObject) + private static string? GetDefaultColumnType(IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var sharedTableRootProperty = property.FindSharedStoreObjectRootProperty(storeObject); return sharedTableRootProperty != null @@ -362,7 +362,8 @@ public static void SetColumnType([NotNull] this IMutableProperty property, [CanB /// The property. /// The default columns to which the property would be mapped. public static IEnumerable GetDefaultColumnMappings([NotNull] this IProperty property) - => (IEnumerable?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultColumnMappings) + => (IEnumerable?)property.FindRuntimeAnnotationValue( + RelationalAnnotationNames.DefaultColumnMappings) ?? Enumerable.Empty(); /// @@ -371,7 +372,8 @@ public static IEnumerable GetDefaultColumnMappings([NotNull] /// The property. /// The table columns to which the property is mapped. public static IEnumerable GetTableColumnMappings([NotNull] this IProperty property) - => (IEnumerable?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableColumnMappings) + => (IEnumerable?)property.FindRuntimeAnnotationValue( + RelationalAnnotationNames.TableColumnMappings) ?? Enumerable.Empty(); /// @@ -380,7 +382,8 @@ public static IEnumerable GetTableColumnMappings([NotNull] this /// The property. /// The view columns to which the property is mapped. public static IEnumerable GetViewColumnMappings([NotNull] this IProperty property) - => (IEnumerable?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewColumnMappings) + => (IEnumerable?)property.FindRuntimeAnnotationValue( + RelationalAnnotationNames.ViewColumnMappings) ?? Enumerable.Empty(); /// @@ -389,7 +392,8 @@ public static IEnumerable GetViewColumnMappings([NotNull] th /// The property. /// The SQL query columns to which the property is mapped. public static IEnumerable GetSqlQueryColumnMappings([NotNull] this IProperty property) - => (IEnumerable?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.SqlQueryColumnMappings) + => (IEnumerable?)property.FindRuntimeAnnotationValue( + RelationalAnnotationNames.SqlQueryColumnMappings) ?? Enumerable.Empty(); /// @@ -398,7 +402,8 @@ public static IEnumerable GetSqlQueryColumnMappings([Not /// The property. /// The function columns to which the property is mapped. public static IEnumerable GetFunctionColumnMappings([NotNull] this IProperty property) - => (IEnumerable?)property.FindRuntimeAnnotationValue(RelationalAnnotationNames.FunctionColumnMappings) + => (IEnumerable?)property.FindRuntimeAnnotationValue( + RelationalAnnotationNames.FunctionColumnMappings) ?? Enumerable.Empty(); /// @@ -461,7 +466,7 @@ public static IEnumerable GetFunctionColumnMappings([Not /// /// The property. /// The SQL expression that is used as the default value for the column this property is mapped to. - public static string? GetDefaultValueSql([NotNull] this IProperty property) + public static string? GetDefaultValueSql([NotNull] this IReadOnlyProperty property) => (string?)property.FindAnnotation(RelationalAnnotationNames.DefaultValueSql)?.Value; /// @@ -470,7 +475,7 @@ public static IEnumerable GetFunctionColumnMappings([Not /// The property. /// The identifier of the table-like store object containing the column. /// The SQL expression that is used as the default value for the column this property is mapped to. - public static string? GetDefaultValueSql([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetDefaultValueSql([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.DefaultValueSql); if (annotation != null) @@ -530,7 +535,7 @@ public static void SetDefaultValueSql([NotNull] this IMutableProperty property, /// /// The property. /// The SQL expression that is used as the computed value for the column this property is mapped to. - public static string? GetComputedColumnSql([NotNull] this IProperty property) + public static string? GetComputedColumnSql([NotNull] this IReadOnlyProperty property) => (string?)property.FindAnnotation(RelationalAnnotationNames.ComputedColumnSql)?.Value; /// @@ -539,7 +544,7 @@ public static void SetDefaultValueSql([NotNull] this IMutableProperty property, /// The property. /// The identifier of the table-like store object containing the column. /// The SQL expression that is used as the computed value for the column this property is mapped to. - public static string? GetComputedColumnSql([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetComputedColumnSql([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.ComputedColumnSql); if (annotation != null) @@ -603,7 +608,7 @@ public static void SetComputedColumnSql([NotNull] this IMutableProperty property /// Whether the value of the computed column this property is mapped to is stored in the database, /// or calculated when it is read. /// - public static bool? GetIsStored([NotNull] this IProperty property) + public static bool? GetIsStored([NotNull] this IReadOnlyProperty property) => (bool?)property.FindAnnotation(RelationalAnnotationNames.IsStored)?.Value; /// @@ -616,7 +621,7 @@ public static void SetComputedColumnSql([NotNull] this IMutableProperty property /// Whether the value of the computed column this property is mapped to is stored in the database, /// or calculated when it is read. /// - public static bool? GetIsStored([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static bool? GetIsStored([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.IsStored); if (annotation != null) @@ -673,7 +678,7 @@ public static void SetIsStored([NotNull] this IMutableProperty property, bool? v /// /// The property. /// The object that is used as the default value for the column this property is mapped to. - public static object? GetDefaultValue([NotNull] this IProperty property) + public static object? GetDefaultValue([NotNull] this IReadOnlyProperty property) => property.FindAnnotation(RelationalAnnotationNames.DefaultValue)?.Value; /// @@ -682,7 +687,7 @@ public static void SetIsStored([NotNull] this IMutableProperty property, bool? v /// The property. /// The identifier of the table-like store object containing the column. /// The object that is used as the default value for the column this property is mapped to. - public static object? GetDefaultValue([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static object? GetDefaultValue([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.DefaultValue); if (annotation != null) @@ -725,7 +730,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca return value; } - private static object? ConvertDefaultValue([NotNull] IProperty property, [CanBeNull] object? value) + private static object? ConvertDefaultValue([NotNull] IReadOnlyProperty property, [CanBeNull] object? value) { if (value == null || value == DBNull.Value) @@ -766,7 +771,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// The property. /// The identifier of the table-like store object containing the column. /// The maximum length, or if none if defined. - public static int? GetMaxLength([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static int? GetMaxLength([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -787,7 +792,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// The property. /// The identifier of the table-like store object containing the column. /// The precision, or if none is defined. - public static int? GetPrecision([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static int? GetPrecision([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -808,7 +813,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// The property. /// The identifier of the table-like store object containing the column. /// The scale, or if none is defined. - public static int? GetScale([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static int? GetScale([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -828,7 +833,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// The property. /// The identifier of the table-like store object containing the column. /// The Unicode setting, or if none is defined. - public static bool? IsUnicode([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static bool? IsUnicode([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -847,7 +852,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// /// The property. /// A flag indicating if the property as capable of storing only fixed-length data, such as strings. - public static bool? IsFixedLength([NotNull] this IProperty property) + public static bool? IsFixedLength([NotNull] this IReadOnlyProperty property) => (bool?)property.FindAnnotation(RelationalAnnotationNames.IsFixedLength)?.Value; /// @@ -856,7 +861,7 @@ public static void SetDefaultValue([NotNull] this IMutableProperty property, [Ca /// The property. /// The identifier of the table-like store object containing the column. /// A flag indicating if the property as capable of storing only fixed-length data, such as strings. - public static bool? IsFixedLength([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static bool? IsFixedLength([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.IsFixedLength); if (annotation != null) @@ -899,10 +904,10 @@ public static void SetIsFixedLength([NotNull] this IMutableProperty property, bo } /// - /// Gets the for . + /// Gets the for . /// /// The property. - /// The for . + /// The for . public static ConfigurationSource? GetIsFixedLengthConfigurationSource([NotNull] this IConventionProperty property) => property.FindAnnotation(RelationalAnnotationNames.IsFixedLength)?.GetConfigurationSource(); @@ -917,15 +922,15 @@ public static void SetIsFixedLength([NotNull] this IMutableProperty property, bo /// As well as properties on optional types sharing the same table. /// /// - /// The . + /// The . /// if the mapped column is nullable; otherwise. - public static bool IsColumnNullable([NotNull] this IProperty property) + public static bool IsColumnNullable([NotNull] this IReadOnlyProperty property) => property.IsNullable || (property.DeclaringEntityType.BaseType != null && property.DeclaringEntityType.GetDiscriminatorProperty() != null); /// /// - /// Checks whether the column mapped to the given will be nullable + /// Checks whether the column mapped to the given property will be nullable /// when created in the database. /// /// @@ -934,10 +939,10 @@ public static bool IsColumnNullable([NotNull] this IProperty property) /// As well as properties on optional types sharing the same table. /// /// - /// The . + /// The property. /// The identifier of the table-like store object containing the column. /// if the mapped column is nullable; otherwise. - public static bool IsColumnNullable([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static bool IsColumnNullable([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { if (property.IsPrimaryKey()) { @@ -955,7 +960,7 @@ public static bool IsColumnNullable([NotNull] this IProperty property, in StoreO || IsOptionalSharingDependent(property.DeclaringEntityType, storeObject, 0); } - private static bool IsOptionalSharingDependent(IEntityType entityType, in StoreObjectIdentifier storeObject, int recursionDepth) + private static bool IsOptionalSharingDependent(IReadOnlyEntityType entityType, in StoreObjectIdentifier storeObject, int recursionDepth) { if (recursionDepth++ == Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable) { @@ -978,7 +983,7 @@ private static bool IsOptionalSharingDependent(IEntityType entityType, in StoreO /// /// The property. /// The comment for the column this property is mapped to. - public static string? GetComment([NotNull] this IProperty property) + public static string? GetComment([NotNull] this IReadOnlyProperty property) => (string?)property.FindAnnotation(RelationalAnnotationNames.Comment)?.Value; /// @@ -987,7 +992,7 @@ private static bool IsOptionalSharingDependent(IEntityType entityType, in StoreO /// The property. /// The identifier of the table-like store object containing the column. /// The comment for the column this property is mapped to. - public static string? GetComment([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetComment([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.Comment); if (annotation != null) @@ -1043,7 +1048,7 @@ public static void SetComment([NotNull] this IMutableProperty property, [CanBeNu /// /// The property. /// The collation for the column this property is mapped to. - public static string? GetCollation([NotNull] this IProperty property) + public static string? GetCollation([NotNull] this IReadOnlyProperty property) => (string?)property.FindAnnotation(RelationalAnnotationNames.Collation)?.Value; /// @@ -1052,7 +1057,7 @@ public static void SetComment([NotNull] this IMutableProperty property, [CanBeNu /// The property. /// The identifier of the table-like store object containing the column. /// The collation for the column this property is mapped to. - public static string? GetCollation([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetCollation([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(RelationalAnnotationNames.Collation); if (annotation != null) @@ -1107,7 +1112,7 @@ public static void SetCollation([NotNull] this IMutableProperty property, [CanBe /// The property. /// The type mapping. [DebuggerStepThrough] - public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IProperty property) + public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IReadOnlyProperty property) => (RelationalTypeMapping)property.GetTypeMapping(); /// @@ -1126,7 +1131,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro /// The property. /// The type mapping, or if none was found. [DebuggerStepThrough] - public static RelationalTypeMapping? FindRelationalTypeMapping([NotNull] this IProperty property) + public static RelationalTypeMapping? FindRelationalTypeMapping([NotNull] this IReadOnlyProperty property) => (RelationalTypeMapping?)property.FindTypeMapping(); /// @@ -1136,7 +1141,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro /// The identifier of the table-like store object containing the column. /// The type mapping, or if none was found. public static RelationalTypeMapping? FindRelationalTypeMapping( - [NotNull] this IProperty property, + [NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) => property.FindRelationalTypeMapping(); @@ -1152,8 +1157,8 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro /// The property. /// The identifier of the table-like store object containing the column. /// The property found, or if none was found. - public static IProperty? FindSharedStoreObjectRootProperty( - [NotNull] this IProperty property, + public static IReadOnlyProperty? FindSharedStoreObjectRootProperty( + [NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) => FindSharedObjectRootProperty(property, storeObject); @@ -1191,7 +1196,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro in StoreObjectIdentifier storeObject) => (IConventionProperty?)FindSharedObjectRootProperty(property, storeObject); - private static IProperty? FindSharedObjectRootProperty([NotNull] IProperty property, in StoreObjectIdentifier storeObject) + private static IReadOnlyProperty? FindSharedObjectRootProperty([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { Check.NotNull(property, nameof(property)); @@ -1210,7 +1215,7 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro // Using a hashset is detrimental to the perf when there are no cycles for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) { - IProperty? linkedProperty = null; + IReadOnlyProperty? linkedProperty = null; foreach (var p in rootProperty.DeclaringEntityType .FindRowInternalForeignKeys(storeObject) .SelectMany(fk => fk.PrincipalEntityType.GetProperties())) @@ -1233,7 +1238,8 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro return rootProperty == property ? null : rootProperty; } - private static IProperty? FindSharedObjectRootPrimaryKeyProperty([NotNull] IProperty property, in StoreObjectIdentifier storeObject) + private static IReadOnlyProperty? FindSharedObjectRootPrimaryKeyProperty( + [NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { if (!property.IsPrimaryKey()) { @@ -1259,8 +1265,8 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro return principalProperty == property ? null : principalProperty; } - private static IProperty? FindSharedObjectRootConcurrencyTokenProperty( - [NotNull] IProperty property, + private static IReadOnlyProperty? FindSharedObjectRootConcurrencyTokenProperty( + [NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { if (!property.IsConcurrencyToken) @@ -1291,6 +1297,21 @@ public static RelationalTypeMapping GetRelationalTypeMapping([NotNull] this IPro return principalProperty == property ? null : principalProperty; } + /// + /// + /// Returns the property facet overrides for a particular table-like store object. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The property. + /// The identifier of the table-like store object containing the column. + /// An object that stores property facet overrides. + public static IReadOnlyAnnotatable? FindOverrides([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) + => RelationalPropertyOverrides.Find(property, storeObject); + /// /// /// Returns the property facet overrides for a particular table-like store object. diff --git a/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs b/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs index 3e04b5ea2fb..f6e3ecb4d40 100644 --- a/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs +++ b/src/EFCore.Relational/Infrastructure/ModelSnapshot.cs @@ -19,7 +19,7 @@ private IModel CreateModel() BuildModel(modelBuilder); - return modelBuilder.Model; + return (IModel)modelBuilder.Model; } /// diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs b/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs index a3724518fa0..202a4e3fa09 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelRuntimeInitializer.cs @@ -63,7 +63,7 @@ protected override void InitializeModel(IModel model, bool preValidation) } else { - RelationalModel.Add((IConventionModel)model, RelationalDependencies.RelationalAnnotationProvider); + RelationalModel.Add(model, RelationalDependencies.RelationalAnnotationProvider); } } } diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index fe765fb5ccf..54668c56697 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -249,13 +249,13 @@ protected virtual void ValidateDefaultValuesOnKeys( [NotNull] IModel model, [NotNull] IDiagnosticsLogger logger) { - foreach (var entityType in ((IConventionModel)model).GetEntityTypes()) + foreach (var entityType in model.GetEntityTypes()) { foreach (var key in entityType.GetDeclaredKeys()) { foreach (var property in key.Properties) { - var defaultValue = property.FindAnnotation(RelationalAnnotationNames.DefaultValue); + var defaultValue = (IConventionAnnotation)property.FindAnnotation(RelationalAnnotationNames.DefaultValue); if (defaultValue?.Value != null && defaultValue.GetConfigurationSource().Overrides(ConfigurationSource.DataAnnotation)) { diff --git a/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs index d0a35964017..dafbc5e18fb 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalPropertyExtensions.cs @@ -22,7 +22,7 @@ public static class RelationalPropertyExtensions /// The identifier of the table-like store object containing the column. /// A comma-separated list of column names. public static string FormatColumns( - [NotNull] this IEnumerable properties, + [NotNull] this IEnumerable properties, StoreObjectIdentifier storeObject) => "{" + string.Join(", ", properties.Select(p => "'" + p.GetColumnName(storeObject) + "'")) + "}"; @@ -33,7 +33,7 @@ public static string FormatColumns( /// The identifier of the table-like store object containing the column. /// A list of column names. public static IReadOnlyList? GetColumnNames( - [NotNull] this IEnumerable properties, + [NotNull] this IEnumerable properties, in StoreObjectIdentifier storeObject) { var propertyNames = new List(); diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalModelConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalModelConvention.cs index f65bb333161..9af8b96d1c5 100644 --- a/src/EFCore.Relational/Metadata/Conventions/RelationalModelConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/RelationalModelConvention.cs @@ -35,8 +35,6 @@ public RelationalModelConvention( /// public virtual IModel ProcessModelFinalized(IModel model) - => model is IConventionModel conventionModel - ? RelationalModel.Add(conventionModel, RelationalDependencies.RelationalAnnotationProvider) - : model; + => RelationalModel.Add(model, RelationalDependencies.RelationalAnnotationProvider); } } diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs index a2be599a241..011a8de2c77 100644 --- a/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/RelationalValueGenerationConvention.cs @@ -150,7 +150,7 @@ private void ProcessTableChanged( /// The property. /// The identifier of the store object. /// The new store value generation strategy to set for the given property. - public static ValueGenerated? GetValueGenerated([NotNull] IProperty property, in StoreObjectIdentifier storeObject) + public static ValueGenerated? GetValueGenerated([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var valueGenerated = GetValueGenerated(property); return valueGenerated diff --git a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs index b4531f39857..a56aedeada5 100644 --- a/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/SharedTableConvention.cs @@ -304,8 +304,8 @@ private void TryUniquifyKeyNames( /// The identifier of the store object. /// if compatible protected virtual bool AreCompatible( - [NotNull] IKey key, - [NotNull] IKey duplicateKey, + [NotNull] IReadOnlyKey key, + [NotNull] IReadOnlyKey duplicateKey, in StoreObjectIdentifier storeObject) => key.AreCompatible(duplicateKey, storeObject, shouldThrow: false); @@ -374,8 +374,8 @@ private void TryUniquifyIndexNames( /// The identifier of the store object. /// if compatible protected virtual bool AreCompatible( - [NotNull] IIndex index, - [NotNull] IIndex duplicateIndex, + [NotNull] IReadOnlyIndex index, + [NotNull] IReadOnlyIndex duplicateIndex, in StoreObjectIdentifier storeObject) => index.AreCompatible(duplicateIndex, storeObject, shouldThrow: false); @@ -465,8 +465,8 @@ private void TryUniquifyForeignKeyNames( /// The identifier of the store object. /// if compatible protected virtual bool AreCompatible( - [NotNull] IForeignKey foreignKey, - [NotNull] IForeignKey duplicateForeignKey, + [NotNull] IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyForeignKey duplicateForeignKey, in StoreObjectIdentifier storeObject) => foreignKey.AreCompatible(duplicateForeignKey, storeObject, shouldThrow: false); diff --git a/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs b/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs index 14331c19421..68480f9c269 100644 --- a/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/TableSharingConcurrencyTokenConvention.cs @@ -80,7 +80,7 @@ public virtual void ProcessModelFinalizing( var concurrencyColumnName = concurrencyColumn.Key; var propertiesMappedToConcurrencyColumn = concurrencyColumn.Value; - Dictionary? entityTypesMissingConcurrencyColumn = null; + Dictionary? entityTypesMissingConcurrencyColumn = null; foreach (var entityType in mappedTypes) { var foundMappedProperty = !IsConcurrencyTokenMissing(propertiesMappedToConcurrencyColumn, entityType, mappedTypes) @@ -91,7 +91,7 @@ public virtual void ProcessModelFinalizing( { if (entityTypesMissingConcurrencyColumn == null) { - entityTypesMissingConcurrencyColumn = new Dictionary(); + entityTypesMissingConcurrencyColumn = new Dictionary(); } // store the entity type which is missing the @@ -133,16 +133,16 @@ public virtual void ProcessModelFinalizing( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [EntityFrameworkInternal] - public static Dictionary>? GetConcurrencyTokensMap( + public static Dictionary>? GetConcurrencyTokensMap( in StoreObjectIdentifier storeObject, - [NotNull] IReadOnlyList mappedTypes) + [NotNull] IReadOnlyList mappedTypes) { if (mappedTypes.Count < 2) { return null; } - Dictionary>? concurrencyColumns = null; + Dictionary>? concurrencyColumns = null; var nonHierarchyTypesCount = 0; foreach (var entityType in mappedTypes) { @@ -168,12 +168,12 @@ public virtual void ProcessModelFinalizing( if (concurrencyColumns == null) { - concurrencyColumns = new Dictionary>(); + concurrencyColumns = new Dictionary>(); } if (!concurrencyColumns.TryGetValue(columnName, out var properties)) { - properties = new List(); + properties = new List(); concurrencyColumns[columnName] = properties; } @@ -192,9 +192,9 @@ public virtual void ProcessModelFinalizing( /// [EntityFrameworkInternal] public static bool IsConcurrencyTokenMissing( - [NotNull] List propertiesMappedToConcurrencyColumn, - [NotNull] IEntityType entityType, - [NotNull] IReadOnlyList mappedTypes) + [NotNull] List propertiesMappedToConcurrencyColumn, + [NotNull] IReadOnlyEntityType entityType, + [NotNull] IReadOnlyList mappedTypes) { if (entityType.FindPrimaryKey() == null) { diff --git a/src/EFCore.Relational/Metadata/DbFunctionExtensions.cs b/src/EFCore.Relational/Metadata/DbFunctionExtensions.cs index d0fee4979f8..7b2c85b1fb2 100644 --- a/src/EFCore.Relational/Metadata/DbFunctionExtensions.cs +++ b/src/EFCore.Relational/Metadata/DbFunctionExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Extension methods for . + /// Extension methods for . /// public static class DbFunctionExtensions { @@ -29,7 +29,7 @@ public static class DbFunctionExtensions /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IDbFunction function, + [NotNull] this IReadOnlyDbFunction function, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore.Relational/Metadata/DbFunctionParameterExtensions.cs b/src/EFCore.Relational/Metadata/DbFunctionParameterExtensions.cs index 32a47ff3e65..43d77e6c403 100644 --- a/src/EFCore.Relational/Metadata/DbFunctionParameterExtensions.cs +++ b/src/EFCore.Relational/Metadata/DbFunctionParameterExtensions.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Extension methods for . + /// Extension methods for . /// public static class DbFunctionParameterExtensions { @@ -28,7 +28,7 @@ public static class DbFunctionParameterExtensions /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IDbFunctionParameter parameter, + [NotNull] this IReadOnlyDbFunctionParameter parameter, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore.Relational/Metadata/ICheckConstraint.cs b/src/EFCore.Relational/Metadata/ICheckConstraint.cs index ec161778d92..9551015debd 100644 --- a/src/EFCore.Relational/Metadata/ICheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/ICheckConstraint.cs @@ -10,21 +10,11 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a check constraint in the . /// - public interface ICheckConstraint : IAnnotatable + public interface ICheckConstraint : IReadOnlyCheckConstraint, IAnnotatable { - /// - /// Gets the name of the check constraint in the database. - /// - string Name { get; } - /// /// Gets the in which this check constraint is defined. /// - IEntityType EntityType { get; } - - /// - /// Gets the constraint sql used in a check constraint in the database. - /// - string Sql { get; } + new IEntityType EntityType { get; } } } diff --git a/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs b/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs index 25d148648fc..94f8c8edbb3 100644 --- a/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/IConventionCheckConstraint.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a check constraint in the . /// - public interface IConventionCheckConstraint : ICheckConstraint, IConventionAnnotatable + public interface IConventionCheckConstraint : IReadOnlyCheckConstraint, IConventionAnnotatable { /// /// Gets the in which this check constraint is defined. diff --git a/src/EFCore.Relational/Metadata/IConventionDbFunction.cs b/src/EFCore.Relational/Metadata/IConventionDbFunction.cs index 3d44c873c4c..9e250509a61 100644 --- a/src/EFCore.Relational/Metadata/IConventionDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IConventionDbFunction.cs @@ -16,7 +16,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. /// - public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction + public interface IConventionDbFunction : IReadOnlyDbFunction, IConventionAnnotatable { /// /// Gets the in which this function is defined. @@ -44,9 +44,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction string? SetName([CanBeNull] string? name, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetNameConfigurationSource(); /// @@ -58,9 +58,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction string? SetSchema([CanBeNull] string? schema, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetSchemaConfigurationSource(); /// @@ -72,9 +72,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction bool SetIsBuiltIn(bool builtIn, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsBuiltInConfigurationSource(); /// @@ -86,9 +86,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction bool SetIsNullable(bool nullable, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsNullableConfigurationSource(); /// @@ -100,9 +100,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction string? SetStoreType([CanBeNull] string? storeType, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetStoreTypeConfigurationSource(); /// @@ -114,9 +114,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction RelationalTypeMapping? SetTypeMapping([CanBeNull] RelationalTypeMapping? typeMapping, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetTypeMappingConfigurationSource(); /// @@ -132,9 +132,9 @@ public interface IConventionDbFunction : IConventionAnnotatable, IDbFunction bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetTranslationConfigurationSource(); /// diff --git a/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs index f101c6210fe..ec0d0eb4e0e 100644 --- a/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a parameter. /// - public interface IConventionDbFunctionParameter : IConventionAnnotatable, IDbFunctionParameter + public interface IConventionDbFunctionParameter : IConventionAnnotatable, IReadOnlyDbFunctionParameter { /// /// The to which this parameter belongs. @@ -38,9 +38,9 @@ public interface IConventionDbFunctionParameter : IConventionAnnotatable, IDbFun string? SetStoreType([CanBeNull] string? storeType, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetStoreTypeConfigurationSource(); /// @@ -51,9 +51,9 @@ public interface IConventionDbFunctionParameter : IConventionAnnotatable, IDbFun RelationalTypeMapping? SetTypeMapping([CanBeNull] RelationalTypeMapping? typeMapping, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetTypeMappingConfigurationSource(); } } diff --git a/src/EFCore.Relational/Metadata/IConventionSequence.cs b/src/EFCore.Relational/Metadata/IConventionSequence.cs index cb93b138d8c..85703b6b276 100644 --- a/src/EFCore.Relational/Metadata/IConventionSequence.cs +++ b/src/EFCore.Relational/Metadata/IConventionSequence.cs @@ -10,10 +10,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a database sequence in the in a form that + /// Represents a database sequence in the model in a form that /// can be mutated while building the model. /// - public interface IConventionSequence : ISequence, IConventionAnnotatable + public interface IConventionSequence : IReadOnlySequence, IConventionAnnotatable { /// /// Gets the in which this sequence is defined. @@ -41,9 +41,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable long? SetStartValue(long? startValue, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetStartValueConfigurationSource(); /// @@ -55,9 +55,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable int? SetIncrementBy(int? incrementBy, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIncrementByConfigurationSource(); /// @@ -69,9 +69,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable long? SetMinValue(long? minValue, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetMinValueConfigurationSource(); /// @@ -83,9 +83,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable long? SetMaxValue(long? maxValue, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetMaxValueConfigurationSource(); /// @@ -97,9 +97,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable Type? SetType([CanBeNull] Type? type, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetTypeConfigurationSource(); /// @@ -112,9 +112,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable Type? SetClrType([CanBeNull] Type? type, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . [Obsolete("Use GetTypeConfigurationSource")] ConfigurationSource? GetClrTypeConfigurationSource(); @@ -130,9 +130,9 @@ public interface IConventionSequence : ISequence, IConventionAnnotatable bool? SetIsCyclic(bool? cyclic, bool fromDataAnnotation = false); /// - /// Gets the configuration source for . + /// Gets the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsCyclicConfigurationSource(); } } diff --git a/src/EFCore.Relational/Metadata/IDbFunction.cs b/src/EFCore.Relational/Metadata/IDbFunction.cs index 3ca10d6ef0d..68f8f10cf0d 100644 --- a/src/EFCore.Relational/Metadata/IDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IDbFunction.cs @@ -1,93 +1,28 @@ // 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.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Query.SqlExpressions; -using Microsoft.EntityFrameworkCore.Storage; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relational database function in an . + /// Represents a relational database function in an in + /// the a form that can be mutated while the model is being built. /// - public interface IDbFunction : IAnnotatable + public interface IDbFunction : IReadOnlyDbFunction, IAnnotatable { /// - /// Gets the name of the function in the database. + /// Gets the model in which this function is defined. /// - string Name { get; } + new IModel Model { get; } /// - /// Gets the schema of the function in the database. + /// Gets the parameters for this function /// - string? Schema { get; } - - /// - /// Gets the name of the function in the model. - /// - string ModelName { get; } - - /// - /// Gets the in which this function is defined. - /// - IModel Model { get; } - - /// - /// Gets the CLR method which maps to the function in the database. - /// - MethodInfo? MethodInfo { get; } - - /// - /// Gets the value indicating whether the database function is built-in. - /// - bool IsBuiltIn { get; } - - /// - /// Gets the value indicating whether this function returns scalar value. - /// - [CA.MemberNotNullWhen(true, nameof(TypeMapping))] - bool IsScalar { get; } - - /// - /// Gets the value indicating whether this function is an aggregate function. - /// - bool IsAggregate { get; } - - /// - /// Gets the value indicating whether the database function can return null. - /// - bool IsNullable { get; } - - /// - /// Gets the configured store type string. - /// - string? StoreType { get; } - - /// - /// Gets the returned CLR type. - /// - Type ReturnType { get; } - - /// - /// Gets the type mapping for the function's return type. - /// - RelationalTypeMapping? TypeMapping { get; } - - /// - /// Gets the parameters for this function. - /// - IReadOnlyList Parameters { get; } - - /// - /// Gets the translation callback for performing custom translation of the method call into a SQL expression fragment. - /// - Func, SqlExpression>? Translation { get; } + new IReadOnlyList Parameters { get; } /// /// Gets the associated . diff --git a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs index 2c1b7c22adb..5a3edbc7a85 100644 --- a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -10,45 +9,28 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a parameter. + /// Represents a function parameter. /// - public interface IDbFunctionParameter : IAnnotatable + public interface IDbFunctionParameter : IReadOnlyDbFunctionParameter, IAnnotatable { /// - /// Gets the to which this parameter belongs. + /// Gets the function to which this parameter belongs. /// - IDbFunction Function { get; } - - /// - /// Gets the parameter name. - /// - string Name { get; } - - /// - /// Gets the parameter type. - /// - Type ClrType { get; } + new IDbFunction Function { get; } /// /// Gets the store type of this parameter. /// - 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. - /// - bool PropagatesNullability { get; } + new string StoreType { get; } /// /// Gets the for this parameter. /// - RelationalTypeMapping TypeMapping { get; } + new RelationalTypeMapping TypeMapping { get; } /// /// Gets the associated . /// - // TODO-NULLABLE: Not sure about what this is for or if it should be nullable - IStoreFunctionParameter? StoreFunctionParameter { get; } + IStoreFunctionParameter StoreFunctionParameter { get; } } } diff --git a/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs b/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs index 79a750207b8..51ec32caf8d 100644 --- a/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/IMutableCheckConstraint.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a check constraint in the . /// - public interface IMutableCheckConstraint : ICheckConstraint, IMutableAnnotatable + public interface IMutableCheckConstraint : IReadOnlyCheckConstraint, IMutableAnnotatable { /// /// Gets the in which this check constraint is defined. diff --git a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs index 47de4debb33..a651d451c4f 100644 --- a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs +++ b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs @@ -15,7 +15,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. /// - public interface IMutableDbFunction : IMutableAnnotatable, IDbFunction + public interface IMutableDbFunction : IReadOnlyDbFunction, IMutableAnnotatable { /// /// Gets or sets the name of the function in the database. diff --git a/src/EFCore.Relational/Metadata/IMutableDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IMutableDbFunctionParameter.cs index 69327cc0018..6b2b8f760c6 100644 --- a/src/EFCore.Relational/Metadata/IMutableDbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/IMutableDbFunctionParameter.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a parameter. /// - public interface IMutableDbFunctionParameter : IMutableAnnotatable, IDbFunctionParameter + public interface IMutableDbFunctionParameter : IReadOnlyDbFunctionParameter, IMutableAnnotatable { /// /// Gets the to which this parameter belongs. diff --git a/src/EFCore.Relational/Metadata/IMutableSequence.cs b/src/EFCore.Relational/Metadata/IMutableSequence.cs index a979201fbd0..ada624b09c3 100644 --- a/src/EFCore.Relational/Metadata/IMutableSequence.cs +++ b/src/EFCore.Relational/Metadata/IMutableSequence.cs @@ -9,10 +9,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a database sequence in the in a form that - /// can be mutated while building the model. + /// Represents a database sequence in the model. /// - public interface IMutableSequence : ISequence, IMutableAnnotatable + public interface IMutableSequence : IReadOnlySequence, IMutableAnnotatable { /// /// Gets the in which this sequence is defined. diff --git a/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs b/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs new file mode 100644 index 00000000000..3ab910250db --- /dev/null +++ b/src/EFCore.Relational/Metadata/IReadOnlyCheckConstraint.cs @@ -0,0 +1,30 @@ +// 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 +{ + /// + /// Represents a check constraint in the . + /// + public interface IReadOnlyCheckConstraint : IReadOnlyAnnotatable + { + /// + /// Gets the name of the check constraint in the database. + /// + string Name { get; } + + /// + /// Gets the in which this check constraint is defined. + /// + IReadOnlyEntityType EntityType { get; } + + /// + /// Gets the constraint sql used in a check constraint in the database. + /// + string Sql { get; } + } +} diff --git a/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs b/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs new file mode 100644 index 00000000000..d1447c8a85d --- /dev/null +++ b/src/EFCore.Relational/Metadata/IReadOnlyDbFunction.cs @@ -0,0 +1,92 @@ +// 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.Reflection; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Query.SqlExpressions; +using Microsoft.EntityFrameworkCore.Storage; +using CA = System.Diagnostics.CodeAnalysis; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a relational database function in an . + /// + public interface IReadOnlyDbFunction : IReadOnlyAnnotatable + { + /// + /// Gets the name of the function in the database. + /// + string Name { get; } + + /// + /// Gets the schema of the function in the database. + /// + string? Schema { get; } + + /// + /// Gets the name of the function in the model. + /// + string ModelName { get; } + + /// + /// Gets the model in which this function is defined. + /// + IReadOnlyModel Model { get; } + + /// + /// Gets the CLR method which maps to the function in the database. + /// + MethodInfo? MethodInfo { get; } + + /// + /// Gets the value indicating whether the database function is built-in. + /// + bool IsBuiltIn { get; } + + /// + /// Gets the value indicating whether this function returns scalar value. + /// + [CA.MemberNotNullWhen(true, nameof(TypeMapping))] + bool IsScalar { get; } + + /// + /// Gets the value indicating whether this function is an aggregate function. + /// + bool IsAggregate { get; } + + /// + /// Gets the value indicating whether the database function can return null. + /// + bool IsNullable { get; } + + /// + /// Gets the configured store type string. + /// + string? StoreType { get; } + + /// + /// Gets the returned CLR type. + /// + Type ReturnType { get; } + + /// + /// Gets the type mapping for the function's return type. + /// + RelationalTypeMapping? TypeMapping { get; } + + /// + /// Gets the parameters for this function. + /// + IReadOnlyList Parameters { get; } + + /// + /// Gets the translation callback for performing custom translation of the method call into a SQL expression fragment. + /// + Func, SqlExpression>? Translation { get; } + } +} diff --git a/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs new file mode 100644 index 00000000000..7e9690240ef --- /dev/null +++ b/src/EFCore.Relational/Metadata/IReadOnlyDbFunctionParameter.cs @@ -0,0 +1,48 @@ +// 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 Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a parameter. + /// + public interface IReadOnlyDbFunctionParameter : IReadOnlyAnnotatable + { + /// + /// Gets the to which this parameter belongs. + /// + IReadOnlyDbFunction Function { get; } + + /// + /// Gets the parameter name. + /// + string Name { get; } + + /// + /// Gets the parameter type. + /// + Type ClrType { get; } + + /// + /// Gets the store type of this parameter. + /// + 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. + /// + bool PropagatesNullability { get; } + + /// + /// Gets the for this parameter. + /// + RelationalTypeMapping? TypeMapping { get; } + } +} diff --git a/src/EFCore.Relational/Metadata/IReadOnlySequence.cs b/src/EFCore.Relational/Metadata/IReadOnlySequence.cs new file mode 100644 index 00000000000..64bc60c9af2 --- /dev/null +++ b/src/EFCore.Relational/Metadata/IReadOnlySequence.cs @@ -0,0 +1,68 @@ +// 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 Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a database sequence in the model. + /// + public interface IReadOnlySequence : IReadOnlyAnnotatable + { + /// + /// Gets the name of the sequence in the database. + /// + string Name { get; } + + /// + /// Gets the database schema that contains the sequence. + /// + string? Schema { get; } + + /// + /// Gets the in which this sequence is defined. + /// + IReadOnlyModel Model { get; } + + /// + /// Gets the value at which the sequence will start. + /// + long StartValue { get; } + + /// + /// Gets the amount incremented to obtain each new value in the sequence. + /// + int IncrementBy { get; } + + /// + /// Gets the minimum value supported by the sequence, or if none has been set. + /// + long? MinValue { get; } + + /// + /// Gets the maximum value supported by the sequence, or if none has been set. + /// + long? MaxValue { get; } + + /// + /// Gets the of values returned by the sequence. + /// + Type Type { get; } + + /// + /// Gets the of values returned by the sequence. + /// + [Obsolete("Use Type")] + Type ClrType { get; } + + /// + /// Gets the value indicating whether the sequence will start again from the beginning when the max value + /// is reached. + /// + bool IsCyclic { get; } + } +} diff --git a/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs b/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs index 820f8fa1e96..4d7056d4975 100644 --- a/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs +++ b/src/EFCore.Relational/Metadata/IRelationalAnnotationProvider.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// A service typically implemented by database providers that gives access to annotations - /// used by relational EF Core components on various elements of the . + /// used by relational EF Core components on various elements of the . /// /// /// The service lifetime is . This means a single instance diff --git a/src/EFCore.Relational/Metadata/ISequence.cs b/src/EFCore.Relational/Metadata/ISequence.cs index cab2584afb5..3f107cd56cc 100644 --- a/src/EFCore.Relational/Metadata/ISequence.cs +++ b/src/EFCore.Relational/Metadata/ISequence.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using Microsoft.EntityFrameworkCore.Infrastructure; #nullable enable @@ -9,60 +8,13 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a database sequence in the . + /// Represents a database sequence in the model. /// - public interface ISequence : IAnnotatable + public interface ISequence : IReadOnlySequence, IAnnotatable { - /// - /// Gets the name of the sequence in the database. - /// - string Name { get; } - - /// - /// Gets the database schema that contains the sequence. - /// - string? Schema { get; } - /// /// Gets the in which this sequence is defined. /// - IModel Model { get; } - - /// - /// Gets the value at which the sequence will start. - /// - long StartValue { get; } - - /// - /// Gets the amount incremented to obtain each new value in the sequence. - /// - int IncrementBy { get; } - - /// - /// Gets the minimum value supported by the sequence, or if none has been set. - /// - long? MinValue { get; } - - /// - /// Gets the maximum value supported by the sequence, or if none has been set. - /// - long? MaxValue { get; } - - /// - /// Gets the of values returned by the sequence. - /// - Type Type { get; } - - /// - /// Gets the of values returned by the sequence. - /// - [Obsolete("Use Type")] - Type ClrType { get; } - - /// - /// Gets the value indicating whether the sequence will start again from the beginning when the max value - /// is reached. - /// - bool IsCyclic { get; } + new IModel Model { get; } } } diff --git a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs index 441976dd09c..47daf600763 100644 --- a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs +++ b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -19,7 +20,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 CheckConstraint : ConventionAnnotatable, IMutableCheckConstraint, IConventionCheckConstraint + public class CheckConstraint : ConventionAnnotatable, IMutableCheckConstraint, IConventionCheckConstraint, ICheckConstraint { private ConfigurationSource _configurationSource; @@ -56,6 +57,8 @@ public CheckConstraint( throw new InvalidOperationException(RelationalStrings.DuplicateCheckConstraint(Name, EntityType.DisplayName())); } + EnsureMutable(); + dataDictionary.Add(name, this); } @@ -65,7 +68,7 @@ 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] IEntityType entityType) + public static IEnumerable GetCheckConstraints([NotNull] IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); @@ -79,7 +82,7 @@ public static IEnumerable GetCheckConstraints([NotNull] IEntity /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static ICheckConstraint? FindCheckConstraint( - [NotNull] IEntityType entityType, + [NotNull] IReadOnlyEntityType entityType, [NotNull] string name) { var dataDictionary = GetConstraintsDictionary(entityType); @@ -106,6 +109,8 @@ public static IEnumerable GetCheckConstraints([NotNull] IEntity if (dataDictionary != null && dataDictionary.TryGetValue(name, out var constraint)) { + constraint.EnsureMutable(); + dataDictionary.Remove(name); return constraint; } @@ -119,7 +124,12 @@ public static IEnumerable GetCheckConstraints([NotNull] IEntity /// 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 IEntityType EntityType { get; } + public virtual IReadOnlyEntityType EntityType { get; } + + /// + /// Indicates whether the check constraint is read-only. + /// + public override bool IsReadOnly => ((Annotatable)EntityType.Model).IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -157,7 +167,7 @@ public virtual void UpdateConfigurationSource(ConfigurationSource configurationS _configurationSource = configurationSource.Max(_configurationSource); } - private static Dictionary? GetConstraintsDictionary(IEntityType entityType) + private static Dictionary? GetConstraintsDictionary(IReadOnlyEntityType entityType) => (Dictionary?)entityType[RelationalAnnotationNames.CheckConstraints]; /// @@ -176,7 +186,10 @@ public override string ToString() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// IConventionEntityType IConventionCheckConstraint.EntityType - => (IConventionEntityType)EntityType; + { + [DebuggerStepThrough] + get => (IConventionEntityType)EntityType; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -185,6 +198,21 @@ IConventionEntityType IConventionCheckConstraint.EntityType /// doing so can result in application failures when updating to a new Entity Framework Core release. /// IMutableEntityType IMutableCheckConstraint.EntityType - => (IMutableEntityType)EntityType; + { + [DebuggerStepThrough] + get => (IMutableEntityType)EntityType; + } + + /// + /// 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. + /// + IEntityType ICheckConstraint.EntityType + { + [DebuggerStepThrough] + get => (IEntityType)EntityType; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs index 2d259fa85f8..734afda10b8 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnBase.cs @@ -32,16 +32,44 @@ public ColumnBase([NotNull] string name, [NotNull] string type, [NotNull] TableB Table = table; } - /// + /// + /// 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 string Name { get; } - /// - public virtual ITableBase Table { 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. + /// + public virtual TableBase Table { 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. + /// + public override bool IsReadOnly => Table.Model.IsReadOnly; + + /// + /// 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 string StoreType { 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. + /// public virtual bool IsNullable { get; set; } /// @@ -72,5 +100,12 @@ IEnumerable IColumnBase.PropertyMappings [DebuggerStepThrough] get => PropertyMappings; } + + /// + ITableBase IColumnBase.Table + { + [DebuggerStepThrough] + get => Table; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/ColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/ColumnMapping.cs index 6a5553d9cdc..2139c909a3b 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnMapping.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Diagnostics; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; @@ -27,9 +28,8 @@ public class ColumnMapping : ColumnMappingBase, IColumnMapping public ColumnMapping( [NotNull] IProperty property, [NotNull] Column column, - [NotNull] RelationalTypeMapping typeMapping, [NotNull] TableMapping tableMapping) - : base(property, column, typeMapping, tableMapping) + : base(property, column, tableMapping) { } @@ -37,6 +37,10 @@ public ColumnMapping( public new virtual ITableMapping TableMapping => (ITableMapping)base.TableMapping; + /// + public override RelationalTypeMapping TypeMapping => Property.FindRelationalTypeMapping( + StoreObjectIdentifier.Table(TableMapping.Table.Name, TableMapping.Table.Schema))!; + /// /// 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.Relational/Metadata/Internal/ColumnMappingBase.cs b/src/EFCore.Relational/Metadata/Internal/ColumnMappingBase.cs index 28ef0d57a68..3440c8c5464 100644 --- a/src/EFCore.Relational/Metadata/Internal/ColumnMappingBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/ColumnMappingBase.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Diagnostics; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; @@ -26,25 +27,55 @@ public class ColumnMappingBase : Annotatable, IColumnMappingBase public ColumnMappingBase( [NotNull] IProperty property, [NotNull] ColumnBase column, - [NotNull] RelationalTypeMapping typeMapping, [NotNull] TableMappingBase tableMapping) { Property = property; Column = column; - TypeMapping = typeMapping; TableMapping = tableMapping; } /// public virtual 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. + /// + public virtual ColumnBase Column { get; } + /// - public virtual IColumnBase Column { get; } + public virtual RelationalTypeMapping TypeMapping => Property.GetRelationalTypeMapping(); + + /// + /// 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 TableMappingBase TableMapping { 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. + /// + public override bool IsReadOnly => TableMapping.IsReadOnly; /// - public virtual RelationalTypeMapping TypeMapping { get; } + IColumnBase IColumnMappingBase.Column + { + [DebuggerStepThrough] + get => Column; + } /// - public virtual ITableMappingBase TableMapping { get; } + ITableMappingBase IColumnMappingBase.TableMapping + { + [DebuggerStepThrough] + get => TableMapping; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs index 2f8b79fe263..0c4c19d395a 100644 --- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs +++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs @@ -16,7 +16,6 @@ using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -28,7 +27,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 + public class DbFunction : ConventionAnnotatable, IMutableDbFunction, IConventionDbFunction, IDbFunction { private readonly List _parameters; private string? _schema; @@ -180,13 +179,18 @@ public virtual bool IsInModel public virtual void SetRemovedFromModel() => _builder = null; + /// + /// Indicates whether the function is read-only. + /// + public override bool IsReadOnly => ((Annotatable)Model).IsReadOnly; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static IEnumerable GetDbFunctions([NotNull] IModel model) + public static IEnumerable GetDbFunctions([NotNull] IReadOnlyModel model) => ((SortedDictionary?)model[RelationalAnnotationNames.DbFunctions]) ?.Values ?? Enumerable.Empty(); @@ -197,7 +201,7 @@ public static IEnumerable GetDbFunctions([NotNull] IModel 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 DbFunction? FindDbFunction([NotNull] IModel model, [NotNull] MethodInfo methodInfo) + public static DbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] MethodInfo methodInfo) => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions && functions.TryGetValue(GetFunctionName(methodInfo, methodInfo.GetParameters()), out var dbFunction) ? dbFunction @@ -209,7 +213,7 @@ public static IEnumerable GetDbFunctions([NotNull] IModel 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 DbFunction? FindDbFunction([NotNull] IModel model, [NotNull] string name) + public static DbFunction? FindDbFunction([NotNull] IReadOnlyModel model, [NotNull] string name) => model[RelationalAnnotationNames.DbFunctions] is SortedDictionary functions && functions.TryGetValue(name, out var dbFunction) ? dbFunction @@ -349,6 +353,8 @@ public virtual string? Schema /// public virtual string? SetSchema([CanBeNull] string? schema, ConfigurationSource configurationSource) { + EnsureMutable(); + _schema = schema; _schemaConfigurationSource = schema == null @@ -389,6 +395,8 @@ public virtual string Name { Check.NullButNotEmpty(name, nameof(name)); + EnsureMutable(); + _name = name; _nameConfigurationSource = name == null @@ -427,6 +435,8 @@ public virtual bool IsBuiltIn /// public virtual bool SetIsBuiltIn(bool builtIn, ConfigurationSource configurationSource) { + EnsureMutable(); + _builtIn = builtIn; _builtInConfigurationSource = configurationSource.Max(_builtInConfigurationSource); @@ -462,6 +472,8 @@ public virtual bool IsNullable /// public virtual bool SetIsNullable(bool nullable, ConfigurationSource configurationSource) { + EnsureMutable(); + if (!IsScalar) { throw new InvalidOperationException(RelationalStrings.NonScalarFunctionCannotBeNullable(Name)); @@ -502,6 +514,8 @@ public virtual string? StoreType /// public virtual string? SetStoreType([CanBeNull] string? storeType, ConfigurationSource configurationSource) { + EnsureMutable(); + _storeType = storeType; _storeTypeConfigurationSource = storeType == null @@ -582,6 +596,8 @@ public virtual Func, SqlExpression>? Translation [CanBeNull] Func, SqlExpression>? translation, ConfigurationSource configurationSource) { + EnsureMutable(); + if (translation != null && (!IsScalar || IsAggregate)) { @@ -606,6 +622,18 @@ public virtual Func, SqlExpression>? Translation public virtual ConfigurationSource? GetTranslationConfigurationSource() => _translationConfigurationSource; + /// + /// 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 IReadOnlyList Parameters + { + [DebuggerStepThrough] + get => _parameters; + } + /// /// 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 @@ -631,7 +659,7 @@ IConventionDbFunctionBuilder IConventionDbFunction.Builder } /// - IModel IDbFunction.Model + IReadOnlyModel IReadOnlyDbFunction.Model { [DebuggerStepThrough] get => Model; @@ -644,20 +672,15 @@ IConventionModel IConventionDbFunction.Model get => (IConventionModel)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 virtual IReadOnlyList Parameters + /// + IModel IDbFunction.Model { [DebuggerStepThrough] - get => _parameters; + get => (IModel)Model; } /// - IReadOnlyList IDbFunction.Parameters + IReadOnlyList IReadOnlyDbFunction.Parameters { [DebuggerStepThrough] get => _parameters; @@ -677,6 +700,13 @@ IReadOnlyList IMutableDbFunction.Parameters get => _parameters; } + /// + IReadOnlyList IDbFunction.Parameters + { + [DebuggerStepThrough] + get => _parameters; + } + /// /// 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.Relational/Metadata/Internal/DbFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs index a70b1a4a67e..1494e9a44b2 100644 --- a/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs +++ b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs @@ -10,7 +10,6 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders.Internal; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -22,7 +21,11 @@ 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 DbFunctionParameter : ConventionAnnotatable, IMutableDbFunctionParameter, IConventionDbFunctionParameter + public class DbFunctionParameter : + ConventionAnnotatable, + IMutableDbFunctionParameter, + IConventionDbFunctionParameter, + IDbFunctionParameter { private string? _storeType; private RelationalTypeMapping? _typeMapping; @@ -81,7 +84,7 @@ IConventionDbFunction IConventionDbFunctionParameter.Function } /// - IDbFunction IDbFunctionParameter.Function + IReadOnlyDbFunction IReadOnlyDbFunctionParameter.Function { [DebuggerStepThrough] get => Function; @@ -240,13 +243,23 @@ public virtual bool SetPropagatesNullability(bool propagatesNullability, Configu public override string ToString() => this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + /// + string IDbFunctionParameter.StoreType + { + [DebuggerStepThrough] + get => StoreType!; // Model validation ensures all parameters have a type mapping + } + + /// + IDbFunction IDbFunctionParameter.Function + { + [DebuggerStepThrough] + get => Function; + } + /// [DebuggerStepThrough] RelationalTypeMapping? IConventionDbFunctionParameter.SetTypeMapping(RelationalTypeMapping? typeMapping, bool fromDataAnnotation) => SetTypeMapping(typeMapping, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - - /// - string IDbFunctionParameter.StoreType - => StoreType!; // Model validation ensures all parameters have a type mapping } } diff --git a/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs index 8dcafbee728..d4a1b14c807 100644 --- a/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/FunctionColumnMapping.cs @@ -27,9 +27,8 @@ public class FunctionColumnMapping : ColumnMappingBase, IFunctionColumnMapping public FunctionColumnMapping( [NotNull] IProperty property, [NotNull] FunctionColumn column, - [NotNull] RelationalTypeMapping typeMapping, [NotNull] FunctionMapping viewMapping) - : base(property, column, typeMapping, viewMapping) + : base(property, column, viewMapping) { } @@ -37,6 +36,10 @@ public FunctionColumnMapping( public virtual IFunctionMapping FunctionMapping => (IFunctionMapping)TableMapping; + /// + public override RelationalTypeMapping TypeMapping => Property.FindRelationalTypeMapping( + StoreObjectIdentifier.DbFunction(FunctionMapping.DbFunction.Name))!; + /// /// 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.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs index e1c8604271c..f3e59ab0534 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs @@ -32,7 +32,8 @@ public static class RelationalEntityTypeExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static IEnumerable GetViewOrTableMappings([NotNull] this IEntityType entityType) - => (IEnumerable?)(entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewMappings) + => (IEnumerable?)(entityType.FindRuntimeAnnotationValue( + RelationalAnnotationNames.ViewMappings) ?? entityType.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableMappings)) ?? Enumerable.Empty(); @@ -42,7 +43,7 @@ public static IEnumerable GetViewOrTableMappings([NotNull] th /// 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 IReadOnlyList GetTptDiscriminatorValues([NotNull] this IEntityType entityType) + public static IReadOnlyList GetTptDiscriminatorValues([NotNull] this IReadOnlyEntityType entityType) => entityType.GetConcreteDerivedTypesInclusive().Select(et => et.ShortName()).ToList(); } } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs index e784f74e10f..4f0b65d0de7 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs @@ -27,8 +27,8 @@ public static class RelationalForeignKeyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool AreCompatible( - [NotNull] this IForeignKey foreignKey, - [NotNull] IForeignKey duplicateForeignKey, + [NotNull] this IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyForeignKey duplicateForeignKey, in StoreObjectIdentifier storeObject, bool shouldThrow) { diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs index df8f09a8b22..072bfc74197 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs @@ -26,8 +26,8 @@ public static class RelationalIndexExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool AreCompatible( - [NotNull] this IIndex index, - [NotNull] IIndex duplicateIndex, + [NotNull] this IReadOnlyIndex index, + [NotNull] IReadOnlyIndex duplicateIndex, in StoreObjectIdentifier storeObject, bool shouldThrow) { diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs index 3c0d2d11771..ffc27a7035d 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs @@ -26,8 +26,8 @@ public static class RelationalKeyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool AreCompatible( - [NotNull] this IKey key, - [NotNull] IKey duplicateKey, + [NotNull] this IReadOnlyKey key, + [NotNull] IReadOnlyKey duplicateKey, in StoreObjectIdentifier storeObject, bool shouldThrow) { diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index b271241a2f6..968962105ee 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -258,8 +258,7 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp column.IsNullable = false; } - var columnMapping = new ColumnMappingBase( - property, column, property.FindRelationalTypeMapping()!, tableMapping); + var columnMapping = new ColumnMappingBase(property, column, tableMapping); tableMapping.ColumnMappings.Add(columnMapping); column.PropertyMappings.Add(columnMapping); @@ -352,8 +351,7 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT column.IsNullable = false; } - var columnMapping = new ColumnMapping( - property, column, property.FindRelationalTypeMapping(mappedTable)!, tableMapping); + var columnMapping = new ColumnMapping(property, column, tableMapping); tableMapping.ColumnMappings.Add(columnMapping); column.PropertyMappings.Add(columnMapping); @@ -444,8 +442,7 @@ private static void AddViews(RelationalModel databaseModel, IEntityType entityTy column.IsNullable = false; } - var columnMapping = new ViewColumnMapping( - property, column, property.FindRelationalTypeMapping(mappedView)!, viewMapping); + var columnMapping = new ViewColumnMapping(property, column, viewMapping); viewMapping.ColumnMappings.Add(columnMapping); column.PropertyMappings.Add(columnMapping); @@ -550,8 +547,7 @@ private static void AddSqlQueries(RelationalModel databaseModel, IEntityType ent column.IsNullable = false; } - var columnMapping = new SqlQueryColumnMapping( - property, column, property.FindRelationalTypeMapping(mappedQuery)!, queryMapping); + var columnMapping = new SqlQueryColumnMapping(property, column, queryMapping); queryMapping.ColumnMappings.Add(columnMapping); column.PropertyMappings.Add(columnMapping); @@ -631,7 +627,7 @@ private static void AddMappedFunctions(RelationalModel databaseModel, IEntityTyp private static void AddTVFs(RelationalModel relationalModel) { - var model = (IConventionModel)relationalModel.Model; + var model = relationalModel.Model; foreach (DbFunction function in model.GetDbFunctions()) { var entityType = function.IsScalar @@ -700,8 +696,7 @@ private static FunctionMapping CreateFunctionMapping( column.IsNullable = false; } - var columnMapping = new FunctionColumnMapping( - property, column, property.FindRelationalTypeMapping(mappedFunction)!, functionMapping); + var columnMapping = new FunctionColumnMapping(property, column, functionMapping); functionMapping.ColumnMappings.Add(columnMapping); column.PropertyMappings.Add(columnMapping); @@ -756,7 +751,7 @@ private static void PopulateConstraints(Table table) continue; } - var entityType = (IConventionEntityType)entityTypeMapping.EntityType; + var entityType = (IEntityType)entityTypeMapping.EntityType; foreach (var foreignKey in entityType.GetForeignKeys()) { var firstPrincipalMapping = true; @@ -843,7 +838,7 @@ private static void PopulateConstraints(Table table) if (entityTypeMapping.IncludesDerivedTypes && foreignKey.DeclaringEntityType != entityType - && entityType.FindPrimaryKey() is IConventionKey primaryKey + && entityType.FindPrimaryKey() is IKey primaryKey && foreignKey.Properties.SequenceEqual(primaryKey.Properties)) { // The identifying FK constraint is needed to be created only on the table that corresponds diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs index 460b73fac53..23fec198a62 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyExtensions.cs @@ -23,7 +23,7 @@ public static class RelationalPropertyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - public static string? GetConfiguredColumnType([NotNull] this IProperty property) + public static string? GetConfiguredColumnType([NotNull] this IReadOnlyProperty property) => (string?)property[RelationalAnnotationNames.ColumnType]; } } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs index 50610150fb8..547a7ec5f58 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs @@ -63,7 +63,7 @@ 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] IProperty property, in StoreObjectIdentifier storeObject) + public static RelationalPropertyOverrides? Find([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var tableOverrides = (SortedDictionary?) property[RelationalAnnotationNames.RelationalOverrides]; diff --git a/src/EFCore.Relational/Metadata/Internal/Sequence.cs b/src/EFCore.Relational/Metadata/Internal/Sequence.cs index a5798286166..71db8af6fa3 100644 --- a/src/EFCore.Relational/Metadata/Internal/Sequence.cs +++ b/src/EFCore.Relational/Metadata/Internal/Sequence.cs @@ -24,7 +24,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 Sequence : ConventionAnnotatable, IMutableSequence, IConventionSequence + public class Sequence : ConventionAnnotatable, IMutableSequence, IConventionSequence, ISequence { private readonly string? _schema; private long? _startValue; @@ -100,7 +100,7 @@ public class Sequence : ConventionAnnotatable, IMutableSequence, IConventionSequ public Sequence( [NotNull] string name, [CanBeNull] string? schema, - [NotNull] IModel model, + [NotNull] IReadOnlyModel model, ConfigurationSource configurationSource) { Check.NotEmpty(name, nameof(name)); @@ -120,7 +120,7 @@ public Sequence( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [Obsolete("Use the other constructor")] - public Sequence([NotNull] IModel model, [NotNull] string annotationName) + public Sequence([NotNull] IReadOnlyModel model, [NotNull] string annotationName) { Check.NotNull(model, nameof(model)); Check.NotEmpty(annotationName, nameof(annotationName)); @@ -146,7 +146,7 @@ public Sequence([NotNull] IModel 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] IModel model) + public static IEnumerable GetSequences([NotNull] IReadOnlyModel model) => ((SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]) ?.Values ?? Enumerable.Empty(); @@ -157,7 +157,7 @@ public static IEnumerable GetSequences([NotNull] IModel 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] IModel model, [NotNull] string name, [CanBeNull] string? schema) + public static Sequence? FindSequence([NotNull] IReadOnlyModel model, [NotNull] string name, [CanBeNull] string? schema) { var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; if (sequences == null @@ -208,6 +208,8 @@ public static Sequence AddSequence( Check.NotNull(sequence, nameof(sequence)); Check.NotEmpty(name, nameof(name)); + sequence.EnsureMutable(); + var sequences = (SortedDictionary<(string, string?), Sequence>?)model[RelationalAnnotationNames.Sequences]; var tuple = (sequence.Name, sequence.Schema); if (sequences == null @@ -281,7 +283,12 @@ 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 virtual IModel Model { get; } + public virtual IReadOnlyModel Model { get; } + + /// + /// Indicates whether the sequence is read-only. + /// + public override bool IsReadOnly => ((Annotatable)Model).IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -338,6 +345,8 @@ public virtual long StartValue /// public virtual long? SetStartValue(long? startValue, ConfigurationSource configurationSource) { + EnsureMutable(); + _startValue = startValue; _startValueConfigurationSource = startValue == null @@ -376,6 +385,8 @@ public virtual int IncrementBy /// public virtual int? SetIncrementBy(int? incrementBy, ConfigurationSource configurationSource) { + EnsureMutable(); + _incrementBy = incrementBy; _incrementByConfigurationSource = incrementBy == null @@ -414,6 +425,8 @@ public virtual long? MinValue /// public virtual long? SetMinValue(long? minValue, ConfigurationSource configurationSource) { + EnsureMutable(); + _minValue = minValue; _minValueConfigurationSource = minValue == null @@ -452,6 +465,8 @@ public virtual long? MaxValue /// public virtual long? SetMaxValue(long? maxValue, ConfigurationSource configurationSource) { + EnsureMutable(); + _maxValue = maxValue; _maxValueConfigurationSource = maxValue == null @@ -499,6 +514,8 @@ public virtual Type Type /// public virtual Type? SetType([CanBeNull] Type? type, ConfigurationSource configurationSource) { + EnsureMutable(); + if (type != null && !SupportedTypes.Contains(type)) { @@ -576,6 +593,8 @@ public virtual bool IsCyclic /// public virtual bool? SetIsCyclic(bool? cyclic, ConfigurationSource configurationSource) { + EnsureMutable(); + _isCyclic = cyclic; _isCyclicConfigurationSource = cyclic == null @@ -610,7 +629,10 @@ public override string ToString() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// IConventionSequenceBuilder IConventionSequence.Builder - => Builder; + { + [DebuggerStepThrough] + get => Builder; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -619,7 +641,10 @@ IConventionSequenceBuilder IConventionSequence.Builder /// doing so can result in application failures when updating to a new Entity Framework Core release. /// IMutableModel IMutableSequence.Model - => (IMutableModel)Model; + { + [DebuggerStepThrough] + get => (IMutableModel)Model; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -628,7 +653,22 @@ IMutableModel IMutableSequence.Model /// doing so can result in application failures when updating to a new Entity Framework Core release. /// IConventionModel IConventionSequence.Model - => (IConventionModel)Model; + { + [DebuggerStepThrough] + get => (IConventionModel)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. + /// + IModel ISequence.Model + { + [DebuggerStepThrough] + get => (IModel)Model; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -636,6 +676,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] long? IConventionSequence.SetStartValue(long? startValue, bool fromDataAnnotation) => SetStartValue(startValue, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -645,6 +686,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] int? IConventionSequence.SetIncrementBy(int? incrementBy, bool fromDataAnnotation) => SetIncrementBy(incrementBy, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -654,6 +696,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] long? IConventionSequence.SetMinValue(long? minValue, bool fromDataAnnotation) => SetMinValue(minValue, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -663,6 +706,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] long? IConventionSequence.SetMaxValue(long? maxValue, bool fromDataAnnotation) => SetMaxValue(maxValue, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -672,6 +716,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] Type? IConventionSequence.SetClrType(Type? type, bool fromDataAnnotation) => SetType(type, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -681,6 +726,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] Type? IConventionSequence.SetType(Type? type, bool fromDataAnnotation) => SetType(type, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -690,6 +736,7 @@ IConventionModel IConventionSequence.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. /// + [DebuggerStepThrough] bool? IConventionSequence.SetIsCyclic(bool? cyclic, bool fromDataAnnotation) => SetIsCyclic(cyclic, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); diff --git a/src/EFCore.Relational/Metadata/Internal/SqlQueryColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/SqlQueryColumnMapping.cs index bb9fc36c282..b75f39e09d1 100644 --- a/src/EFCore.Relational/Metadata/Internal/SqlQueryColumnMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/SqlQueryColumnMapping.cs @@ -27,9 +27,8 @@ public class SqlQueryColumnMapping : ColumnMappingBase, ISqlQueryColumnMapping public SqlQueryColumnMapping( [NotNull] IProperty property, [NotNull] SqlQueryColumn column, - [NotNull] RelationalTypeMapping typeMapping, [NotNull] SqlQueryMapping sqlQueryMapping) - : base(property, column, typeMapping, sqlQueryMapping) + : base(property, column, sqlQueryMapping) { } @@ -37,6 +36,10 @@ public SqlQueryColumnMapping( public virtual ISqlQueryMapping SqlQueryMapping => (ISqlQueryMapping)TableMapping; + /// + public override RelationalTypeMapping TypeMapping => Property.FindRelationalTypeMapping( + StoreObjectIdentifier.SqlQuery(SqlQueryMapping.SqlQuery.Name))!; + /// /// 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.Relational/Metadata/Internal/TableBase.cs b/src/EFCore.Relational/Metadata/Internal/TableBase.cs index 67aa1f3d86b..637c38158a7 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -47,7 +48,20 @@ public TableBase([NotNull] string name, [CanBeNull] string? schema, [NotNull] Re /// public virtual RelationalModel Model { 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. + /// + public override bool IsReadOnly => Model.IsReadOnly; + + /// + /// 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 IsShared { get; set; } /// @@ -132,15 +146,24 @@ private void CheckMappedEntityType(IEntityType entityType) /// IRelationalModel ITableBase.Model - => Model; + { + [DebuggerStepThrough] + get => Model; + } /// IEnumerable ITableBase.EntityTypeMappings - => EntityTypeMappings; + { + [DebuggerStepThrough] + get => EntityTypeMappings; + } /// IEnumerable ITableBase.Columns - => Columns.Values; + { + [DebuggerStepThrough] + get => Columns.Values; + } /// IEnumerable ITableBase.GetRowInternalForeignKeys(IEntityType entityType) diff --git a/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs b/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs index 96f21ad2422..6e11f15df3b 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs @@ -26,7 +26,7 @@ public class TableMappingBase : Annotatable, ITableMappingBase /// public TableMappingBase( [NotNull] IEntityType entityType, - [NotNull] ITableBase table, + [NotNull] TableBase table, bool includesDerivedTypes) { EntityType = entityType; @@ -37,8 +37,21 @@ public TableMappingBase( /// public virtual IEntityType EntityType { get; } - /// - public virtual ITableBase Table { 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. + /// + public virtual TableBase Table { 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. + /// + public override bool IsReadOnly => Table.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -46,7 +59,7 @@ public TableMappingBase( /// 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 ColumnMappings { get; } + public virtual SortedSet ColumnMappings { get; } = new(ColumnMappingBaseComparer.Instance); /// @@ -63,5 +76,11 @@ IEnumerable ITableMappingBase.ColumnMappings [DebuggerStepThrough] get => ColumnMappings; } + + ITableBase ITableMappingBase.Table + { + [DebuggerStepThrough] + get => Table; + } } } diff --git a/src/EFCore.Relational/Metadata/Internal/ViewColumnMapping.cs b/src/EFCore.Relational/Metadata/Internal/ViewColumnMapping.cs index ea7e34a8f64..c3d35a0391d 100644 --- a/src/EFCore.Relational/Metadata/Internal/ViewColumnMapping.cs +++ b/src/EFCore.Relational/Metadata/Internal/ViewColumnMapping.cs @@ -27,9 +27,8 @@ public class ViewColumnMapping : ColumnMappingBase, IViewColumnMapping public ViewColumnMapping( [NotNull] IProperty property, [NotNull] ViewColumn column, - [NotNull] RelationalTypeMapping typeMapping, [NotNull] ViewMapping viewMapping) - : base(property, column, typeMapping, viewMapping) + : base(property, column, viewMapping) { } @@ -37,6 +36,10 @@ public ViewColumnMapping( public virtual IViewMapping ViewMapping => (IViewMapping)TableMapping; + /// + public override RelationalTypeMapping TypeMapping => Property.FindRelationalTypeMapping( + StoreObjectIdentifier.View(ViewMapping.View.Name, ViewMapping.View.Schema))!; + /// /// 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.Relational/Metadata/RelationalAnnotationProvider.cs b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs index 7e3d3150c6b..14a481cab6c 100644 --- a/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs +++ b/src/EFCore.Relational/Metadata/RelationalAnnotationProvider.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// A base class inherited by database providers that gives access to annotations - /// used by relational EF Core components on various elements of the . + /// used by relational EF Core components on various elements of the . /// /// /// The service lifetime is . This means a single instance diff --git a/src/EFCore.Relational/Metadata/SequenceExtensions.cs b/src/EFCore.Relational/Metadata/SequenceExtensions.cs index 020b215fc90..e940a015192 100644 --- a/src/EFCore.Relational/Metadata/SequenceExtensions.cs +++ b/src/EFCore.Relational/Metadata/SequenceExtensions.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Extension methods for . + /// Sequence extension methods. /// public static class SequenceExtensions { @@ -28,7 +28,7 @@ public static class SequenceExtensions /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this ISequence sequence, + [NotNull] this IReadOnlySequence sequence, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs b/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs index dafaec08afa..a286e489f90 100644 --- a/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs +++ b/src/EFCore.Relational/Metadata/StoreObjectIdentifier.cs @@ -27,7 +27,7 @@ private StoreObjectIdentifier(StoreObjectType storeObjectType, string name, stri /// The entity type. /// The store object type. /// The store object id. - public static StoreObjectIdentifier? Create([NotNull] IEntityType entityType, StoreObjectType type) + public static StoreObjectIdentifier? Create([NotNull] IReadOnlyEntityType entityType, StoreObjectType type) { Check.NotNull(entityType, nameof(entityType)); @@ -81,7 +81,7 @@ public static StoreObjectIdentifier View([NotNull] string name, [CanBeNull] stri /// /// The entity type. /// The SQL query id. - public static StoreObjectIdentifier SqlQuery([NotNull] IEntityType entityType) + public static StoreObjectIdentifier SqlQuery([NotNull] IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index 7ab5b2d278b..e5a6759fc8e 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -600,7 +600,7 @@ protected virtual IEnumerable Diff( // Populate column mapping foreach(var _ in Diff(source.Columns, target.Columns, diffContext)) { } - + yield break; } @@ -951,26 +951,14 @@ private static bool EntityTypePathEquals(IEntityType source, IEntityType target, return false; } - var nextSource = GetLinkedPrincipal(source); - var nextTarget = GetLinkedPrincipal(target); + var nextSource = sourceTable.GetRowInternalForeignKeys(source).FirstOrDefault()?.PrincipalEntityType; + var nextTarget = targetTable.GetRowInternalForeignKeys(target).FirstOrDefault()?.PrincipalEntityType; return (nextSource == null && nextTarget == null) || (nextSource != null && nextTarget != null && EntityTypePathEquals(nextSource, nextTarget, diffContext)); } - private static IEntityType GetLinkedPrincipal(IEntityType entityType) - { - var table = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table); - if (table == null) - { - return null; - } - - var linkingForeignKey = entityType.FindRowInternalForeignKeys(table.Value).FirstOrDefault(); - return linkingForeignKey?.PrincipalEntityType; - } - /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.Relational/Migrations/Migration.cs b/src/EFCore.Relational/Migrations/Migration.cs index fd38f5dc7c3..24dfd72a767 100644 --- a/src/EFCore.Relational/Migrations/Migration.cs +++ b/src/EFCore.Relational/Migrations/Migration.cs @@ -36,7 +36,7 @@ IModel Create() var modelBuilder = new ModelBuilder(); BuildTargetModel(modelBuilder); - return modelBuilder.Model; + return (IModel)modelBuilder.Model; } return _targetModel ??= Create(); diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index b573062cbbd..1086841a62e 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -1513,7 +1513,7 @@ private static Expression AddConvertToObject(Expression expression) var propertyExpressions = new Dictionary(); foreach (var property in entityType .GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredProperties)) + .SelectMany(t => t.GetDeclaredProperties())) { propertyExpressions[property] = new ColumnExpression( property, table.FindColumn(property)!, tableExpression, nullable || !property.IsPrimaryKey()); @@ -1556,7 +1556,8 @@ private static IDictionary GetPropertyExpressionsFr { var propertyExpressions = new Dictionary(); foreach (var property in entityType - .GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()).SelectMany(EntityTypeExtensions.GetDeclaredProperties)) + .GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) + .SelectMany(t => t.GetDeclaredProperties())) { propertyExpressions[property] = new ColumnExpression( property, table.FindColumn(property)!, tableExpression, nullable: true); diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index b7e5fae29ca..1b00f5989a8 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -388,7 +388,7 @@ public void ClearProjection() private static IEnumerable GetAllPropertiesInHierarchy(IEntityType entityType) => entityType.GetAllBaseTypes().Concat(entityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredProperties); + .SelectMany(t => t.GetDeclaredProperties()); /// /// Replaces current projection mapping with a new one to change what is being projected out from this . @@ -1210,7 +1210,7 @@ EntityProjectionExpression LiftEntityProjectionFromSubquery(EntityProjectionExpr // Also lift nested entity projections foreach (var navigation in entityProjection.EntityType .GetAllBaseTypes().Concat(entityProjection.EntityType.GetDerivedTypesInclusive()) - .SelectMany(EntityTypeExtensions.GetDeclaredNavigations)) + .SelectMany(t => t.GetDeclaredNavigations())) { var boundEntityShaperExpression = entityProjection.BindNavigation(navigation); if (boundEntityShaperExpression != null) diff --git a/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs b/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs index bdfab7b547e..c880839b1cd 100644 --- a/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs +++ b/src/EFCore.SqlServer/Diagnostics/ConflictingValueGenerationStrategiesEventData.cs @@ -27,7 +27,7 @@ public ConflictingValueGenerationStrategiesEventData( [NotNull] Func messageGenerator, SqlServerValueGenerationStrategy sqlServerValueGenerationStrategy, [NotNull] string otherValueGenerationStrategy, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) : base(eventDefinition, messageGenerator) { SqlServerValueGenerationStrategy = sqlServerValueGenerationStrategy; @@ -48,6 +48,6 @@ public ConflictingValueGenerationStrategiesEventData( /// /// The property. /// - public virtual IProperty Property { get; } + public virtual IReadOnlyProperty Property { get; } } } diff --git a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs index cdaece713e9..c5f61d84d51 100644 --- a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs @@ -138,7 +138,7 @@ public static void ConflictingValueGenerationStrategiesWarning( [NotNull] this IDiagnosticsLogger diagnostics, SqlServerValueGenerationStrategy sqlServerValueGenerationStrategy, [NotNull] string otherValueGenerationStrategy, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) { var definition = SqlServerResources.LogConflictingValueGenerationStrategies(diagnostics); diff --git a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs index a844912caad..63db54e4b6b 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerEntityTypeExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for SQL Server-specific metadata. + /// Entity type extension methods for SQL Server-specific metadata. /// public static class SqlServerEntityTypeExtensions { @@ -20,7 +20,7 @@ public static class SqlServerEntityTypeExtensions /// /// The entity type. /// if the entity type is mapped to a memory-optimized table. - public static bool IsMemoryOptimized([NotNull] this IEntityType entityType) + public static bool IsMemoryOptimized([NotNull] this IReadOnlyEntityType entityType) => entityType[SqlServerAnnotationNames.MemoryOptimized] as bool? ?? false; /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs index fbc8271b725..a3dc6ae61f6 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerIndexExtensions.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for SQL Server-specific metadata. + /// Index extension methods for SQL Server-specific metadata. /// public static class SqlServerIndexExtensions { @@ -22,7 +22,7 @@ public static class SqlServerIndexExtensions /// /// The index. /// if the index is clustered. - public static bool? IsClustered([NotNull] this IIndex index) + public static bool? IsClustered([NotNull] this IReadOnlyIndex index) => (bool?)index[SqlServerAnnotationNames.Clustered]; /// @@ -31,7 +31,7 @@ public static class SqlServerIndexExtensions /// The index. /// The identifier of the store object. /// if the index is clustered. - public static bool? IsClustered([NotNull] this IIndex index, in StoreObjectIdentifier storeObject) + public static bool? IsClustered([NotNull] this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) { var annotation = index.FindAnnotation(SqlServerAnnotationNames.Clustered); if (annotation != null) @@ -42,7 +42,7 @@ public static class SqlServerIndexExtensions return GetDefaultIsClustered(index, storeObject); } - private static bool? GetDefaultIsClustered([NotNull] IIndex index, in StoreObjectIdentifier storeObject) + private static bool? GetDefaultIsClustered([NotNull] IReadOnlyIndex index, in StoreObjectIdentifier storeObject) { var sharedTableRootIndex = index.FindSharedObjectRootIndex(storeObject); return sharedTableRootIndex?.IsClustered(storeObject); @@ -91,7 +91,7 @@ public static void SetIsClustered([NotNull] this IMutableIndex index, bool? valu /// /// The index. /// The included property names, or if they have not been specified. - public static IReadOnlyList? GetIncludeProperties([NotNull] this IIndex index) + public static IReadOnlyList? GetIncludeProperties([NotNull] this IReadOnlyIndex index) => (string[]?)index[SqlServerAnnotationNames.Include]; /// @@ -137,7 +137,7 @@ public static void SetIncludeProperties([NotNull] this IMutableIndex index, [Not /// /// The index. /// if the index is online. - public static bool? IsCreatedOnline([NotNull] this IIndex index) + public static bool? IsCreatedOnline([NotNull] this IReadOnlyIndex index) => (bool?)index[SqlServerAnnotationNames.CreatedOnline]; /// @@ -183,7 +183,7 @@ public static void SetIsCreatedOnline([NotNull] this IMutableIndex index, bool? /// /// The index. /// if the index is online. - public static int? GetFillFactor([NotNull] this IIndex index) + public static int? GetFillFactor([NotNull] this IReadOnlyIndex index) => (int?)index[SqlServerAnnotationNames.FillFactor]; /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs index 1e37cdef8dc..70f35eabda8 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerKeyExtensions.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for SQL Server-specific metadata. + /// Key extension methods for SQL Server-specific metadata. /// public static class SqlServerKeyExtensions { @@ -20,7 +20,7 @@ public static class SqlServerKeyExtensions /// /// The key. /// if the key is clustered. - public static bool? IsClustered([NotNull] this IKey key) + public static bool? IsClustered([NotNull] this IReadOnlyKey key) => (bool?)key[SqlServerAnnotationNames.Clustered]; /// @@ -29,7 +29,7 @@ public static class SqlServerKeyExtensions /// The key. /// The identifier of the store object. /// if the key is clustered. - public static bool? IsClustered([NotNull] this IKey key, in StoreObjectIdentifier storeObject) + public static bool? IsClustered([NotNull] this IReadOnlyKey key, in StoreObjectIdentifier storeObject) { var annotation = key.FindAnnotation(SqlServerAnnotationNames.Clustered); if (annotation != null) @@ -40,7 +40,7 @@ public static class SqlServerKeyExtensions return GetDefaultIsClustered(key, storeObject); } - private static bool? GetDefaultIsClustered([NotNull] IKey key, in StoreObjectIdentifier storeObject) + private static bool? GetDefaultIsClustered([NotNull] IReadOnlyKey key, in StoreObjectIdentifier storeObject) { var sharedTableRootKey = key.FindSharedObjectRootKey(storeObject); return sharedTableRootKey?.IsClustered(storeObject); diff --git a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs index 682f49a3897..9129259dca9 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerModelExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for SQL Server-specific metadata. + /// Model extension methods for SQL Server-specific metadata. /// public static class SqlServerModelExtensions { @@ -26,7 +26,7 @@ public static class SqlServerModelExtensions /// /// The model. /// The name to use for the default hi-lo sequence. - public static string GetHiLoSequenceName([NotNull] this IModel model) + public static string GetHiLoSequenceName([NotNull] this IReadOnlyModel model) => (string?)model[SqlServerAnnotationNames.HiLoSequenceName] ?? DefaultHiLoSequenceName; @@ -75,7 +75,7 @@ public static void SetHiLoSequenceName([NotNull] this IMutableModel model, [CanB /// /// The model. /// The schema to use for the default hi-lo sequence. - public static string? GetHiLoSequenceSchema([NotNull] this IModel model) + public static string? GetHiLoSequenceSchema([NotNull] this IReadOnlyModel model) => (string?)model[SqlServerAnnotationNames.HiLoSequenceSchema]; /// @@ -122,7 +122,7 @@ public static void SetHiLoSequenceSchema([NotNull] this IMutableModel model, [Ca /// /// The model. /// The default identity seed. - public static int GetIdentitySeed([NotNull] this IModel model) + public static int GetIdentitySeed([NotNull] this IReadOnlyModel model) => (int?)model[SqlServerAnnotationNames.IdentitySeed] ?? 1; /// @@ -165,7 +165,7 @@ public static void SetIdentitySeed([NotNull] this IMutableModel model, int? seed /// /// The model. /// The default identity increment. - public static int GetIdentityIncrement([NotNull] this IModel model) + public static int GetIdentityIncrement([NotNull] this IReadOnlyModel model) => (int?)model[SqlServerAnnotationNames.IdentityIncrement] ?? 1; /// @@ -212,7 +212,7 @@ public static void SetIdentityIncrement([NotNull] this IMutableModel model, int? /// /// The model. /// The default . - public static SqlServerValueGenerationStrategy? GetValueGenerationStrategy([NotNull] this IModel model) + public static SqlServerValueGenerationStrategy? GetValueGenerationStrategy([NotNull] this IReadOnlyModel model) => (SqlServerValueGenerationStrategy?)model[SqlServerAnnotationNames.ValueGenerationStrategy]; /// @@ -257,7 +257,7 @@ public static void SetValueGenerationStrategy( /// /// The model. /// The maximum size of the database. - public static string? GetDatabaseMaxSize([NotNull] this IModel model) + public static string? GetDatabaseMaxSize([NotNull] this IReadOnlyModel model) => (string?)model[SqlServerAnnotationNames.MaxDatabaseSize]; /// @@ -298,7 +298,7 @@ public static void SetDatabaseMaxSize([NotNull] this IMutableModel model, [CanBe /// /// The model. /// The service tier of the database. - public static string? GetServiceTierSql([NotNull] this IModel model) + public static string? GetServiceTierSql([NotNull] this IReadOnlyModel model) => (string?)model[SqlServerAnnotationNames.ServiceTierSql]; /// @@ -339,7 +339,7 @@ public static void SetServiceTierSql([NotNull] this IMutableModel model, [CanBeN /// /// The model. /// The performance level of the database. - public static string? GetPerformanceLevelSql([NotNull] this IModel model) + public static string? GetPerformanceLevelSql([NotNull] this IReadOnlyModel model) => (string?)model[SqlServerAnnotationNames.PerformanceLevelSql]; /// diff --git a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs index 9a77ec3c28d..648d8ecf9af 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerPropertyExtensions.cs @@ -6,7 +6,6 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; using Microsoft.EntityFrameworkCore.Storage; @@ -18,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for for SQL Server-specific metadata. + /// Property extension methods for SQL Server-specific metadata. /// public static class SqlServerPropertyExtensions { @@ -27,7 +26,7 @@ public static class SqlServerPropertyExtensions /// /// The property. /// The name to use for the hi-lo sequence. - public static string? GetHiLoSequenceName([NotNull] this IProperty property) + public static string? GetHiLoSequenceName([NotNull] this IReadOnlyProperty property) => (string?)property[SqlServerAnnotationNames.HiLoSequenceName]; /// @@ -36,7 +35,7 @@ public static class SqlServerPropertyExtensions /// The property. /// The identifier of the store object. /// The name to use for the hi-lo sequence. - public static string? GetHiLoSequenceName([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetHiLoSequenceName([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(SqlServerAnnotationNames.HiLoSequenceName); if (annotation != null) @@ -93,7 +92,7 @@ public static void SetHiLoSequenceName([NotNull] this IMutableProperty property, /// /// The property. /// The schema to use for the hi-lo sequence. - public static string? GetHiLoSequenceSchema([NotNull] this IProperty property) + public static string? GetHiLoSequenceSchema([NotNull] this IReadOnlyProperty property) => (string?)property[SqlServerAnnotationNames.HiLoSequenceSchema]; /// @@ -102,7 +101,7 @@ public static void SetHiLoSequenceName([NotNull] this IMutableProperty property, /// The property. /// The identifier of the store object. /// The schema to use for the hi-lo sequence. - public static string? GetHiLoSequenceSchema([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static string? GetHiLoSequenceSchema([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(SqlServerAnnotationNames.HiLoSequenceSchema); if (annotation != null) @@ -159,7 +158,7 @@ public static void SetHiLoSequenceSchema([NotNull] this IMutableProperty propert /// /// The property. /// The sequence to use, or if no sequence exists in the model. - public static ISequence? FindHiLoSequence([NotNull] this IProperty property) + public static IReadOnlySequence? FindHiLoSequence([NotNull] this IReadOnlyProperty property) { var model = property.DeclaringEntityType.Model; @@ -178,7 +177,7 @@ public static void SetHiLoSequenceSchema([NotNull] this IMutableProperty propert /// The property. /// The identifier of the store object. /// The sequence to use, or if no sequence exists in the model. - public static ISequence? FindHiLoSequence([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static IReadOnlySequence? FindHiLoSequence([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var model = property.DeclaringEntityType.Model; @@ -191,12 +190,29 @@ public static void SetHiLoSequenceSchema([NotNull] this IMutableProperty propert return model.FindSequence(sequenceName, sequenceSchema); } + /// + /// Finds the in the model to use for the hi-lo pattern. + /// + /// The property. + /// The sequence to use, or if no sequence exists in the model. + public static ISequence? FindHiLoSequence([NotNull] this IProperty property) + => (ISequence?)((IReadOnlyProperty)property).FindHiLoSequence(); + + /// + /// Finds the in the model to use for the hi-lo pattern. + /// + /// The property. + /// The identifier of the store object. + /// The sequence to use, or if no sequence exists in the model. + public static ISequence? FindHiLoSequence([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + => (ISequence?)((IReadOnlyProperty)property).FindHiLoSequence(storeObject); + /// /// Returns the identity seed. /// /// The property. /// The identity seed. - public static int? GetIdentitySeed([NotNull] this IProperty property) + public static int? GetIdentitySeed([NotNull] this IReadOnlyProperty property) => (int?)property[SqlServerAnnotationNames.IdentitySeed]; /// @@ -205,7 +221,7 @@ public static void SetHiLoSequenceSchema([NotNull] this IMutableProperty propert /// The property. /// The identifier of the store object. /// The identity seed. - public static int? GetIdentitySeed([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static int? GetIdentitySeed([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(SqlServerAnnotationNames.IdentitySeed); if (annotation != null) @@ -262,7 +278,7 @@ public static void SetIdentitySeed([NotNull] this IMutableProperty property, int /// /// The property. /// The identity increment. - public static int? GetIdentityIncrement([NotNull] this IProperty property) + public static int? GetIdentityIncrement([NotNull] this IReadOnlyProperty property) => (int?)property[SqlServerAnnotationNames.IdentityIncrement]; /// @@ -271,7 +287,7 @@ public static void SetIdentitySeed([NotNull] this IMutableProperty property, int /// The property. /// The identifier of the store object. /// The identity increment. - public static int? GetIdentityIncrement([NotNull] this IProperty property, in StoreObjectIdentifier storeObject) + public static int? GetIdentityIncrement([NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(SqlServerAnnotationNames.IdentityIncrement); if (annotation != null) @@ -333,7 +349,7 @@ public static void SetIdentityIncrement([NotNull] this IMutableProperty property /// /// The property. /// The strategy, or if none was set. - public static SqlServerValueGenerationStrategy GetValueGenerationStrategy([NotNull] this IProperty property) + public static SqlServerValueGenerationStrategy GetValueGenerationStrategy([NotNull] this IReadOnlyProperty property) { var annotation = property.FindAnnotation(SqlServerAnnotationNames.ValueGenerationStrategy); if (annotation != null) @@ -365,12 +381,12 @@ public static SqlServerValueGenerationStrategy GetValueGenerationStrategy([NotNu /// The identifier of the store object. /// The strategy, or if none was set. public static SqlServerValueGenerationStrategy GetValueGenerationStrategy( - [NotNull] this IProperty property, + [NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) => GetValueGenerationStrategy(property, storeObject, null); internal static SqlServerValueGenerationStrategy GetValueGenerationStrategy( - [NotNull] this IProperty property, + [NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject, ITypeMappingSource? typeMappingSource) { @@ -402,7 +418,7 @@ internal static SqlServerValueGenerationStrategy GetValueGenerationStrategy( return GetDefaultValueGenerationStrategy(property, storeObject, typeMappingSource); } - private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrategy(IProperty property) + private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrategy(IReadOnlyProperty property) { var modelStrategy = property.DeclaringEntityType.Model.GetValueGenerationStrategy(); @@ -419,7 +435,7 @@ private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrateg } private static SqlServerValueGenerationStrategy GetDefaultValueGenerationStrategy( - IProperty property, + IReadOnlyProperty property, in StoreObjectIdentifier storeObject, ITypeMappingSource? typeMappingSource) { @@ -470,7 +486,7 @@ public static void SetValueGenerationStrategy( return value; } - private static void CheckValueGenerationStrategy(IProperty property, SqlServerValueGenerationStrategy? value) + private static void CheckValueGenerationStrategy(IReadOnlyProperty property, SqlServerValueGenerationStrategy? value) { if (value != null) { @@ -508,7 +524,7 @@ private static void CheckValueGenerationStrategy(IProperty property, SqlServerVa /// /// The property. /// if compatible. - public static bool IsCompatibleWithValueGeneration([NotNull] IProperty property) + public static bool IsCompatibleWithValueGeneration([NotNull] IReadOnlyProperty property) { var type = property.ClrType; @@ -520,7 +536,7 @@ public static bool IsCompatibleWithValueGeneration([NotNull] IProperty property) } private static bool IsCompatibleWithValueGeneration( - [NotNull] IProperty property, + [NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject, ITypeMappingSource? typeMappingSource) { @@ -530,7 +546,7 @@ private static bool IsCompatibleWithValueGeneration( || type == typeof(decimal)) && (property.GetValueConverter() ?? (property.FindRelationalTypeMapping(storeObject) - ?? typeMappingSource?.FindMapping(property))?.Converter) + ?? typeMappingSource?.FindMapping((IProperty)property))?.Converter) == null; } @@ -539,7 +555,7 @@ private static bool IsCompatibleWithValueGeneration( /// /// The property. /// if the property's column is sparse. - public static bool? IsSparse([NotNull] this IProperty property) + public static bool? IsSparse([NotNull] this IReadOnlyProperty property) => (bool?)property[SqlServerAnnotationNames.Sparse]; /// diff --git a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs index 6fea8048f6e..acefe5538e3 100644 --- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs +++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs @@ -93,12 +93,12 @@ protected virtual void ValidateDecimalColumns( && (ConfigurationSource.Convention.Overrides(property.GetPrecisionConfigurationSource()) || ConfigurationSource.Convention.Overrides(property.GetScaleConfigurationSource()))) { - logger.DecimalTypeDefaultWarning(property); + logger.DecimalTypeDefaultWarning((IProperty)property); } if (property.IsKey()) { - logger.DecimalTypeKeyWarning(property); + logger.DecimalTypeKeyWarning((IProperty)property); } } } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs index d6fef269e6a..33a7dd60188 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerDbFunctionConvention.cs @@ -12,8 +12,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { /// - /// A convention that ensures that is populated for database functions which - /// have flag set to . + /// A convention that ensures that is populated for database functions which + /// have flag set to . /// public class SqlServerDbFunctionConvention : IModelFinalizingConvention { diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs index 71752d77e12..5790a995b99 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerIndexConvention.cs @@ -194,7 +194,7 @@ private string CreateIndexFilter(List nullableColumns) return builder.ToString(); } - private List? GetNullableColumns(IIndex index) + private List? GetNullableColumns(IReadOnlyIndex index) { var tableName = index.DeclaringEntityType.GetTableName(); if (tableName == null) diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs index ff4c02aca92..b9ecad244ed 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerSharedTableConvention.cs @@ -28,12 +28,12 @@ public SqlServerSharedTableConvention( } /// - protected override bool AreCompatible(IKey key, IKey duplicateKey, in StoreObjectIdentifier storeObject) + protected override bool AreCompatible(IReadOnlyKey key, IReadOnlyKey duplicateKey, in StoreObjectIdentifier storeObject) => base.AreCompatible(key, duplicateKey, storeObject) && key.AreCompatibleForSqlServer(duplicateKey, storeObject, shouldThrow: false); /// - protected override bool AreCompatible(IIndex index, IIndex duplicateIndex, in StoreObjectIdentifier storeObject) + protected override bool AreCompatible(IReadOnlyIndex index, IReadOnlyIndex duplicateIndex, in StoreObjectIdentifier storeObject) => base.AreCompatible(index, duplicateIndex, storeObject) && index.AreCompatibleForSqlServer(duplicateIndex, storeObject, shouldThrow: false); } diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs index 81bca81262e..6a3d673de21 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationConvention.cs @@ -81,14 +81,14 @@ public override void ProcessPropertyAnnotationChanged( /// The property. /// The identifier of the store object. /// The store value generation strategy to set for the given property. - public static new ValueGenerated? GetValueGenerated([NotNull] IProperty property, in StoreObjectIdentifier storeObject) + public static new ValueGenerated? GetValueGenerated([NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject) => RelationalValueGenerationConvention.GetValueGenerated(property, storeObject) ?? (property.GetValueGenerationStrategy(storeObject) != SqlServerValueGenerationStrategy.None ? ValueGenerated.OnAdd : (ValueGenerated?)null); private ValueGenerated? GetValueGenerated( - [NotNull] IProperty property, + [NotNull] IReadOnlyProperty property, in StoreObjectIdentifier storeObject, ITypeMappingSource typeMappingSource) => RelationalValueGenerationConvention.GetValueGenerated(property, storeObject) diff --git a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs index 32d85583843..a8ee05d9c18 100644 --- a/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs +++ b/src/EFCore.SqlServer/Metadata/Conventions/SqlServerValueGenerationStrategyConvention.cs @@ -87,7 +87,7 @@ public virtual void ProcessModelFinalizing( } } - bool IsStrategyNoneNeeded(IProperty property, StoreObjectIdentifier storeObject) + bool IsStrategyNoneNeeded(IReadOnlyProperty property, StoreObjectIdentifier storeObject) { if (property.ValueGenerated == ValueGenerated.OnAdd && property.GetDefaultValue(storeObject) == null @@ -97,7 +97,7 @@ bool IsStrategyNoneNeeded(IProperty property, StoreObjectIdentifier storeObject) { var providerClrType = (property.GetValueConverter() ?? (property.FindRelationalTypeMapping(storeObject) - ?? Dependencies.TypeMappingSource.FindMapping(property))?.Converter) + ?? Dependencies.TypeMappingSource.FindMapping((IProperty)property))?.Converter) ?.ProviderClrType.UnwrapNullableType(); return providerClrType != null diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs index 1dd76a2f41a..7f2d8f31021 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerIndexExtensions.cs @@ -25,8 +25,8 @@ public static class SqlServerIndexExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool AreCompatibleForSqlServer( - [NotNull] this IIndex index, - [NotNull] IIndex duplicateIndex, + [NotNull] this IReadOnlyIndex index, + [NotNull] IReadOnlyIndex duplicateIndex, in StoreObjectIdentifier storeObject, bool shouldThrow) { @@ -107,7 +107,7 @@ public static bool AreCompatibleForSqlServer( return true; - static bool SameColumnNames(IIndex index, IIndex duplicateIndex, StoreObjectIdentifier storeObject) + static bool SameColumnNames(IReadOnlyIndex index, IReadOnlyIndex duplicateIndex, StoreObjectIdentifier storeObject) => index.GetIncludeProperties()!.Select( p => index.DeclaringEntityType.FindProperty(p)!.GetColumnName(storeObject)) .SequenceEqual( @@ -115,7 +115,7 @@ static bool SameColumnNames(IIndex index, IIndex duplicateIndex, StoreObjectIden p => duplicateIndex.DeclaringEntityType.FindProperty(p)!.GetColumnName(storeObject))); } - private static string FormatInclude(IIndex index, StoreObjectIdentifier storeObject) + private static string FormatInclude(IReadOnlyIndex index, StoreObjectIdentifier storeObject) => index.GetIncludeProperties() == null ? "{}" : "{'" diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerKeyExtensions.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerKeyExtensions.cs index 705c0b4e073..78d8af7852d 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerKeyExtensions.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerKeyExtensions.cs @@ -24,8 +24,8 @@ public static class SqlServerKeyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static bool AreCompatibleForSqlServer( - [NotNull] this IKey key, - [NotNull] IKey duplicateKey, + [NotNull] this IReadOnlyKey key, + [NotNull] IReadOnlyKey duplicateKey, in StoreObjectIdentifier storeObject, bool shouldThrow) { diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs index ebdaa02b1fa..0a478918bd7 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerDatabaseCreator.cs @@ -8,6 +8,7 @@ using System.Transactions; using JetBrains.Annotations; using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.EntityFrameworkCore.SqlServer.Internal; @@ -182,7 +183,8 @@ private IReadOnlyList CreateCreateOperations() FileName = builder.AttachDBFilename, Collation = Dependencies.Model.GetCollation() } - }); + }, + null); } /// @@ -389,7 +391,7 @@ private IReadOnlyList CreateDropCommands() var operations = new MigrationOperation[] { new SqlServerDropDatabaseOperation { Name = databaseName } }; - return Dependencies.MigrationsSqlGenerator.Generate(operations); + return Dependencies.MigrationsSqlGenerator.Generate(operations, null); } // Clear connection pools in case there are active connections that are pooled diff --git a/src/EFCore.Sqlite.Core/Extensions/Internal/SqliteLoggerExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/Internal/SqliteLoggerExtensions.cs index 9985e4d90b6..f03fa06db31 100644 --- a/src/EFCore.Sqlite.Core/Extensions/Internal/SqliteLoggerExtensions.cs +++ b/src/EFCore.Sqlite.Core/Extensions/Internal/SqliteLoggerExtensions.cs @@ -65,7 +65,7 @@ private static string SchemaConfiguredWarning(EventDefinitionBase definition, Ev /// public static void SequenceConfiguredWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] ISequence sequence) + [NotNull] IReadOnlySequence sequence) { var definition = SqliteResources.LogSequenceConfigured(diagnostics); diff --git a/src/EFCore.Sqlite.Core/Extensions/SqlitePropertyExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqlitePropertyExtensions.cs index 47c0737aaf2..2ef7d155b28 100644 --- a/src/EFCore.Sqlite.Core/Extensions/SqlitePropertyExtensions.cs +++ b/src/EFCore.Sqlite.Core/Extensions/SqlitePropertyExtensions.cs @@ -20,7 +20,7 @@ public static class SqlitePropertyExtensions /// /// The property. /// The SRID to use when creating a column for this property. - public static int? GetSrid([NotNull] this IProperty property) + public static int? GetSrid([NotNull] this IReadOnlyProperty property) => (int?)property[SqliteAnnotationNames.Srid]; /// @@ -30,7 +30,7 @@ public static class SqlitePropertyExtensions /// The identifier of the store object. /// The SRID to use when creating a column for this property. public static int? GetSrid( - [NotNull] this IProperty property, + [NotNull] this IReadOnlyProperty property, in StoreObjectIdentifier storeObject) { var annotation = property.FindAnnotation(SqliteAnnotationNames.Srid); diff --git a/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs b/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs index 016529664cf..309e4fed4a1 100644 --- a/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs +++ b/src/EFCore.Sqlite.Core/Query/Internal/SqliteRegexMethodTranslator.cs @@ -23,7 +23,7 @@ namespace Microsoft.EntityFrameworkCore.Sqlite.Query.Internal /// public class SqliteRegexMethodTranslator : IMethodCallTranslator { - private readonly static MethodInfo _regexIsMatchMethodInfo + private static readonly MethodInfo _regexIsMatchMethodInfo = typeof(Regex).GetRequiredRuntimeMethod(nameof(Regex.IsMatch), new Type[] { typeof(string), typeof(string) }); private readonly ISqlExpressionFactory _sqlExpressionFactory; diff --git a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs index 3a15368e0d9..9796a9c98c2 100644 --- a/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs @@ -64,7 +64,7 @@ public override void SetValues(object obj) { if (!Properties[i].IsShadowProperty()) { - SetValue(i, ((Property)Properties[i]).Getter.GetClrValue(obj)); + SetValue(i, Properties[i].GetGetter().GetClrValue(obj)); } } } diff --git a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs index d153f5992a3..7d0515ef623 100644 --- a/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs +++ b/src/EFCore/ChangeTracking/Internal/EntryPropertyValues.cs @@ -57,7 +57,7 @@ public override void SetValues(object obj) { foreach (var property in Properties.Where(p => !p.IsShadowProperty())) { - SetValueInternal(property, ((Property)property).Getter.GetClrValue(obj)); + SetValueInternal(property, property.GetGetter().GetClrValue(obj)); } } else diff --git a/src/EFCore/ChangeTracking/Internal/IdentityMap.cs b/src/EFCore/ChangeTracking/Internal/IdentityMap.cs index ff260eb0677..0e77f5a37d6 100644 --- a/src/EFCore/ChangeTracking/Internal/IdentityMap.cs +++ b/src/EFCore/ChangeTracking/Internal/IdentityMap.cs @@ -47,7 +47,7 @@ public IdentityMap( { _foreignKeys = key.DeclaringEntityType .GetDerivedTypesInclusive() - .SelectMany(EntityTypeExtensions.GetDeclaredForeignKeys) + .SelectMany(t => t.GetDeclaredForeignKeys()) .ToArray(); } } diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs index e33f16faf63..73f5ddcc722 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs @@ -743,7 +743,7 @@ protected virtual object ReadPropertyValue([NotNull] IPropertyBase propertyBase) { Check.DebugAssert(!propertyBase.IsShadowProperty(), "propertyBase is shadow property"); - return ((PropertyBase)propertyBase).Getter.GetClrValue(Entity); + return propertyBase.GetGetter().GetClrValue(Entity); } /// @@ -756,7 +756,7 @@ protected virtual bool PropertyHasDefaultValue([NotNull] IPropertyBase propertyB { Check.DebugAssert(!propertyBase.IsShadowProperty(), "propertyBase is shadow property"); - return ((PropertyBase)propertyBase).Getter.HasDefaultValue(Entity); + return propertyBase.GetGetter().HasDefaultValue(Entity); } /// @@ -1620,7 +1620,7 @@ public virtual void HandleINotifyPropertyChanging( [NotNull] object sender, [NotNull] PropertyChangingEventArgs eventArgs) { - foreach (var propertyBase in EntityType.GetNotificationProperties(eventArgs.PropertyName)) + foreach (var propertyBase in GetNotificationProperties(EntityType, eventArgs.PropertyName)) { StateManager.InternalEntityEntryNotifier.PropertyChanging(this, propertyBase); } @@ -1636,12 +1636,48 @@ public virtual void HandleINotifyPropertyChanged( [NotNull] object sender, [NotNull] PropertyChangedEventArgs eventArgs) { - foreach (var propertyBase in EntityType.GetNotificationProperties(eventArgs.PropertyName)) + foreach (var propertyBase in GetNotificationProperties(EntityType, eventArgs.PropertyName)) { StateManager.InternalEntityEntryNotifier.PropertyChanged(this, propertyBase, setModified: true); } } + private static IEnumerable GetNotificationProperties( + [NotNull] IEntityType entityType, + [CanBeNull] string propertyName) + { + if (string.IsNullOrEmpty(propertyName)) + { + foreach (var property in entityType.GetProperties() + .Where(p => p.GetAfterSaveBehavior() == PropertySaveBehavior.Save)) + { + yield return property; + } + + foreach (var navigation in entityType.GetNavigations()) + { + yield return navigation; + } + + foreach (var navigation in entityType.GetSkipNavigations()) + { + yield return navigation; + } + } + else + { + // ReSharper disable once AssignNullToNotNullAttribute + var property = entityType.FindProperty(propertyName) + ?? entityType.FindNavigation(propertyName) + ?? (IPropertyBase)entityType.FindSkipNavigation(propertyName); + + if (property != null) + { + yield return property; + } + } + } + /// /// 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/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs index 1076a671f64..f77f9ace9c4 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs @@ -177,8 +177,7 @@ public virtual void StateChanging(InternalEntityEntry entry, EntityState newStat /// 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 IModel Model - => _model; + public virtual IModel Model => _model; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs b/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs index 32582267b9e..0ce41b284a5 100644 --- a/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs +++ b/src/EFCore/ChangeTracking/Internal/ValueGenerationManager.cs @@ -61,7 +61,7 @@ public ValueGenerationManager( public virtual InternalEntityEntry Propagate(InternalEntityEntry entry) { InternalEntityEntry chosenPrincipal = null; - foreach (var property in FindCandidatePropagatingProperties(entry)) + foreach (var property in entry.EntityType.GetForeignKeyProperties()) { if (!entry.HasDefaultValue(property)) { @@ -88,7 +88,7 @@ public virtual void Generate(InternalEntityEntry entry, bool includePrimaryKey = { var entityEntry = new EntityEntry(entry); - foreach (var property in FindCandidateGeneratingProperties(entry)) + foreach (var property in entry.EntityType.GetValueGeneratingProperties()) { if (!entry.HasDefaultValue(property) || (!includePrimaryKey @@ -137,7 +137,7 @@ public virtual async Task GenerateAsync( { var entityEntry = new EntityEntry(entry); - foreach (var property in FindCandidateGeneratingProperties(entry)) + foreach (var property in entry.EntityType.GetValueGeneratingProperties()) { if (!entry.HasDefaultValue(property) || (!includePrimaryKey @@ -161,12 +161,6 @@ public virtual async Task GenerateAsync( } } - private IReadOnlyList FindCandidatePropagatingProperties(InternalEntityEntry entry) - => entry.EntityType.GetPropagatingProperties(); - - private IReadOnlyList FindCandidateGeneratingProperties(InternalEntityEntry entry) - => entry.EntityType.GetGeneratingProperties(); - private ValueGenerator GetValueGenerator(InternalEntityEntry entry, IProperty property) => _valueGeneratorSelector.Select( property, property.IsKey() diff --git a/src/EFCore/Diagnostics/CollectionChangedEventData.cs b/src/EFCore/Diagnostics/CollectionChangedEventData.cs index 1f0e97aefa9..c2e068f6fa7 100644 --- a/src/EFCore/Diagnostics/CollectionChangedEventData.cs +++ b/src/EFCore/Diagnostics/CollectionChangedEventData.cs @@ -50,6 +50,11 @@ public CollectionChangedEventData( /// public virtual EntityEntry EntityEntry { get; } + /// + /// The navigation. + /// + public new virtual INavigation Navigation => (INavigation)base.Navigation; + /// /// The entities added to the collection. /// diff --git a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs index b34d4bd5053..0d2acd34fca 100644 --- a/src/EFCore/Diagnostics/CoreLoggerExtensions.cs +++ b/src/EFCore/Diagnostics/CoreLoggerExtensions.cs @@ -1081,8 +1081,8 @@ private static string CollectionWithoutComparer(EventDefinitionBase definition, /// The other index. public static void RedundantIndexRemoved( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] IReadOnlyList redundantIndex, - [NotNull] IReadOnlyList otherIndex) + [NotNull] IReadOnlyList redundantIndex, + [NotNull] IReadOnlyList otherIndex) { var definition = CoreResources.LogRedundantIndexRemoved(diagnostics); @@ -1174,8 +1174,8 @@ public static void IncompatibleMatchingForeignKeyProperties( [NotNull] this IDiagnosticsLogger diagnostics, [NotNull] string dependentToPrincipalNavigationSpecification, [NotNull] string principalToDependentNavigationSpecification, - [NotNull] IReadOnlyList foreignKeyProperties, - [NotNull] IReadOnlyList principalKeyProperties) + [NotNull] IReadOnlyList foreignKeyProperties, + [NotNull] IReadOnlyList principalKeyProperties) { var definition = CoreResources.LogIncompatibleMatchingForeignKeyProperties(diagnostics); @@ -1334,8 +1334,8 @@ private static string NonNullableInverted(EventDefinitionBase definition, EventD [Obsolete] public static void RequiredAttributeOnBothNavigations( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation firstNavigation, - [NotNull] INavigation secondNavigation) + [NotNull] IReadOnlyNavigation firstNavigation, + [NotNull] IReadOnlyNavigation secondNavigation) { var definition = CoreResources.LogRequiredAttributeOnBothNavigations(diagnostics); @@ -1384,8 +1384,8 @@ private static string RequiredAttributeOnBothNavigations(EventDefinitionBase def [Obsolete] public static void NonNullableReferenceOnBothNavigations( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation firstNavigation, - [NotNull] INavigation secondNavigation) + [NotNull] IReadOnlyNavigation firstNavigation, + [NotNull] IReadOnlyNavigation secondNavigation) { var definition = CoreResources.LogNonNullableReferenceOnBothNavigations(diagnostics); @@ -1509,7 +1509,7 @@ private static string NonNullableReferenceOnDependent(EventDefinitionBase defini /// The navigation property. public static void RequiredAttributeOnCollection( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation navigation) + [NotNull] IReadOnlyNavigation navigation) { var definition = CoreResources.LogRequiredAttributeOnCollection(diagnostics); @@ -1543,7 +1543,7 @@ private static string RequiredAttributeOnCollection(EventDefinitionBase definiti /// The navigation property. public static void RequiredAttributeOnSkipNavigation( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] ISkipNavigation navigation) + [NotNull] IReadOnlySkipNavigation navigation) { var definition = CoreResources.LogRequiredAttributeOnSkipNavigation(diagnostics); @@ -1577,7 +1577,7 @@ private static string RequiredAttributeOnSkipNavigation(EventDefinitionBase defi /// The foreign key. public static void ConflictingShadowForeignKeysWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] IForeignKey foreignKey) + [NotNull] IReadOnlyForeignKey foreignKey) { var definition = CoreResources.LogConflictingShadowForeignKeys(diagnostics); @@ -1620,8 +1620,8 @@ private static string ConflictingShadowForeignKeysWarning(EventDefinitionBase de /// The second property. public static void MultiplePrimaryKeyCandidates( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] IProperty firstProperty, - [NotNull] IProperty secondProperty) + [NotNull] IReadOnlyProperty firstProperty, + [NotNull] IReadOnlyProperty secondProperty) { var definition = CoreResources.LogMultiplePrimaryKeyCandidates(diagnostics); @@ -1822,9 +1822,9 @@ private static string NonDefiningInverseNavigationWarning(EventDefinitionBase de /// The ownership navigation property. public static void NonOwnershipInverseNavigationWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] IEntityType declaringType, + [NotNull] IReadOnlyEntityType declaringType, [NotNull] MemberInfo navigation, - [NotNull] IEntityType targetType, + [NotNull] IReadOnlyEntityType targetType, [NotNull] MemberInfo inverseNavigation, [NotNull] MemberInfo ownershipNavigation) { @@ -1882,8 +1882,8 @@ private static string NonOwnershipInverseNavigationWarning(EventDefinitionBase d /// The second property. public static void ForeignKeyAttributesOnBothPropertiesWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation firstNavigation, - [NotNull] INavigation secondNavigation, + [NotNull] IReadOnlyNavigation firstNavigation, + [NotNull] IReadOnlyNavigation secondNavigation, [NotNull] MemberInfo firstProperty, [NotNull] MemberInfo secondProperty) { @@ -1948,8 +1948,8 @@ private static string ForeignKeyAttributesOnBothPropertiesWarning(EventDefinitio /// The second navigation property. public static void ForeignKeyAttributesOnBothNavigationsWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation firstNavigation, - [NotNull] INavigation secondNavigation) + [NotNull] IReadOnlyNavigation firstNavigation, + [NotNull] IReadOnlyNavigation secondNavigation) { var definition = CoreResources.LogForeignKeyAttributesOnBothNavigations(diagnostics); @@ -1996,7 +1996,7 @@ private static string ForeignKeyAttributesOnBothNavigationsWarning(EventDefiniti /// The property. public static void ConflictingForeignKeyAttributesOnNavigationAndPropertyWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] INavigation navigation, + [NotNull] IReadOnlyNavigation navigation, [NotNull] MemberInfo property) { var definition = CoreResources.LogConflictingForeignKeyAttributesOnNavigationAndProperty(diagnostics); @@ -3311,7 +3311,7 @@ private static string ContextDisposed(EventDefinitionBase definition, EventData /// The property which is being defined as part of a key. public static void ConflictingKeylessAndKeyAttributesWarning( [NotNull] this IDiagnosticsLogger diagnostics, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) { var definition = CoreResources.LogConflictingKeylessAndKeyAttributes(diagnostics); diff --git a/src/EFCore/Diagnostics/ForeignKeyCandidateEventData.cs b/src/EFCore/Diagnostics/ForeignKeyCandidateEventData.cs index cbb49f78099..74e9fbcdaf1 100644 --- a/src/EFCore/Diagnostics/ForeignKeyCandidateEventData.cs +++ b/src/EFCore/Diagnostics/ForeignKeyCandidateEventData.cs @@ -35,8 +35,8 @@ public ForeignKeyCandidateEventData( [NotNull] Func messageGenerator, [NotNull] string dependentToPrincipalNavigationSpecification, [NotNull] string principalToDependentNavigationSpecification, - [NotNull] IReadOnlyList firstPropertyCollection, - [NotNull] IReadOnlyList secondPropertyCollection) + [NotNull] IReadOnlyList firstPropertyCollection, + [NotNull] IReadOnlyList secondPropertyCollection) : base(eventDefinition, messageGenerator, firstPropertyCollection, secondPropertyCollection) { DependentToPrincipalNavigationSpecification = dependentToPrincipalNavigationSpecification; diff --git a/src/EFCore/Diagnostics/ForeignKeyEventData.cs b/src/EFCore/Diagnostics/ForeignKeyEventData.cs index e219303b9f9..e4fc9bdaf5a 100644 --- a/src/EFCore/Diagnostics/ForeignKeyEventData.cs +++ b/src/EFCore/Diagnostics/ForeignKeyEventData.cs @@ -23,7 +23,7 @@ public class ForeignKeyEventData : EventData public ForeignKeyEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] IForeignKey foreignKey) + [NotNull] IReadOnlyForeignKey foreignKey) : base(eventDefinition, messageGenerator) { ForeignKey = foreignKey; @@ -32,6 +32,6 @@ public ForeignKeyEventData( /// /// The foreign key. /// - public virtual IForeignKey ForeignKey { get; } + public virtual IReadOnlyForeignKey ForeignKey { get; } } } diff --git a/src/EFCore/Diagnostics/NavigationBaseEventData.cs b/src/EFCore/Diagnostics/NavigationBaseEventData.cs index f958891c99a..d8aa645b521 100644 --- a/src/EFCore/Diagnostics/NavigationBaseEventData.cs +++ b/src/EFCore/Diagnostics/NavigationBaseEventData.cs @@ -22,7 +22,7 @@ public class NavigationBaseEventData : EventData, INavigationBaseEventData public NavigationBaseEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] INavigationBase navigationBase) + [NotNull] IReadOnlyNavigationBase navigationBase) : base(eventDefinition, messageGenerator) { NavigationBase = navigationBase; @@ -31,6 +31,9 @@ public NavigationBaseEventData( /// /// The navigation base. /// - public virtual INavigationBase NavigationBase { get; } + public virtual IReadOnlyNavigationBase NavigationBase { get; } + + INavigationBase INavigationBaseEventData.NavigationBase + => (INavigationBase)NavigationBase; } } diff --git a/src/EFCore/Diagnostics/NavigationEventData.cs b/src/EFCore/Diagnostics/NavigationEventData.cs index 2d3c36e4ad4..e07508e2a07 100644 --- a/src/EFCore/Diagnostics/NavigationEventData.cs +++ b/src/EFCore/Diagnostics/NavigationEventData.cs @@ -22,7 +22,7 @@ public class NavigationEventData : EventData, INavigationBaseEventData public NavigationEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] INavigation navigation) + [NotNull] IReadOnlyNavigation navigation) : base(eventDefinition, messageGenerator) { Navigation = navigation; @@ -31,12 +31,12 @@ public NavigationEventData( /// /// The navigation. /// - public virtual INavigation Navigation { get; } + public virtual IReadOnlyNavigation Navigation { get; } /// /// The navigation. /// INavigationBase INavigationBaseEventData.NavigationBase - => Navigation; + => (INavigationBase)Navigation; } } diff --git a/src/EFCore/Diagnostics/PropertyChangedEventData.cs b/src/EFCore/Diagnostics/PropertyChangedEventData.cs index 657c2f27a30..544ece26f03 100644 --- a/src/EFCore/Diagnostics/PropertyChangedEventData.cs +++ b/src/EFCore/Diagnostics/PropertyChangedEventData.cs @@ -46,6 +46,11 @@ public PropertyChangedEventData( /// public virtual EntityEntry EntityEntry { get; } + /// + /// The property. + /// + public new virtual IProperty Property => (IProperty)base.Property; + /// /// The old value. /// diff --git a/src/EFCore/Diagnostics/PropertyEventData.cs b/src/EFCore/Diagnostics/PropertyEventData.cs index c236c7ceacd..2401f957fd8 100644 --- a/src/EFCore/Diagnostics/PropertyEventData.cs +++ b/src/EFCore/Diagnostics/PropertyEventData.cs @@ -23,7 +23,7 @@ public class PropertyEventData : EventData public PropertyEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) : base(eventDefinition, messageGenerator) { Property = property; @@ -32,6 +32,6 @@ public PropertyEventData( /// /// The property. /// - public virtual IProperty Property { get; } + public virtual IReadOnlyProperty Property { get; } } } diff --git a/src/EFCore/Diagnostics/PropertyValueEventData.cs b/src/EFCore/Diagnostics/PropertyValueEventData.cs index 1dcd2b74522..48df93887e0 100644 --- a/src/EFCore/Diagnostics/PropertyValueEventData.cs +++ b/src/EFCore/Diagnostics/PropertyValueEventData.cs @@ -43,6 +43,11 @@ public PropertyValueEventData( /// public virtual EntityEntry EntityEntry { get; } + /// + /// The property. + /// + public new virtual IProperty Property => (IProperty)base.Property; + /// /// The value. /// diff --git a/src/EFCore/Diagnostics/ReferenceChangedEventData.cs b/src/EFCore/Diagnostics/ReferenceChangedEventData.cs index d3a274c0776..7f243ff3e6b 100644 --- a/src/EFCore/Diagnostics/ReferenceChangedEventData.cs +++ b/src/EFCore/Diagnostics/ReferenceChangedEventData.cs @@ -46,6 +46,11 @@ public ReferenceChangedEventData( /// public virtual EntityEntry EntityEntry { get; } + /// + /// The navigation. + /// + public new virtual INavigation Navigation => (INavigation)base.Navigation; + /// /// The old referenced entity. /// diff --git a/src/EFCore/Diagnostics/SkipCollectionChangedEventData.cs b/src/EFCore/Diagnostics/SkipCollectionChangedEventData.cs index e6f6de5d96b..18f893fd1e9 100644 --- a/src/EFCore/Diagnostics/SkipCollectionChangedEventData.cs +++ b/src/EFCore/Diagnostics/SkipCollectionChangedEventData.cs @@ -50,6 +50,11 @@ public SkipCollectionChangedEventData( /// public virtual EntityEntry EntityEntry { get; } + /// + /// The navigation. + /// + public new virtual ISkipNavigation Navigation => (ISkipNavigation)base.Navigation; + /// /// The entities added to the collection. /// diff --git a/src/EFCore/Diagnostics/SkipNavigationEventData.cs b/src/EFCore/Diagnostics/SkipNavigationEventData.cs index 968f8c29c1d..89b6333395a 100644 --- a/src/EFCore/Diagnostics/SkipNavigationEventData.cs +++ b/src/EFCore/Diagnostics/SkipNavigationEventData.cs @@ -22,7 +22,7 @@ public class SkipNavigationEventData : EventData, INavigationBaseEventData public SkipNavigationEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] ISkipNavigation navigation) + [NotNull] IReadOnlySkipNavigation navigation) : base(eventDefinition, messageGenerator) { Navigation = navigation; @@ -31,12 +31,12 @@ public SkipNavigationEventData( /// /// The navigation. /// - public virtual ISkipNavigation Navigation { get; } + public virtual IReadOnlySkipNavigation Navigation { get; } /// /// The navigation. /// INavigationBase INavigationBaseEventData.NavigationBase - => Navigation; + => (INavigationBase)Navigation; } } diff --git a/src/EFCore/Diagnostics/TwoPropertyBaseCollectionsEventData.cs b/src/EFCore/Diagnostics/TwoPropertyBaseCollectionsEventData.cs index f77ff4f3ead..0f6ea5eab8f 100644 --- a/src/EFCore/Diagnostics/TwoPropertyBaseCollectionsEventData.cs +++ b/src/EFCore/Diagnostics/TwoPropertyBaseCollectionsEventData.cs @@ -25,8 +25,8 @@ public class TwoPropertyBaseCollectionsEventData : EventData public TwoPropertyBaseCollectionsEventData( [NotNull] EventDefinitionBase eventDefinition, [NotNull] Func messageGenerator, - [NotNull] IReadOnlyList firstPropertyCollection, - [NotNull] IReadOnlyList secondPropertyCollection) + [NotNull] IReadOnlyList firstPropertyCollection, + [NotNull] IReadOnlyList secondPropertyCollection) : base(eventDefinition, messageGenerator) { FirstPropertyCollection = firstPropertyCollection; @@ -36,11 +36,11 @@ public TwoPropertyBaseCollectionsEventData( /// /// The first property collection. /// - public virtual IReadOnlyList FirstPropertyCollection { get; } + public virtual IReadOnlyList FirstPropertyCollection { get; } /// /// The second property collection. /// - public virtual IReadOnlyList SecondPropertyCollection { get; } + public virtual IReadOnlyList SecondPropertyCollection { get; } } } diff --git a/src/EFCore/Extensions/ConventionAnnotatableExtensions.cs b/src/EFCore/Extensions/ConventionAnnotatableExtensions.cs index 4f8fd686513..52d15091077 100644 --- a/src/EFCore/Extensions/ConventionAnnotatableExtensions.cs +++ b/src/EFCore/Extensions/ConventionAnnotatableExtensions.cs @@ -26,7 +26,7 @@ public static class ConventionAnnotatableExtensions public static IConventionAnnotation GetAnnotation( [NotNull] this IConventionAnnotatable annotatable, [NotNull] string annotationName) - => (IConventionAnnotation)((IAnnotatable)annotatable).GetAnnotation(annotationName); + => (IConventionAnnotation)((IReadOnlyAnnotatable)annotatable).GetAnnotation(annotationName); /// /// Adds annotations to an object. diff --git a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs index 34f5113267c..f7322398e0c 100644 --- a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs +++ b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs @@ -32,7 +32,7 @@ public static class ConventionEntityTypeExtensions /// The root base type. If the given entity type is not a derived type, then the same entity type is returned. /// public static IConventionEntityType GetRootType([NotNull] this IConventionEntityType entityType) - => (IConventionEntityType)((IEntityType)entityType).GetRootType(); + => (IConventionEntityType)((IReadOnlyEntityType)entityType).GetRootType(); /// /// Gets all types in the model from which a given entity type derives, starting with the root. @@ -54,6 +54,14 @@ public static IEnumerable GetAllBaseTypes([NotNull] this public static IEnumerable GetAllBaseTypesAscending([NotNull] this IConventionEntityType entityType) => entityType.GetAllBaseTypesInclusiveAscending().Skip(1); + /// + /// Returns all base types of the given entity type, including the type itself, bottom to top. + /// + /// The entity type. + /// Base types. + public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IConventionEntityType entityType) + => ((IReadOnlyEntityType)entityType).GetAllBaseTypesInclusiveAscending().Cast(); + /// /// Gets all types in the model that derive from a given entity type. /// @@ -79,33 +87,16 @@ public static IEnumerable GetDirectlyDerivedTypes([NotNul => ((EntityType)entityType).GetDirectlyDerivedTypes(); /// - /// Returns all base types of the given , including the type itself, top to bottom. + /// Returns all base types of the given , including the type itself, top to bottom. /// /// The entity type. /// Base types. public static IEnumerable GetAllBaseTypesInclusive([NotNull] this IConventionEntityType entityType) - => GetAllBaseTypesInclusiveAscending(entityType).Reverse(); - - /// - /// Returns all base types of the given , including the type itself, bottom to top. - /// - /// The entity type. - /// Base types. - public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IConventionEntityType entityType) - { - Check.NotNull(entityType, nameof(entityType)); - - var tmp = (IConventionEntityType?)entityType; - while (tmp != null) - { - yield return tmp; - tmp = tmp.BaseType; - } - } + => entityType.GetAllBaseTypesInclusiveAscending().Reverse().Cast(); /// /// - /// Gets all keys declared on the given . + /// Gets all keys declared on the given . /// /// /// This method does not return keys declared on base types. @@ -118,6 +109,40 @@ public static IEnumerable GetAllBaseTypesInclusiveAscendi public static IEnumerable GetDeclaredKeys([NotNull] this IConventionEntityType entityType) => ((EntityType)entityType).GetDeclaredKeys(); + /// + /// Gets the primary or alternate key that is defined on the given property. Returns if no key is defined + /// for the given property. + /// + /// The entity type. + /// The property that the key is defined on. + /// The key, or null if none is defined. + public static IConventionKey? FindKey([NotNull] this IConventionEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindKey(new[] { property }); + + /// + /// Adds a new alternate key to this entity type. + /// + /// The entity type. + /// The property to use as an alternate key. + /// Indicates whether the configuration was specified using a data annotation. + /// The newly created key. + public static IConventionKey? AddKey( + [NotNull] this IConventionEntityType entityType, + [NotNull] IConventionProperty property, + bool fromDataAnnotation = false) + => Check.NotNull(entityType, nameof(entityType)).AddKey(new[] { property }, fromDataAnnotation); + + /// + /// Removes a primary or alternate key from this entity type. + /// + /// The entity type. + /// The properties that make up the key. + /// The key that was removed. + public static IConventionKey? RemoveKey( + [NotNull] this IConventionEntityType entityType, + [NotNull] IReadOnlyList properties) + => ((EntityType)entityType).RemoveKey(properties); + /// /// /// Gets all non-navigation properties declared on the given . @@ -133,6 +158,15 @@ public static IEnumerable GetDeclaredKeys([NotNull] this IConven public static IEnumerable GetDeclaredProperties([NotNull] this IConventionEntityType entityType) => ((EntityType)entityType).GetDeclaredProperties(); + /// + /// Removes a property from this entity type. + /// + /// The entity type. + /// The name of the property to remove. + /// The property that was removed. + public static IConventionProperty? RemoveProperty([NotNull] this IConventionEntityType entityType, [NotNull] string name) + => ((EntityType)entityType).RemoveProperty(name); + /// /// /// Gets all navigation properties declared on the given . @@ -170,7 +204,7 @@ public static IEnumerable GetDeclaredServiceProperti /// /// This method does not return indexes declared on base types. /// It is useful when iterating over all entity types to avoid processing the same index more than once. - /// Use to also return indexes declared on base types. + /// Use to also return indexes declared on base types. /// /// /// The entity type. @@ -179,51 +213,52 @@ public static IEnumerable GetDeclaredIndexes([NotNull] this IC => ((EntityType)entityType).GetDeclaredIndexes(); /// - /// Removes a property from this entity type. + /// + /// Gets all indexes declared on the types derived from the given . + /// /// /// The entity type. - /// The name of the property to remove. - /// The property that was removed. - public static IConventionProperty? RemoveProperty([NotNull] this IConventionEntityType entityType, [NotNull] string name) - => ((EntityType)entityType).RemoveProperty(name); + /// Derived indexes. + public static IEnumerable GetDerivedIndexes([NotNull] this IConventionEntityType entityType) + => ((EntityType)entityType).GetDerivedIndexes(); /// - /// Gets the primary or alternate key that is defined on the given property. Returns if no key is defined - /// for the given property. + /// + /// Gets the unnamed index defined on the given property. Returns if no such index is defined. + /// + /// + /// Named indexes will not be returned even if the list of properties matches. + /// /// /// The entity type. - /// The property that the key is defined on. - /// The key, or null if none is defined. - public static IConventionKey? FindKey([NotNull] this IConventionEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindKey(new[] { property }); - } + /// The property to find the index on. + /// The index, or null if none is found. + public static IConventionIndex? FindIndex([NotNull] this IConventionEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindIndex(new[] { property }); /// - /// Adds a new alternate key to this entity type. + /// Adds an index to this entity type. /// /// The entity type. - /// The property to use as an alternate key. + /// The property to be indexed. /// Indicates whether the configuration was specified using a data annotation. - /// The newly created key. - public static IConventionKey? AddKey( + /// The newly created index. + public static IConventionIndex? AddIndex( [NotNull] this IConventionEntityType entityType, [NotNull] IConventionProperty property, bool fromDataAnnotation = false) - => Check.NotNull(entityType, nameof(entityType)).AddKey(new[] { property }, fromDataAnnotation); + => Check.NotNull(entityType, nameof(entityType)).AddIndex(new[] { property }, fromDataAnnotation); /// - /// Removes a primary or alternate key from this entity type. + /// Removes an index from this entity type. /// /// The entity type. - /// The properties that make up the key. - /// The key that was removed. - public static IConventionKey? RemoveKey( + /// The properties that make up the index. + /// The index that was removed. + public static IConventionIndex? RemoveIndex( [NotNull] this IConventionEntityType entityType, [NotNull] IReadOnlyList properties) - => ((EntityType)entityType).RemoveKey(properties); + => ((EntityType)entityType).RemoveIndex(properties); /// /// @@ -244,11 +279,6 @@ public static IEnumerable GetDeclaredForeignKeys([NotNull /// /// Gets all foreign keys declared on the types derived from the given . /// - /// - /// This method does not return foreign keys declared on the given entity type itself. - /// Use to return foreign keys declared on this - /// and base entity typed types. - /// /// /// The entity type. /// Derived foreign keys. @@ -264,7 +294,7 @@ public static IEnumerable GetDerivedForeignKeys([NotNull] /// The foreign keys. public static IEnumerable FindForeignKeys( [NotNull] this IConventionEntityType entityType, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) => entityType.FindForeignKeys(new[] { property }); /// @@ -276,7 +306,7 @@ public static IEnumerable FindForeignKeys( /// The foreign keys. public static IEnumerable FindForeignKeys( [NotNull] this IConventionEntityType entityType, - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => ((EntityType)entityType).FindForeignKeys(properties); /// @@ -294,14 +324,10 @@ public static IEnumerable FindForeignKeys( /// The foreign key, or if none is defined. public static IConventionForeignKey? FindForeignKey( [NotNull] this IConventionEntityType entityType, - [NotNull] IProperty property, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindForeignKey(new[] { property }, principalKey, principalEntityType); - } + [NotNull] IReadOnlyProperty property, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) + => entityType.FindForeignKey(new[] { property }, principalKey, principalEntityType); /// /// Gets the foreign keys declared on the given using the given properties. @@ -311,7 +337,7 @@ public static IEnumerable FindForeignKeys( /// Declared foreign keys. public static IEnumerable FindDeclaredForeignKeys( [NotNull] this IConventionEntityType entityType, - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => ((EntityType)entityType).FindDeclaredForeignKeys(properties); /// @@ -338,7 +364,7 @@ public static IEnumerable GetDeclaredReferencingForeignKe /// The entity type. /// The relationship to the owner if this is an owned type or otherwise. public static IConventionForeignKey? FindOwnership([NotNull] this IConventionEntityType entityType) - => ((EntityType)entityType).FindOwnership(); + => (IConventionForeignKey?)((IReadOnlyEntityType)entityType).FindOwnership(); /// /// Adds a new relationship to this entity type. @@ -400,7 +426,7 @@ public static IEnumerable GetDeclaredReferencingForeignKe /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. public static IConventionNavigation? FindNavigation([NotNull] this IConventionEntityType entityType, [NotNull] string name) - => ((EntityType)entityType).FindNavigation(name); + => (IConventionNavigation?)((IReadOnlyEntityType)entityType).FindNavigation(name); /// /// Gets a navigation property on the given entity type. Does not return navigation properties defined on a base type. @@ -419,7 +445,7 @@ public static IEnumerable GetDeclaredReferencingForeignKe /// The defining navigation if one exists or otherwise. [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] public static IConventionNavigation? FindDefiningNavigation([NotNull] this IConventionEntityType entityType) - => (IConventionNavigation?)((IEntityType)entityType).FindDefiningNavigation(); + => (IConventionNavigation?)((IReadOnlyEntityType)entityType).FindDefiningNavigation(); /// /// Gets all navigation properties on the given entity type. @@ -427,7 +453,7 @@ public static IEnumerable GetDeclaredReferencingForeignKe /// The entity type. /// All navigation properties on the given entity type. public static IEnumerable GetNavigations([NotNull] this IConventionEntityType entityType) - => ((EntityType)entityType).GetNavigations(); + => ((IReadOnlyEntityType)entityType).GetNavigations().Cast(); /// /// @@ -442,14 +468,7 @@ public static IEnumerable GetNavigations([NotNull] this I /// The property on the entity class. /// The property, or if none is found. public static IConventionProperty? FindProperty([NotNull] this IConventionEntityType entityType, [NotNull] MemberInfo memberInfo) - { - Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(memberInfo, nameof(memberInfo)); - - return (memberInfo as PropertyInfo)?.IsIndexerProperty() == true - ? null - : entityType.FindProperty(memberInfo.GetSimpleMemberName()); - } + => (IConventionProperty?)((IReadOnlyEntityType)entityType).FindProperty(memberInfo); /// /// @@ -465,7 +484,24 @@ public static IEnumerable GetNavigations([NotNull] this I public static IReadOnlyList? FindProperties( [NotNull] this IConventionEntityType entityType, [NotNull] IReadOnlyList propertyNames) - => ((EntityType)entityType).FindProperties(Check.NotNull(propertyNames, nameof(propertyNames))); + => (IReadOnlyList?)((IReadOnlyEntityType)entityType).FindProperties(propertyNames); + + /// + /// + /// Gets a property with the given name. + /// + /// + /// This API only finds scalar properties and does not find navigation properties. Use + /// to find a navigation property. + /// + /// + /// The entity type. + /// The property name. + /// The property, or if none is found. + public static IConventionProperty GetProperty( + [NotNull] this IConventionEntityType entityType, + [NotNull] string name) + => (IConventionProperty)((IReadOnlyEntityType)entityType).GetProperty(name); /// /// Finds a property declared on the type with the given name. @@ -557,48 +593,6 @@ public static IEnumerable GetNavigations([NotNull] this I return entityType.AddProperty(name, propertyType, indexerPropertyInfo, setTypeConfigurationSource, fromDataAnnotation); } - /// - /// - /// Gets the unnamed index defined on the given property. Returns if no such index is defined. - /// - /// - /// Named indexes will not be returned even if the list of properties matches. - /// - /// - /// The entity type. - /// The property to find the index on. - /// The index, or null if none is found. - public static IConventionIndex? FindIndex([NotNull] this IConventionEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindIndex(new[] { property }); - } - - /// - /// Adds an index to this entity type. - /// - /// The entity type. - /// The property to be indexed. - /// Indicates whether the configuration was specified using a data annotation. - /// The newly created index. - public static IConventionIndex? AddIndex( - [NotNull] this IConventionEntityType entityType, - [NotNull] IConventionProperty property, - bool fromDataAnnotation = false) - => Check.NotNull(entityType, nameof(entityType)).AddIndex(new[] { property }, fromDataAnnotation); - - /// - /// Removes an index from this entity type. - /// - /// The entity type. - /// The properties that make up the index. - /// The index that was removed. - public static IConventionIndex? RemoveIndex( - [NotNull] this IConventionEntityType entityType, - [NotNull] IReadOnlyList properties) - => ((EntityType)entityType).RemoveIndex(properties); - /// /// Sets the change tracking strategy to use for this entity type. This strategy indicates how the /// context detects changes to properties for an instance of the entity type. @@ -678,10 +672,10 @@ public static void SetDefiningQuery( /// /// The entity type. public static IConventionProperty? GetDiscriminatorProperty([NotNull] this IConventionEntityType entityType) - => (IConventionProperty?)((IEntityType)entityType).GetDiscriminatorProperty(); + => (IConventionProperty?)((IReadOnlyEntityType)entityType).GetDiscriminatorProperty(); /// - /// Sets the that will be used for storing a discriminator value. + /// Sets the that will be used for storing a discriminator value. /// /// The entity type. /// The property to set. @@ -689,7 +683,7 @@ public static void SetDefiningQuery( /// The discriminator property. public static IConventionProperty? SetDiscriminatorProperty( [NotNull] this IConventionEntityType entityType, - [CanBeNull] IProperty? property, + [CanBeNull] IReadOnlyProperty? property, bool fromDataAnnotation = false) => Check.NotNull(entityType, nameof(entityType)).AsEntityType() .SetDiscriminatorProperty( diff --git a/src/EFCore/Extensions/ConventionForeignKeyExtensions.cs b/src/EFCore/Extensions/ConventionForeignKeyExtensions.cs index cbcf3c640a1..77f6a4fed00 100644 --- a/src/EFCore/Extensions/ConventionForeignKeyExtensions.cs +++ b/src/EFCore/Extensions/ConventionForeignKeyExtensions.cs @@ -23,7 +23,7 @@ public static class ConventionForeignKeyExtensions public static IConventionEntityType GetRelatedEntityType( [NotNull] this IConventionForeignKey foreignKey, [NotNull] IConventionEntityType entityType) - => (IConventionEntityType)((IForeignKey)foreignKey).GetRelatedEntityType(entityType); + => (IConventionEntityType)((IReadOnlyForeignKey)foreignKey).GetRelatedEntityType(entityType); /// /// Returns a navigation associated with this foreign key. diff --git a/src/EFCore/Extensions/ConventionKeyExtensions.cs b/src/EFCore/Extensions/ConventionKeyExtensions.cs index d887236d4cf..30451df0a5f 100644 --- a/src/EFCore/Extensions/ConventionKeyExtensions.cs +++ b/src/EFCore/Extensions/ConventionKeyExtensions.cs @@ -22,6 +22,6 @@ public static class ConventionKeyExtensions /// The key to find the foreign keys for. /// The foreign keys that reference the given key. public static IEnumerable GetReferencingForeignKeys([NotNull] this IConventionKey key) - => ((IKey)key).GetReferencingForeignKeys().Cast(); + => ((IReadOnlyKey)key).GetReferencingForeignKeys().Cast(); } } diff --git a/src/EFCore/Extensions/ConventionModelExtensions.cs b/src/EFCore/Extensions/ConventionModelExtensions.cs index b407774a7de..9a0196ed6f6 100644 --- a/src/EFCore/Extensions/ConventionModelExtensions.cs +++ b/src/EFCore/Extensions/ConventionModelExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -24,7 +25,7 @@ public static class ConventionModelExtensions /// /// The model to find the entity type in. /// The type to find the corresponding entity type for. - /// The entity type, or if none if found. + /// The entity type, or if none is found. public static IConventionEntityType? FindEntityType([NotNull] this IConventionModel model, [NotNull] Type type) => ((Model)model).FindEntityType(type); @@ -36,13 +37,13 @@ public static class ConventionModelExtensions /// The type of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. public static IConventionEntityType? FindEntityType( [NotNull] this IConventionModel model, [NotNull] Type type, [NotNull] string definingNavigationName, [NotNull] IConventionEntityType definingEntityType) - => (IConventionEntityType?)((IModel)model).FindEntityType(type, definingNavigationName, definingEntityType); + => (IConventionEntityType?)((IReadOnlyModel)model).FindEntityType(type, definingNavigationName, definingEntityType); /// /// Gets the entity types matching the given type. @@ -146,12 +147,12 @@ public static IReadOnlyCollection GetEntityTypes( /// The base type. /// An optional condition for filtering entity types. /// List of entity types corresponding to the least derived types from the given. - public static IReadOnlyList FindLeastDerivedEntityTypes( + public static IEnumerable FindLeastDerivedEntityTypes( [NotNull] this IConventionModel model, [NotNull] Type type, [CanBeNull] Func? condition = null) - => Check.NotNull((Model)model, nameof(model)) - .FindLeastDerivedEntityTypes(type, condition); + => ((IReadOnlyModel)model).FindLeastDerivedEntityTypes(type, condition == null ? null : t => condition((IConventionEntityType)t)) + .Cast(); /// /// @@ -324,7 +325,7 @@ public static void AddShared([NotNull] this IConventionModel model, [NotNull] Ty /// explicitly in cases where the automatic execution is not possible. /// /// The model to finalize. - /// The finalized . + /// The finalized model. public static IModel FinalizeModel([NotNull] this IConventionModel model) => ((Model)model).FinalizeModel(); } diff --git a/src/EFCore/Extensions/ConventionPropertyBaseExtensions.cs b/src/EFCore/Extensions/ConventionPropertyBaseExtensions.cs index 6f17a629af2..1427994511c 100644 --- a/src/EFCore/Extensions/ConventionPropertyBaseExtensions.cs +++ b/src/EFCore/Extensions/ConventionPropertyBaseExtensions.cs @@ -32,10 +32,10 @@ public static class ConventionPropertyBaseExtensions propertyAccessMode, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// /// The property to find configuration source for. - /// The configuration source for . + /// The configuration source for . public static ConfigurationSource? GetPropertyAccessModeConfigurationSource([NotNull] this IConventionPropertyBase property) => property.FindAnnotation(CoreAnnotationNames.PropertyAccessMode)?.GetConfigurationSource(); } diff --git a/src/EFCore/Extensions/ConventionPropertyExtensions.cs b/src/EFCore/Extensions/ConventionPropertyExtensions.cs index 1e0d4c47892..de7b89267a0 100644 --- a/src/EFCore/Extensions/ConventionPropertyExtensions.cs +++ b/src/EFCore/Extensions/ConventionPropertyExtensions.cs @@ -29,7 +29,7 @@ public static class ConventionPropertyExtensions /// The foreign key property. /// The first associated principal property, or if none exists. public static IConventionProperty? FindFirstPrincipal([NotNull] this IConventionProperty property) - => (IConventionProperty?)((IProperty)property).FindFirstPrincipal(); + => (IConventionProperty?)((IReadOnlyProperty)property).FindFirstPrincipal(); /// /// Finds the list of principal properties including the given property that the given property is constrained by @@ -38,7 +38,7 @@ public static class ConventionPropertyExtensions /// The foreign key property. /// The list of all associated principal properties including the given property. public static IReadOnlyList FindPrincipals([NotNull] this IConventionProperty property) - => ((IProperty)property).FindPrincipals().Cast().ToList(); + => ((IReadOnlyProperty)property).FindPrincipals().Cast().ToList(); /// /// Gets all foreign keys that use this property (including composite foreign keys in which this property @@ -49,7 +49,7 @@ public static IReadOnlyList FindPrincipals([NotNull] this I /// The foreign keys that use this property. /// public static IEnumerable GetContainingForeignKeys([NotNull] this IConventionProperty property) - => ((IProperty)property).GetContainingForeignKeys().Cast(); + => ((IReadOnlyProperty)property).GetContainingForeignKeys().Cast(); /// /// Gets all indexes that use this property (including composite indexes in which this property @@ -71,7 +71,7 @@ public static IEnumerable GetContainingIndexes([NotNull] this /// The primary that use this property, or if it is not part of the primary key. /// public static IConventionKey? FindContainingPrimaryKey([NotNull] this IConventionProperty property) - => (IConventionKey?)((IProperty)property).FindContainingPrimaryKey(); + => (IConventionKey?)((IReadOnlyProperty)property).FindContainingPrimaryKey(); /// /// Gets all primary or alternate keys that use this property (including composite keys in which this property @@ -98,10 +98,10 @@ public static IEnumerable GetContainingKeys([NotNull] this IConv typeMapping, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - /// Gets the for . + /// Gets the for of the property. /// /// The property. - /// The for . + /// The for of the property. public static ConfigurationSource? GetTypeMappingConfigurationSource([NotNull] this IConventionProperty property) => ((Property)property).GetTypeMappingConfigurationSource(); diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs index d63174ae5a0..1b67e8db378 100644 --- a/src/EFCore/Extensions/EntityTypeExtensions.cs +++ b/src/EFCore/Extensions/EntityTypeExtensions.cs @@ -22,17 +22,17 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Entity type extension methods for . /// public static class EntityTypeExtensions { /// - /// Returns all the derived types of the given , including the type itself, + /// Returns all the derived types of the given , including the type itself, /// which are not . /// /// The entity type. /// Non-abstract, derived types. - public static IEnumerable GetConcreteDerivedTypesInclusive([NotNull] this IEntityType entityType) + public static IEnumerable GetConcreteDerivedTypesInclusive([NotNull] this IReadOnlyEntityType entityType) => entityType.GetDerivedTypesInclusive().Where(et => !et.IsAbstract()); /// @@ -41,7 +41,7 @@ public static IEnumerable GetConcreteDerivedTypesInclusive([NotNull /// The entity type. /// if the type is abstract, otherwise. [DebuggerStepThrough] - public static bool IsAbstract([NotNull] this ITypeBase type) + public static bool IsAbstract([NotNull] this IReadOnlyTypeBase type) => type.ClrType.IsAbstract; /// @@ -51,7 +51,7 @@ public static bool IsAbstract([NotNull] this ITypeBase type) /// /// The root base type. If the given entity type is not a derived type, then the same entity type is returned. /// - public static IEntityType GetRootType([NotNull] this IEntityType entityType) + public static IReadOnlyEntityType GetRootType([NotNull] this IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); @@ -65,7 +65,7 @@ public static IEntityType GetRootType([NotNull] this IEntityType entityType) /// /// The base types. /// - public static IEnumerable GetAllBaseTypes([NotNull] this IEntityType entityType) + public static IEnumerable GetAllBaseTypes([NotNull] this IReadOnlyEntityType entityType) => entityType.GetAllBaseTypesAscending().Reverse(); /// @@ -75,7 +75,7 @@ public static IEnumerable GetAllBaseTypes([NotNull] this IEntityTyp /// /// The base types. /// - public static IEnumerable GetAllBaseTypesAscending([NotNull] this IEntityType entityType) + public static IEnumerable GetAllBaseTypesAscending([NotNull] this IReadOnlyEntityType entityType) => entityType.GetAllBaseTypesInclusiveAscending().Skip(1); /// @@ -83,15 +83,15 @@ public static IEnumerable GetAllBaseTypesAscending([NotNull] this I /// /// The entity type. /// The derived types. - public static IEnumerable GetDerivedTypes([NotNull] this IEntityType entityType) + public static IEnumerable GetDerivedTypes([NotNull] this IReadOnlyEntityType entityType) => Check.NotNull(entityType, nameof(entityType)).AsEntityType().GetDerivedTypes(); /// - /// Returns all derived types of the given , including the type itself. + /// Returns all derived types of the given , including the type itself. /// /// The entity type. /// Derived types. - public static IEnumerable GetDerivedTypesInclusive([NotNull] this IEntityType entityType) + public static IEnumerable GetDerivedTypesInclusive([NotNull] this IReadOnlyEntityType entityType) => new[] { entityType }.Concat(entityType.GetDerivedTypes()); /// @@ -99,7 +99,7 @@ public static IEnumerable GetDerivedTypesInclusive([NotNull] this I /// /// The entity type. /// The derived types. - public static IEnumerable GetDirectlyDerivedTypes([NotNull] this IEntityType entityType) + public static IEnumerable GetDirectlyDerivedTypes([NotNull] this IReadOnlyEntityType entityType) => ((EntityType)entityType).GetDirectlyDerivedTypes(); /// @@ -111,7 +111,7 @@ public static IEnumerable GetDirectlyDerivedTypes([NotNull] this IE /// if derives from (or is the same as) , /// otherwise . /// - public static bool IsAssignableFrom([NotNull] this IEntityType entityType, [NotNull] IEntityType derivedType) + public static bool IsAssignableFrom([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyEntityType derivedType) { Check.NotNull(entityType, nameof(entityType)); Check.NotNull(derivedType, nameof(derivedType)); @@ -140,7 +140,7 @@ public static bool IsAssignableFrom([NotNull] this IEntityType entityType, [NotN /// The closest common parent of and , /// or null if they have not common parent. /// - public static IEntityType? GetClosestCommonParent([NotNull] this IEntityType entityType1, [NotNull] IEntityType entityType2) + public static IReadOnlyEntityType? GetClosestCommonParent([NotNull] this IReadOnlyEntityType entityType1, [NotNull] IReadOnlyEntityType entityType2) { Check.NotNull(entityType1, nameof(entityType1)); Check.NotNull(entityType2, nameof(entityType2)); @@ -159,7 +159,7 @@ public static bool IsAssignableFrom([NotNull] this IEntityType entityType, [NotN /// if derives from (but is not the same as) , /// otherwise . /// - public static bool IsStrictlyDerivedFrom([NotNull] this IEntityType entityType, [NotNull] IEntityType baseType) + public static bool IsStrictlyDerivedFrom([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyEntityType baseType) { Check.NotNull(entityType, nameof(entityType)); Check.NotNull(baseType, nameof(baseType)); @@ -176,7 +176,7 @@ public static bool IsStrictlyDerivedFrom([NotNull] this IEntityType entityType, /// The least derived type between the specified two. /// If the given entity types are not related, then is returned. /// - public static IEntityType? LeastDerivedType([NotNull] this IEntityType entityType, [NotNull] IEntityType otherEntityType) + public static IReadOnlyEntityType? LeastDerivedType([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyEntityType otherEntityType) { Check.NotNull(entityType, nameof(entityType)); Check.NotNull(otherEntityType, nameof(otherEntityType)); @@ -189,23 +189,23 @@ public static bool IsStrictlyDerivedFrom([NotNull] this IEntityType entityType, } /// - /// Returns all base types of the given , including the type itself, top to bottom. + /// Returns all base types of the given , including the type itself, top to bottom. /// /// The entity type. /// Base types. - public static IEnumerable GetAllBaseTypesInclusive([NotNull] this IEntityType entityType) + public static IEnumerable GetAllBaseTypesInclusive([NotNull] this IReadOnlyEntityType entityType) => GetAllBaseTypesInclusiveAscending(entityType).Reverse(); /// - /// Returns all base types of the given , including the type itself, bottom to top. + /// Returns all base types of the given , including the type itself, bottom to top. /// /// The entity type. /// Base types. - public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IEntityType entityType) + public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); - var tmp = (IEntityType?)entityType; + var tmp = (IReadOnlyEntityType?)entityType; while (tmp != null) { yield return tmp; @@ -215,52 +215,52 @@ public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNul /// /// - /// Gets all keys declared on the given . + /// Gets all keys declared on the given . /// /// /// This method does not return keys declared on base types. /// It is useful when iterating over all entity types to avoid processing the same key more than once. - /// Use to also return keys declared on base types. + /// Use to also return keys declared on base types. /// /// /// The entity type. /// Declared keys. - public static IEnumerable GetDeclaredKeys([NotNull] this IEntityType entityType) + public static IEnumerable GetDeclaredKeys([NotNull] this IReadOnlyEntityType entityType) => entityType.AsEntityType().GetDeclaredKeys(); /// /// - /// Gets all foreign keys declared on the given . + /// Gets all foreign keys declared on the given . /// /// /// This method does not return foreign keys declared on base types. /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once. - /// Use to also return foreign keys declared on base types. + /// Use to also return foreign keys declared on base types. /// /// /// The entity type. /// Declared foreign keys. - public static IEnumerable GetDeclaredForeignKeys([NotNull] this IEntityType entityType) + public static IEnumerable GetDeclaredForeignKeys([NotNull] this IReadOnlyEntityType entityType) => entityType.AsEntityType().GetDeclaredForeignKeys(); /// /// - /// Gets all foreign keys declared on the types derived from the given . + /// Gets all foreign keys declared on the types derived from the given . /// /// /// This method does not return foreign keys declared on the given entity type itself. - /// Use to return foreign keys declared on this + /// Use to return foreign keys declared on this /// and base entity typed types. /// /// /// The entity type. /// Derived foreign keys. - public static IEnumerable GetDerivedForeignKeys([NotNull] this IEntityType entityType) + public static IEnumerable GetDerivedForeignKeys([NotNull] this IReadOnlyEntityType entityType) => entityType.AsEntityType().GetDerivedForeignKeys(); /// /// - /// Gets all navigation properties declared on the given . + /// Gets all navigation properties declared on the given . /// /// /// This method does not return navigation properties declared on base types. @@ -270,65 +270,85 @@ public static IEnumerable GetDerivedForeignKeys([NotNull] this IEnt /// /// The entity type. /// Declared navigation properties. - public static IEnumerable GetDeclaredNavigations([NotNull] this IEntityType entityType) - => entityType.GetDeclaredForeignKeys() - .Concat(entityType.GetDeclaredReferencingForeignKeys()) - .SelectMany(foreignKey => foreignKey.FindNavigationsFrom(entityType)) - .Distinct() - .OrderBy(m => m.Name); + public static IEnumerable GetDeclaredNavigations([NotNull] this IReadOnlyEntityType entityType) + => ((EntityType)entityType).GetDeclaredNavigations(); /// /// - /// Gets all non-navigation properties declared on the given . + /// Gets all non-navigation properties declared on the given . /// /// /// This method does not return properties declared on base types. /// It is useful when iterating over all entity types to avoid processing the same property more than once. - /// Use to also return properties declared on base types. + /// Use to also return properties declared on base types. /// /// /// The entity type. /// Declared non-navigation properties. - public static IEnumerable GetDeclaredProperties([NotNull] this IEntityType entityType) + public static IEnumerable GetDeclaredProperties([NotNull] this IReadOnlyEntityType entityType) => entityType.AsEntityType().GetDeclaredProperties(); /// /// - /// Gets all service properties declared on the given . + /// Gets all service properties declared on the given . /// /// /// This method does not return properties declared on base types. /// It is useful when iterating over all entity types to avoid processing the same property more than once. - /// Use to also return properties declared on base types. + /// Use to also return properties declared on base types. /// /// /// The entity type. /// Declared service properties. - public static IEnumerable GetDeclaredServiceProperties([NotNull] this IEntityType entityType) + public static IEnumerable GetDeclaredServiceProperties([NotNull] this IReadOnlyEntityType entityType) => entityType.AsEntityType().GetDeclaredServiceProperties(); /// /// - /// Gets all indexes declared on the given . + /// Gets all indexes declared on the given . /// /// /// This method does not return indexes declared on base types. /// It is useful when iterating over all entity types to avoid processing the same index more than once. - /// Use to also return indexes declared on base types. + /// Use to also return indexes declared on base types. /// /// /// The entity type. /// Declared indexes. - public static IEnumerable GetDeclaredIndexes([NotNull] this IEntityType entityType) - => entityType.AsEntityType().GetDeclaredIndexes(); + public static IEnumerable GetDeclaredIndexes([NotNull] this IReadOnlyEntityType entityType) + => ((EntityType)entityType).GetDeclaredIndexes(); /// - /// Gets the friendly display name for the given . + /// + /// Gets all indexes declared on the types derived from the given . + /// + /// + /// The entity type. + /// Derived indexes. + public static IEnumerable GetDerivedIndexes([NotNull] this IReadOnlyEntityType entityType) + => ((EntityType)entityType).GetDerivedIndexes(); + + /// + /// + /// Gets the unnamed index defined on the given property. Returns if no such index is defined. + /// + /// + /// Named indexes will not be returned even if the list of properties matches. + /// + /// + /// The entity type. + /// The property to find the index on. + /// The index, or null if none is found. + public static IReadOnlyIndex? FindIndex([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindIndex(new[] { property }); + + /// + /// Gets the friendly display name for the given . /// /// The entity type. /// The display name. [DebuggerStepThrough] - public static string DisplayName([NotNull] this ITypeBase type) + public static string DisplayName([NotNull] this IReadOnlyTypeBase type) { if (!type.HasSharedClrType) { @@ -367,20 +387,20 @@ public static string DisplayName([NotNull] this ITypeBase type) } /// - /// Gets the unique name for the given . + /// Gets the unique name for the given . /// /// The entity type. /// The full name. [DebuggerStepThrough] - public static string FullName([NotNull] this ITypeBase type) => type.Name; + public static string FullName([NotNull] this IReadOnlyTypeBase type) => type.Name; /// - /// Gets a short name for the given that can be used in other identifiers. + /// Gets a short name for the given that can be used in other identifiers. /// /// The entity type. /// The short name. [DebuggerStepThrough] - public static string ShortName([NotNull] this ITypeBase type) + public static string ShortName([NotNull] this IReadOnlyTypeBase type) { if (!type.HasSharedClrType) { @@ -412,7 +432,7 @@ public static string ShortName([NotNull] this ITypeBase type) /// if this entity type has a defining navigation. [DebuggerStepThrough] [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] - public static bool HasDefiningNavigation([NotNull] this IEntityType entityType) + public static bool HasDefiningNavigation([NotNull] this IReadOnlyEntityType entityType) => entityType.HasDefiningNavigation(); /// @@ -421,7 +441,7 @@ public static bool HasDefiningNavigation([NotNull] this IEntityType entityType) /// The entity type. /// if this entity type is owned by another entity type. [DebuggerStepThrough] - public static bool IsOwned([NotNull] this IEntityType entityType) + public static bool IsOwned([NotNull] this IReadOnlyEntityType entityType) => entityType.GetForeignKeys().Any(fk => fk.IsOwnership); /// @@ -433,7 +453,7 @@ public static bool IsOwned([NotNull] this IEntityType entityType) /// if is in ownership path of , /// otherwise . /// - public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] IEntityType targetType) + public static bool IsInOwnershipPath([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyEntityType targetType) { var owner = entityType; while (true) @@ -459,12 +479,8 @@ public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [Not /// The entity type. /// The property that the key is defined on. /// The key, or null if none is defined. - public static IKey? FindKey([NotNull] this IEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindKey(new[] { property }); - } + public static IReadOnlyKey? FindKey([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindKey(new[] { property }); /// /// Gets the foreign keys defined on the given property. Only foreign keys that are defined on exactly the specified @@ -473,8 +489,8 @@ public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [Not /// The entity type. /// The property to find the foreign keys on. /// The foreign keys. - public static IEnumerable FindForeignKeys([NotNull] this IEntityType entityType, [NotNull] IProperty property) - => property.GetContainingForeignKeys(); + public static IEnumerable FindForeignKeys([NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindForeignKeys(new[] { property }); /// /// Gets the foreign keys defined on the given properties. Only foreign keys that are defined on exactly the specified @@ -483,17 +499,10 @@ public static IEnumerable FindForeignKeys([NotNull] this IEntityTyp /// The entity type. /// The properties to find the foreign keys on. /// The foreign keys. - public static IEnumerable FindForeignKeys( - [NotNull] this IEntityType entityType, - [NotNull] IReadOnlyList properties) - { - Check.NotNull(entityType, nameof(entityType)); - Check.NotEmpty(properties, nameof(properties)); - Check.HasNoNulls(properties, nameof(properties)); - - return entityType.GetForeignKeys() - .Where(foreignKey => PropertyListComparer.Instance.Equals(foreignKey.Properties, properties)); - } + public static IEnumerable FindForeignKeys( + [NotNull] this IReadOnlyEntityType entityType, + [NotNull] IReadOnlyList properties) + => ((EntityType)entityType).FindForeignKeys(properties); /// /// Gets the foreign key for the given properties that points to a given primary or alternate key. Returns @@ -508,11 +517,11 @@ public static IEnumerable FindForeignKeys( /// base type of the hierarchy). /// /// The foreign key, or if none is defined. - public static IForeignKey? FindForeignKey( - [NotNull] this IEntityType entityType, - [NotNull] IProperty property, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + public static IReadOnlyForeignKey? FindForeignKey( + [NotNull] this IReadOnlyEntityType entityType, + [NotNull] IReadOnlyProperty property, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) => Check.NotNull(entityType, nameof(entityType)) .FindForeignKey( new[] { property }, principalKey, principalEntityType); @@ -523,7 +532,7 @@ public static IEnumerable FindForeignKeys( /// /// The entity type. /// The foreign keys that reference the given entity type. - public static IEnumerable GetReferencingForeignKeys([NotNull] this IEntityType entityType) + public static IEnumerable GetReferencingForeignKeys([NotNull] this IReadOnlyEntityType entityType) => Check.NotNull(entityType, nameof(entityType)).AsEntityType().GetReferencingForeignKeys(); /// @@ -532,7 +541,7 @@ public static IEnumerable GetReferencingForeignKeys([NotNull] this /// /// The entity type. /// The foreign keys that reference the given entity type. - public static IEnumerable GetDeclaredReferencingForeignKeys([NotNull] this IEntityType entityType) + public static IEnumerable GetDeclaredReferencingForeignKeys([NotNull] this IReadOnlyEntityType entityType) => Check.NotNull(entityType, nameof(entityType)).AsEntityType().GetDeclaredReferencingForeignKeys(); /// @@ -540,8 +549,18 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// /// The entity type. /// The relationship to the owner if this is an owned type or otherwise. - public static IForeignKey? FindOwnership([NotNull] this IEntityType entityType) - => ((EntityType)entityType).FindOwnership(); + public static IReadOnlyForeignKey? FindOwnership([NotNull] this IReadOnlyEntityType entityType) + { + foreach (var foreignKey in entityType.GetForeignKeys()) + { + if (foreignKey.IsOwnership) + { + return foreignKey; + } + } + + return null; + } /// /// Gets a navigation property on the given entity type. Returns if no navigation property is found. @@ -549,13 +568,8 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// The entity type. /// The navigation property on the entity class. /// The navigation property, or if none is found. - public static INavigation? FindNavigation([NotNull] this IEntityType entityType, [NotNull] MemberInfo memberInfo) - { - Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(memberInfo, nameof(memberInfo)); - - return entityType.FindNavigation(memberInfo.GetSimpleMemberName()); - } + public static IReadOnlyNavigation? FindNavigation([NotNull] this IReadOnlyEntityType entityType, [NotNull] MemberInfo memberInfo) + => entityType.FindNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); /// /// Gets a navigation property on the given entity type. Returns if no navigation property is found. @@ -563,8 +577,8 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// The entity type. /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. - public static INavigation? FindNavigation([NotNull] this IEntityType entityType, [NotNull] string name) - => Check.NotNull(entityType, nameof(entityType)).AsEntityType().FindNavigation(Check.NotNull(name, nameof(name))); + public static IReadOnlyNavigation? FindNavigation([NotNull] this IReadOnlyEntityType entityType, [NotNull] string name) + => entityType.FindDeclaredNavigation(Check.NotEmpty(name, nameof(name))) ?? entityType.BaseType?.FindNavigation(name); /// /// Gets a navigation property on the given entity type. Does not return navigation properties defined on a base type. @@ -573,8 +587,8 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// The entity type. /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. - public static INavigation? FindDeclaredNavigation([NotNull] this IEntityType entityType, [NotNull] string name) - => Check.NotNull(entityType, nameof(entityType)).AsEntityType().FindDeclaredNavigation(Check.NotNull(name, nameof(name))); + public static IReadOnlyNavigation? FindDeclaredNavigation([NotNull] this IReadOnlyEntityType entityType, [NotNull] string name) + => ((EntityType)entityType).FindDeclaredNavigation(Check.NotNull(name, nameof(name))); /// /// Returns the defining navigation if one exists or otherwise. @@ -582,7 +596,7 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// The entity type. /// The defining navigation if one exists or otherwise. [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] - public static INavigation? FindDefiningNavigation([NotNull] this IEntityType entityType) + public static IReadOnlyNavigation? FindDefiningNavigation([NotNull] this IReadOnlyEntityType entityType) { if (!entityType.HasDefiningNavigation()) { @@ -598,7 +612,7 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul /// /// The entity type. /// All navigation properties on the given entity type. - public static IEnumerable GetNavigations([NotNull] this IEntityType entityType) + public static IEnumerable GetNavigations([NotNull] this IReadOnlyEntityType entityType) => Check.NotNull(entityType, nameof(entityType)).AsEntityType().GetNavigations(); /// @@ -607,21 +621,16 @@ public static IEnumerable GetNavigations([NotNull] this IEntityType /// /// /// This API only finds scalar properties and does not find navigation properties. Use - /// to find a navigation property. + /// to find a navigation property. /// /// /// The entity type. /// The member on the entity class. /// The property, or if none is found. - public static IProperty? FindProperty([NotNull] this IEntityType entityType, [NotNull] MemberInfo memberInfo) - { - Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(memberInfo, nameof(memberInfo)); - - return (memberInfo as PropertyInfo)?.IsIndexerProperty() == true + public static IReadOnlyProperty? FindProperty([NotNull] this IReadOnlyEntityType entityType, [NotNull] MemberInfo memberInfo) + => (Check.NotNull(memberInfo, nameof(memberInfo)) as PropertyInfo)?.IsIndexerProperty() == true ? null : entityType.FindProperty(memberInfo.GetSimpleMemberName()); - } /// /// @@ -629,13 +638,13 @@ public static IEnumerable GetNavigations([NotNull] this IEntityType /// /// /// This API only finds scalar properties and does not find navigation properties. Use - /// to find a navigation property. + /// to find a navigation property. /// /// /// The entity type. /// The property name. /// The property, or if none is found. - public static IProperty GetProperty([NotNull] this IEntityType entityType, [NotNull] string name) + public static IReadOnlyProperty GetProperty([NotNull] this IReadOnlyEntityType entityType, [NotNull] string name) { Check.NotEmpty(name, nameof(name)); @@ -668,10 +677,10 @@ public static IProperty GetProperty([NotNull] this IEntityType entityType, [NotN /// The entity type. /// The property names. /// The properties, or if any property is not found. - public static IReadOnlyList? FindProperties( - [NotNull] this IEntityType entityType, + public static IReadOnlyList? FindProperties( + [NotNull] this IReadOnlyEntityType entityType, [NotNull] IReadOnlyList propertyNames) - => entityType.AsEntityType().FindProperties(Check.NotNull(propertyNames, nameof(propertyNames))); + => ((EntityType)entityType).FindProperties(propertyNames); /// /// Finds a property declared on the type with the given name. @@ -680,27 +689,9 @@ public static IProperty GetProperty([NotNull] this IEntityType entityType, [NotN /// The entity type. /// The property name. /// The property, or if none is found. - public static IProperty? FindDeclaredProperty([NotNull] this IEntityType entityType, [NotNull] string name) + public static IReadOnlyProperty? FindDeclaredProperty([NotNull] this IReadOnlyEntityType entityType, [NotNull] string name) => entityType.AsEntityType().FindDeclaredProperty(name); - /// - /// - /// Gets the unnamed index defined on the given property. Returns if no such index is defined. - /// - /// - /// Named indexes will not be returned even if the list of properties matches. - /// - /// - /// The entity type. - /// The property to find the index on. - /// The index, or null if none is found. - public static IIndex? FindIndex([NotNull] this IEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindIndex(new[] { property }); - } - /// /// Gets the change tracking strategy being used for this entity type. This strategy indicates how the /// context detects changes to properties for an instance of the entity type. @@ -708,7 +699,7 @@ public static IProperty GetProperty([NotNull] this IEntityType entityType, [NotN /// The entity type. /// The change tracking strategy. [DebuggerStepThrough] - public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IEntityType entityType) + public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IReadOnlyEntityType entityType) => ((EntityType)entityType).GetChangeTrackingStrategy(); /// @@ -718,7 +709,7 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IE /// If true, then provider values are used. /// The data. public static IEnumerable> GetSeedData( - [NotNull] this IEntityType entityType, + [NotNull] this IReadOnlyEntityType entityType, bool providerValues = false) => entityType.AsEntityType().GetSeedData(providerValues); @@ -727,7 +718,7 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IE /// /// The entity type to get the query filter for. /// The LINQ expression filter. - public static LambdaExpression? GetQueryFilter([NotNull] this IEntityType entityType) + public static LambdaExpression? GetQueryFilter([NotNull] this IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); @@ -740,7 +731,7 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IE /// The entity type to get the defining query for. /// The LINQ query used as the default source. [Obsolete("Use InMemoryEntityTypeExtensions.GetInMemoryQuery")] - public static LambdaExpression? GetDefiningQuery([NotNull] this IEntityType entityType) + public static LambdaExpression? GetDefiningQuery([NotNull] this IReadOnlyEntityType entityType) { Check.NotNull(entityType, nameof(entityType)); @@ -748,10 +739,10 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IE } /// - /// Returns the that will be used for storing a discriminator value. + /// Returns the that will be used for storing a discriminator value. /// /// The entity type. - public static IProperty? GetDiscriminatorProperty([NotNull] this IEntityType entityType) + public static IReadOnlyProperty? GetDiscriminatorProperty([NotNull] this IReadOnlyEntityType entityType) { if (entityType.BaseType != null) { @@ -767,11 +758,11 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IE /// Returns the value indicating whether the discriminator mapping is complete for this entity type. /// /// The entity type. - public static bool GetIsDiscriminatorMappingComplete([NotNull] this IEntityType entityType) + public static bool GetIsDiscriminatorMappingComplete([NotNull] this IReadOnlyEntityType entityType) => (bool?)entityType[CoreAnnotationNames.DiscriminatorMappingComplete] ?? GetDefaultIsDiscriminatorMappingComplete(entityType); - private static bool GetDefaultIsDiscriminatorMappingComplete(IEntityType entityType) + private static bool GetDefaultIsDiscriminatorMappingComplete(IReadOnlyEntityType entityType) => true; /// @@ -779,7 +770,7 @@ private static bool GetDefaultIsDiscriminatorMappingComplete(IEntityType entityT /// /// The entity type. /// The discriminator value for this entity type. - public static object? GetDiscriminatorValue([NotNull] this IEntityType entityType) + public static object? GetDiscriminatorValue([NotNull] this IReadOnlyEntityType entityType) => entityType[CoreAnnotationNames.DiscriminatorValue]; /// @@ -796,7 +787,7 @@ private static bool GetDefaultIsDiscriminatorMappingComplete(IEntityType entityT /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IEntityType entityType, + [NotNull] this IReadOnlyEntityType entityType, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/ForeignKeyExtensions.cs b/src/EFCore/Extensions/ForeignKeyExtensions.cs index 24191b2d294..2b3d91cb798 100644 --- a/src/EFCore/Extensions/ForeignKeyExtensions.cs +++ b/src/EFCore/Extensions/ForeignKeyExtensions.cs @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class ForeignKeyExtensions { @@ -31,11 +31,11 @@ public static class ForeignKeyExtensions /// not used in application code. /// /// - /// The for which a factory is needed. + /// The for which a factory is needed. /// The type of key instanceas. /// A new factory. public static IDependentKeyValueFactory? GetDependentKeyValueFactory( - [NotNull] this IForeignKey foreignKey) + [NotNull] this IReadOnlyForeignKey foreignKey) => (IDependentKeyValueFactory?)foreignKey.AsForeignKey().DependentKeyValueFactory; /// @@ -44,7 +44,8 @@ public static class ForeignKeyExtensions /// The foreign key. /// One of the entity types related by the foreign key. /// The entity type related to the given one. - public static IEntityType GetRelatedEntityType([NotNull] this IForeignKey foreignKey, [NotNull] IEntityType entityType) + public static IReadOnlyEntityType GetRelatedEntityType( + [NotNull] this IReadOnlyForeignKey foreignKey, [NotNull] IReadOnlyEntityType entityType) { if (foreignKey.DeclaringEntityType != entityType && foreignKey.PrincipalEntityType != entityType) @@ -71,7 +72,7 @@ public static IEntityType GetRelatedEntityType([NotNull] this IForeignKey foreig /// /// A navigation associated with this foreign key or . /// - public static INavigation? GetNavigation([NotNull] this IForeignKey foreignKey, bool pointsToPrincipal) + public static IReadOnlyNavigation? GetNavigation([NotNull] this IReadOnlyForeignKey foreignKey, bool pointsToPrincipal) => pointsToPrincipal ? foreignKey.DependentToPrincipal : foreignKey.PrincipalToDependent; /// @@ -79,7 +80,7 @@ public static IEntityType GetRelatedEntityType([NotNull] this IForeignKey foreig /// /// The foreign key. /// A value indicating whether the foreign key is defined on the primary key and pointing to the same primary key. - public static bool IsBaseLinking([NotNull] this IForeignKey foreignKey) + public static bool IsBaseLinking([NotNull] this IReadOnlyForeignKey foreignKey) { var primaryKey = foreignKey.DeclaringEntityType.FindPrimaryKey(); return primaryKey == foreignKey.PrincipalKey @@ -100,7 +101,7 @@ public static bool IsBaseLinking([NotNull] this IForeignKey foreignKey) /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IForeignKey foreignKey, + [NotNull] this IReadOnlyForeignKey foreignKey, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/IndexExtensions.cs b/src/EFCore/Extensions/IndexExtensions.cs index 4d121c3009e..07cb4af6da6 100644 --- a/src/EFCore/Extensions/IndexExtensions.cs +++ b/src/EFCore/Extensions/IndexExtensions.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class IndexExtensions { @@ -31,7 +31,7 @@ public static class IndexExtensions /// The index metadata. /// The type of the index instance. /// The factory. - public static IDependentKeyValueFactory GetNullableValueFactory([NotNull] this IIndex index) + public static IDependentKeyValueFactory GetNullableValueFactory([NotNull] this IReadOnlyIndex index) => index.AsIndex().GetNullableValueFactory(); /// @@ -48,7 +48,7 @@ public static IDependentKeyValueFactory GetNullableValueFactory([Not /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IIndex index, + [NotNull] this IReadOnlyIndex index, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/Internal/ExpressionExtensions.cs b/src/EFCore/Extensions/Internal/ExpressionExtensions.cs index 182aa34dc12..69eb251ad8a 100644 --- a/src/EFCore/Extensions/Internal/ExpressionExtensions.cs +++ b/src/EFCore/Extensions/Internal/ExpressionExtensions.cs @@ -34,7 +34,7 @@ public static class ExpressionExtensions /// public static Expression MakeHasDefaultValue( [NotNull] this Expression currentValueExpression, - [CanBeNull] IPropertyBase? propertyBase) + [CanBeNull] IReadOnlyPropertyBase? propertyBase) { if (!currentValueExpression.Type.IsValueType) { @@ -52,7 +52,7 @@ public static Expression MakeHasDefaultValue( currentValueExpression.Type.GetRequiredMethod("get_HasValue"))); } - var property = propertyBase as IProperty; + var property = propertyBase as IReadOnlyProperty; var clrType = propertyBase?.ClrType ?? currentValueExpression.Type; var comparer = property?.GetValueComparer() ?? ValueComparer.CreateDefault(clrType, favorStructuralComparisons: false); @@ -219,7 +219,7 @@ private static readonly MethodInfo _objectEqualsMethodInfo /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static Expression BuildPredicate( - [NotNull] IReadOnlyList keyProperties, + [NotNull] IReadOnlyList keyProperties, ValueBuffer keyValues, [NotNull] ParameterExpression entityParameter) { @@ -237,7 +237,7 @@ public static Expression BuildPredicate( static Expression GenerateEqualExpression( Expression entityParameterExpression, Expression keyValuesConstantExpression, - IProperty property, + IReadOnlyProperty property, int i) => property.ClrType.IsValueType && property.ClrType.UnwrapNullableType() is Type nonNullableType diff --git a/src/EFCore/Extensions/KeyExtensions.cs b/src/EFCore/Extensions/KeyExtensions.cs index 42176e40ae8..48d1a58c44a 100644 --- a/src/EFCore/Extensions/KeyExtensions.cs +++ b/src/EFCore/Extensions/KeyExtensions.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class KeyExtensions { @@ -34,7 +34,7 @@ public static class KeyExtensions /// The key metadata. /// The type of the key instance. /// The factory. - public static IPrincipalKeyValueFactory GetPrincipalKeyValueFactory([NotNull] this IKey key) + public static IPrincipalKeyValueFactory GetPrincipalKeyValueFactory([NotNull] this IReadOnlyKey key) => key.AsKey().GetPrincipalKeyValueFactory(); /// @@ -42,7 +42,7 @@ public static IPrincipalKeyValueFactory GetPrincipalKeyValueFactory( /// /// Key metadata. /// The key type. - public static Type GetKeyType([NotNull] this IKey key) + public static Type GetKeyType([NotNull] this IReadOnlyKey key) => key.Properties.Count > 1 ? typeof(object[]) : key.Properties.First().ClrType; /// @@ -50,15 +50,15 @@ public static Type GetKeyType([NotNull] this IKey key) /// /// The key to find the foreign keys for. /// The foreign keys that reference the given key. - public static IEnumerable GetReferencingForeignKeys([NotNull] this IKey key) - => Check.NotNull(key, nameof(key)).AsKey().ReferencingForeignKeys ?? Enumerable.Empty(); + public static IEnumerable GetReferencingForeignKeys([NotNull] this IReadOnlyKey key) + => Check.NotNull(key, nameof(key)).AsKey().ReferencingForeignKeys ?? Enumerable.Empty(); /// /// Returns a value indicating whether the key is the primary key. /// /// The key to find whether it is primary. /// if the key is the primary key. - public static bool IsPrimaryKey([NotNull] this IKey key) + public static bool IsPrimaryKey([NotNull] this IReadOnlyKey key) => key == key.DeclaringEntityType.FindPrimaryKey(); /// @@ -75,7 +75,7 @@ public static bool IsPrimaryKey([NotNull] this IKey key) /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IKey key, + [NotNull] this IReadOnlyKey key, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/ModelExtensions.cs b/src/EFCore/Extensions/ModelExtensions.cs index 1baf47e426b..af9049f4e08 100644 --- a/src/EFCore/Extensions/ModelExtensions.cs +++ b/src/EFCore/Extensions/ModelExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Reflection; using System.Text; using JetBrains.Annotations; @@ -18,7 +19,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class ModelExtensions { @@ -29,31 +30,11 @@ public static class ModelExtensions /// /// The model to find the entity type in. /// The type to find the corresponding entity type for. - /// The entity type, or if none if found. + /// The entity type, or if none is found. [DebuggerStepThrough] - public static IEntityType? FindEntityType([NotNull] this IModel model, [NotNull] Type type) + public static IReadOnlyEntityType? FindEntityType([NotNull] this IReadOnlyModel model, [NotNull] Type type) => ((Model)model).FindEntityType(Check.NotNull(type, nameof(type))); - /// - /// Gets the entity that maps the given entity class, where the class may be a proxy derived from the - /// actual entity type. Returns if no entity type with the given CLR type is found - /// or the given CLR type is being used by shared type entity type - /// or the entity type has a defining navigation. - /// - /// The model to find the entity type in. - /// The type to find the corresponding entity type for. - /// The entity type, or if none if found. - public static IEntityType? FindRuntimeEntityType([NotNull] this IModel model, [NotNull] Type type) - { - Check.NotNull(type, nameof(type)); - var realModel = (Model)Check.NotNull(model, nameof(model)); - - return realModel.FindEntityType(type) - ?? (type.BaseType == null - ? null - : realModel.FindEntityType(type.BaseType)); - } - /// /// Gets the entity type for the given type, defining navigation name /// and the defining entity type. Returns if no matching entity type is found. @@ -62,13 +43,13 @@ public static class ModelExtensions /// The type of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. [DebuggerStepThrough] - public static IEntityType? FindEntityType( - [NotNull] this IModel model, + public static IReadOnlyEntityType? FindEntityType( + [NotNull] this IReadOnlyModel model, [NotNull] Type type, [NotNull] string definingNavigationName, - [NotNull] IEntityType definingEntityType) + [NotNull] IReadOnlyEntityType definingEntityType) { Check.NotNull(model, nameof(model)); Check.NotNull(type, nameof(type)); @@ -88,7 +69,7 @@ public static class ModelExtensions /// The type of the entity type to find. /// The entity types found. [DebuggerStepThrough] - public static IEnumerable GetEntityTypes([NotNull] this IModel model, [NotNull] Type type) + public static IEnumerable GetEntityTypes([NotNull] this IReadOnlyModel model, [NotNull] Type type) => ((Model)model).GetEntityTypes(type); /// @@ -99,7 +80,7 @@ public static IEnumerable GetEntityTypes([NotNull] this IModel mode /// The entity types found. [DebuggerStepThrough] [Obsolete("Use GetEntityTypes(Type) or FindEntityType(string)")] - public static IReadOnlyCollection GetEntityTypes([NotNull] this IModel model, [NotNull] string name) + public static IReadOnlyCollection GetEntityTypes([NotNull] this IReadOnlyModel model, [NotNull] string name) => ((Model)model).GetEntityTypes(name); /// @@ -110,7 +91,7 @@ public static IReadOnlyCollection GetEntityTypes([NotNull] this IMo /// if the model contains a corresponding entity type with a defining navigation. [DebuggerStepThrough] [Obsolete("Use IsShared(Type)")] - public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IModel model, [NotNull] Type type) + public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IReadOnlyModel model, [NotNull] Type type) => model.IsShared(type); /// @@ -121,9 +102,64 @@ public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IModel mod /// if the model contains a corresponding entity type with a defining navigation. [DebuggerStepThrough] [Obsolete("Use FindEntityType(string)?.HasSharedClrType")] - public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IModel model, [NotNull] string name) + public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IReadOnlyModel model, [NotNull] string name) => model.FindEntityType(name)?.HasSharedClrType ?? false; + /// + /// Returns the entity types corresponding to the least derived types from the given. + /// + /// The model to find the entity types in. + /// The base type. + /// An optional condition for filtering entity types. + /// List of entity types corresponding to the least derived types from the given. + public static IEnumerable FindLeastDerivedEntityTypes( + [NotNull] this IReadOnlyModel model, + [NotNull] Type type, + [CanBeNull] Func? condition = null) + { + var derivedLevels = new Dictionary { [type] = 0 }; + + var leastDerivedTypesGroups = model.GetEntityTypes() + .GroupBy(t => GetDerivedLevel(t.ClrType, derivedLevels)) + .Where(g => g.Key != int.MaxValue) + .OrderBy(g => g.Key); + + foreach (var leastDerivedTypes in leastDerivedTypesGroups) + { + if (condition == null) + { + return leastDerivedTypes.ToList(); + } + + var filteredTypes = leastDerivedTypes.Where(condition).ToList(); + if (filteredTypes.Count > 0) + { + return filteredTypes; + } + } + + return Enumerable.Empty(); + } + + private static int GetDerivedLevel(Type? derivedType, Dictionary derivedLevels) + { + if (derivedType?.BaseType == null) + { + return int.MaxValue; + } + + if (derivedLevels.TryGetValue(derivedType, out var level)) + { + return level; + } + + var baseType = derivedType.BaseType; + level = GetDerivedLevel(baseType, derivedLevels); + level += level == int.MaxValue ? 0 : 1; + derivedLevels.Add(derivedType, level); + return level; + } + /// /// Gets whether the CLR type is used by shared type entities in the model. /// @@ -131,7 +167,7 @@ public static bool HasEntityTypeWithDefiningNavigation([NotNull] this IModel mod /// The CLR type. /// Whether the CLR type is used by shared type entities in the model. [DebuggerStepThrough] - public static bool IsShared([NotNull] this IModel model, [NotNull] Type type) + public static bool IsShared([NotNull] this IReadOnlyModel model, [NotNull] Type type) => Check.NotNull(model, nameof(model)).AsModel().IsShared(Check.NotNull(type, nameof(type))); /// @@ -141,7 +177,7 @@ public static bool IsShared([NotNull] this IModel model, [NotNull] Type type) /// The model to get the default change tracking strategy for. /// The change tracking strategy. [DebuggerStepThrough] - public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IModel model) + public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IReadOnlyModel model) => ((Model)model).GetChangeTrackingStrategy(); /// @@ -155,9 +191,9 @@ public static ChangeTrackingStrategy GetChangeTrackingStrategy([NotNull] this IM /// /// /// The model to get the access mode for. - /// The access mode being used, or null if the default access mode is being used. + /// The access mode being used. [DebuggerStepThrough] - public static PropertyAccessMode GetPropertyAccessMode([NotNull] this IModel model) + public static PropertyAccessMode GetPropertyAccessMode([NotNull] this IReadOnlyModel model) => (PropertyAccessMode?)Check.NotNull(model, nameof(model))[CoreAnnotationNames.PropertyAccessMode] ?? PropertyAccessMode.PreferField; @@ -165,7 +201,7 @@ public static PropertyAccessMode GetPropertyAccessMode([NotNull] this IModel mod /// Gets the EF Core assembly version used to build this model /// /// The model to get the version for. - public static string? GetProductVersion([NotNull] this IModel model) + public static string? GetProductVersion([NotNull] this IReadOnlyModel model) => model[CoreAnnotationNames.ProductVersion] as string; /// @@ -173,7 +209,7 @@ public static PropertyAccessMode GetPropertyAccessMode([NotNull] this IModel mod /// /// The model to use. /// The MethodInfo to check for. - public static bool IsIndexerMethod([NotNull] this IModel model, [NotNull] MethodInfo methodInfo) + public static bool IsIndexerMethod([NotNull] this IReadOnlyModel model, [NotNull] MethodInfo methodInfo) => !methodInfo.IsStatic && methodInfo.IsSpecialName && methodInfo.DeclaringType != null @@ -194,7 +230,7 @@ public static bool IsIndexerMethod([NotNull] this IModel model, [NotNull] Method /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IModel model, + [NotNull] this IReadOnlyModel model, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/MutableAnnotatableExtensions.cs b/src/EFCore/Extensions/MutableAnnotatableExtensions.cs index 1577e8d8748..d458d0b42f9 100644 --- a/src/EFCore/Extensions/MutableAnnotatableExtensions.cs +++ b/src/EFCore/Extensions/MutableAnnotatableExtensions.cs @@ -24,7 +24,7 @@ public static class MutableAnnotatableExtensions /// The key of the annotation to find. /// The annotation with the specified name. public static IAnnotation GetAnnotation([NotNull] this IMutableAnnotatable annotatable, [NotNull] string annotationName) - => ((IAnnotatable)annotatable).GetAnnotation(annotationName); + => ((IReadOnlyAnnotatable)annotatable).GetAnnotation(annotationName); /// /// Adds annotations to an object. diff --git a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs index 2d4e3913df2..322a8c1fb0f 100644 --- a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs +++ b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs @@ -31,7 +31,7 @@ public static class MutableEntityTypeExtensions /// The root base type. If the given entity type is not a derived type, then the same entity type is returned. /// public static IMutableEntityType GetRootType([NotNull] this IMutableEntityType entityType) - => (IMutableEntityType)((IEntityType)entityType).GetRootType(); + => (IMutableEntityType)((IReadOnlyEntityType)entityType).GetRootType(); /// /// Gets all types in the model from which a given entity type derives, starting with the root. @@ -51,7 +51,15 @@ public static IEnumerable GetAllBaseTypes([NotNull] this IMu /// The base types. /// public static IEnumerable GetAllBaseTypesAscending([NotNull] this IMutableEntityType entityType) - => entityType.GetAllBaseTypesInclusiveAscending().Skip(1).Cast(); + => entityType.GetAllBaseTypesInclusiveAscending().Skip(1); + + /// + /// Returns all base types of the given entity type, including the type itself, bottom to top. + /// + /// The entity type. + /// Base types. + public static IEnumerable GetAllBaseTypesInclusiveAscending([NotNull] this IMutableEntityType entityType) + => ((IReadOnlyEntityType)entityType).GetAllBaseTypesInclusiveAscending().Cast(); /// /// Gets all types in the model that derive from a given entity type. @@ -79,7 +87,7 @@ public static IEnumerable GetDirectlyDerivedTypes([NotNull] /// /// - /// Gets all keys declared on the given . + /// Gets all keys declared on the given . /// /// /// This method does not return keys declared on base types. @@ -92,6 +100,38 @@ public static IEnumerable GetDirectlyDerivedTypes([NotNull] public static IEnumerable GetDeclaredKeys([NotNull] this IMutableEntityType entityType) => ((EntityType)entityType).GetDeclaredKeys(); + /// + /// Gets the primary or alternate key that is defined on the given property. Returns if no key is defined + /// for the given property. + /// + /// The entity type. + /// The property that the key is defined on. + /// The key, or null if none is defined. + public static IMutableKey? FindKey([NotNull] this IMutableEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindKey(new[] { property }); + + /// + /// Adds a new alternate key to this entity type. + /// + /// The entity type. + /// The property to use as an alternate key. + /// The newly created key. + public static IMutableKey AddKey( + [NotNull] this IMutableEntityType entityType, + [NotNull] IMutableProperty property) + => Check.NotNull(entityType, nameof(entityType)).AddKey(new[] { property }); + + /// + /// Removes a primary or alternate key from this entity type. + /// + /// The entity type. + /// The properties that make up the key. + /// The removed key, or if the key was not found. + public static IMutableKey? RemoveKey( + [NotNull] this IMutableEntityType entityType, + [NotNull] IReadOnlyList properties) + => ((EntityType)entityType).RemoveKey(properties); + /// /// /// Gets all non-navigation properties declared on the given . @@ -107,6 +147,15 @@ public static IEnumerable GetDeclaredKeys([NotNull] this IMutableEn public static IEnumerable GetDeclaredProperties([NotNull] this IMutableEntityType entityType) => ((EntityType)entityType).GetDeclaredProperties(); + /// + /// Removes a property from this entity type. + /// + /// The entity type. + /// The name of the property to remove. + /// The removed property, or if the property was not found. + public static IMutableProperty? RemoveProperty([NotNull] this IMutableEntityType entityType, [NotNull] string name) + => ((EntityType)entityType).RemoveProperty(name); + /// /// /// Gets all navigation properties declared on the given . @@ -144,7 +193,7 @@ public static IEnumerable GetDeclaredServiceProperties( /// /// This method does not return indexes declared on base types. /// It is useful when iterating over all entity types to avoid processing the same index more than once. - /// Use to also return indexes declared on base types. + /// Use to also return indexes declared on base types. /// /// /// The entity type. @@ -153,49 +202,45 @@ public static IEnumerable GetDeclaredIndexes([NotNull] this IMuta => ((EntityType)entityType).GetDeclaredIndexes(); /// - /// Removes a property from this entity type. + /// + /// Gets all indexes declared on the types derived from the given . + /// /// /// The entity type. - /// The name of the property to remove. - /// The removed property, or if the property was not found. - public static IMutableProperty? RemoveProperty([NotNull] this IMutableEntityType entityType, [NotNull] string name) - => ((EntityType)entityType).RemoveProperty(name); + /// Derived indexes. + public static IEnumerable GetDerivedIndexes([NotNull] this IMutableEntityType entityType) + => ((EntityType)entityType).GetDerivedIndexes(); /// - /// Gets the primary or alternate key that is defined on the given property. Returns if no key is defined - /// for the given property. + /// Gets the index defined on the given property. Returns null if no index is defined. /// /// The entity type. - /// The property that the key is defined on. - /// The key, or null if none is defined. - public static IMutableKey? FindKey([NotNull] this IMutableEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindKey(new[] { property }); - } + /// The property to find the index on. + /// The index, or null if none is found. + public static IMutableIndex? FindIndex([NotNull] this IMutableEntityType entityType, [NotNull] IReadOnlyProperty property) + => entityType.FindIndex(new[] { property }); /// - /// Adds a new alternate key to this entity type. + /// Adds an index to this entity type. /// /// The entity type. - /// The property to use as an alternate key. - /// The newly created key. - public static IMutableKey AddKey( + /// The property to be indexed. + /// The newly created index. + public static IMutableIndex AddIndex( [NotNull] this IMutableEntityType entityType, [NotNull] IMutableProperty property) - => Check.NotNull(entityType, nameof(entityType)).AddKey(new[] { property }); + => Check.NotNull(entityType, nameof(entityType)).AddIndex(new[] { property }); /// - /// Removes a primary or alternate key from this entity type. + /// Removes an index from this entity type. /// /// The entity type. - /// The properties that make up the key. - /// The removed key, or if the key was not found. - public static IMutableKey? RemoveKey( + /// The properties that make up the index. + /// The removed index, or if the index was not found. + public static IMutableIndex? RemoveIndex( [NotNull] this IMutableEntityType entityType, [NotNull] IReadOnlyList properties) - => ((EntityType)entityType).RemoveKey(properties); + => ((EntityType)entityType).RemoveIndex(properties); /// /// @@ -216,11 +261,6 @@ public static IEnumerable GetDeclaredForeignKeys([NotNull] t /// /// Gets all foreign keys declared on the types derived from the given . /// - /// - /// This method does not return foreign keys declared on the given entity type itself. - /// Use to return foreign keys declared on this - /// and base entity typed types. - /// /// /// The entity type. /// Derived foreign keys. @@ -236,7 +276,7 @@ public static IEnumerable GetDerivedForeignKeys([NotNull] th /// The foreign keys. public static IEnumerable FindForeignKeys( [NotNull] this IMutableEntityType entityType, - [NotNull] IProperty property) + [NotNull] IReadOnlyProperty property) => entityType.FindForeignKeys(new[] { property }); /// @@ -248,7 +288,7 @@ public static IEnumerable FindForeignKeys( /// The foreign keys. public static IEnumerable FindForeignKeys( [NotNull] this IMutableEntityType entityType, - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => ((EntityType)entityType).FindForeignKeys(properties); /// @@ -266,24 +306,20 @@ public static IEnumerable FindForeignKeys( /// The foreign key, or if none is defined. public static IMutableForeignKey? FindForeignKey( [NotNull] this IMutableEntityType entityType, - [NotNull] IProperty property, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindForeignKey(new[] { property }, principalKey, principalEntityType); - } + [NotNull] IReadOnlyProperty property, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) + => entityType.FindForeignKey(new[] { property }, principalKey, principalEntityType); /// - /// Gets the foreign keys declared on the given using the given properties. + /// Gets the foreign keys declared on the given using the given properties. /// /// The entity type. /// The properties to find the foreign keys on. /// Declared foreign keys. public static IEnumerable FindDeclaredForeignKeys( [NotNull] this IMutableEntityType entityType, - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => ((EntityType)entityType).FindDeclaredForeignKeys(properties); /// @@ -333,7 +369,7 @@ public static IMutableForeignKey AddForeignKey( /// The entity type. /// The relationship to the owner if this is an owned type or otherwise. public static IMutableForeignKey? FindOwnership([NotNull] this IMutableEntityType entityType) - => ((EntityType)entityType).FindOwnership(); + => (IMutableForeignKey?)((IReadOnlyEntityType)entityType).FindOwnership(); /// /// Removes a foreign key from this entity type. @@ -373,7 +409,7 @@ public static IMutableForeignKey AddForeignKey( /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. public static IMutableNavigation? FindNavigation([NotNull] this IMutableEntityType entityType, [NotNull] string name) - => ((EntityType)entityType).FindNavigation(name); + => (IMutableNavigation?)((IReadOnlyEntityType)entityType).FindNavigation(name); /// /// Gets a navigation property on the given entity type. Does not return navigation properties defined on a base type. @@ -392,7 +428,7 @@ public static IMutableForeignKey AddForeignKey( /// The defining navigation if one exists or otherwise. [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] public static IMutableNavigation? FindDefiningNavigation([NotNull] this IMutableEntityType entityType) - => (IMutableNavigation?)((IEntityType)entityType).FindDefiningNavigation(); + => (IMutableNavigation?)((IReadOnlyEntityType)entityType).FindDefiningNavigation(); /// /// Gets all navigation properties on the given entity type. @@ -400,7 +436,7 @@ public static IMutableForeignKey AddForeignKey( /// The entity type. /// All navigation properties on the given entity type. public static IEnumerable GetNavigations([NotNull] this IMutableEntityType entityType) - => ((EntityType)entityType).GetNavigations(); + => ((IReadOnlyEntityType)entityType).GetNavigations().Cast(); /// /// @@ -412,15 +448,10 @@ public static IEnumerable GetNavigations([NotNull] this IMut /// /// /// The entity type. - /// The property on the entity class. + /// The property on the entity class. /// The property, or if none is found. - public static IMutableProperty? FindProperty([NotNull] this IMutableEntityType entityType, [NotNull] PropertyInfo propertyInfo) - { - Check.NotNull(entityType, nameof(entityType)); - Check.NotNull(propertyInfo, nameof(propertyInfo)); - - return propertyInfo.IsIndexerProperty() ? null : entityType.FindProperty(propertyInfo.GetSimpleMemberName()); - } + public static IMutableProperty? FindProperty([NotNull] this IMutableEntityType entityType, [NotNull] MemberInfo memberInfo) + => (IMutableProperty?)((IReadOnlyEntityType)entityType).FindProperty(memberInfo); /// /// @@ -436,7 +467,24 @@ public static IEnumerable GetNavigations([NotNull] this IMut public static IReadOnlyList? FindProperties( [NotNull] this IMutableEntityType entityType, [NotNull] IReadOnlyList propertyNames) - => ((EntityType)entityType).FindProperties(Check.NotNull(propertyNames, nameof(propertyNames))); + => (IReadOnlyList?)((IReadOnlyEntityType)entityType).FindProperties(propertyNames); + + /// + /// + /// Gets a property with the given name. + /// + /// + /// This API only finds scalar properties and does not find navigation properties. Use + /// to find a navigation property. + /// + /// + /// The entity type. + /// The property name. + /// The property, or if none is found. + public static IMutableProperty GetProperty( + [NotNull] this IMutableEntityType entityType, + [NotNull] string name) + => (IMutableProperty)((IReadOnlyEntityType)entityType).GetProperty(name); /// /// Finds a property declared on the type with the given name. @@ -508,41 +556,6 @@ public static IMutableProperty AddIndexerProperty( return entityType.AddProperty(name, propertyType, indexerPropertyInfo); } - /// - /// Gets the index defined on the given property. Returns null if no index is defined. - /// - /// The entity type. - /// The property to find the index on. - /// The index, or null if none is found. - public static IMutableIndex? FindIndex([NotNull] this IMutableEntityType entityType, [NotNull] IProperty property) - { - Check.NotNull(entityType, nameof(entityType)); - - return entityType.FindIndex(new[] { property }); - } - - /// - /// Adds an index to this entity type. - /// - /// The entity type. - /// The property to be indexed. - /// The newly created index. - public static IMutableIndex AddIndex( - [NotNull] this IMutableEntityType entityType, - [NotNull] IMutableProperty property) - => Check.NotNull(entityType, nameof(entityType)).AddIndex(new[] { property }); - - /// - /// Removes an index from this entity type. - /// - /// The entity type. - /// The properties that make up the index. - /// The removed index, or if the index was not found. - public static IMutableIndex? RemoveIndex( - [NotNull] this IMutableEntityType entityType, - [NotNull] IReadOnlyList properties) - => ((EntityType)entityType).RemoveIndex(properties); - /// /// Sets the change tracking strategy to use for this entity type. This strategy indicates how the /// context detects changes to properties for an instance of the entity type. @@ -582,14 +595,14 @@ public static void SetDefiningQuery( /// /// The entity type. public static IMutableProperty? GetDiscriminatorProperty([NotNull] this IMutableEntityType entityType) - => (IMutableProperty?)((IEntityType)entityType).GetDiscriminatorProperty(); + => (IMutableProperty?)((IReadOnlyEntityType)entityType).GetDiscriminatorProperty(); /// - /// Sets the that will be used for storing a discriminator value. + /// Sets the that will be used for storing a discriminator value. /// /// The entity type. /// The property to set. - public static void SetDiscriminatorProperty([NotNull] this IMutableEntityType entityType, [CanBeNull] IProperty? property) + public static void SetDiscriminatorProperty([NotNull] this IMutableEntityType entityType, [CanBeNull] IReadOnlyProperty? property) => Check.NotNull(entityType, nameof(entityType)).AsEntityType() .SetDiscriminatorProperty((Property?)property, ConfigurationSource.Explicit); diff --git a/src/EFCore/Extensions/MutableForeignKeyExtensions.cs b/src/EFCore/Extensions/MutableForeignKeyExtensions.cs index 27ae946b4be..3a02951c406 100644 --- a/src/EFCore/Extensions/MutableForeignKeyExtensions.cs +++ b/src/EFCore/Extensions/MutableForeignKeyExtensions.cs @@ -23,7 +23,7 @@ public static class MutableForeignKeyExtensions public static IMutableEntityType GetRelatedEntityType( [NotNull] this IMutableForeignKey foreignKey, [NotNull] IMutableEntityType entityType) - => (IMutableEntityType)((IForeignKey)foreignKey).GetRelatedEntityType(entityType); + => (IMutableEntityType)((IReadOnlyForeignKey)foreignKey).GetRelatedEntityType(entityType); /// /// Returns a navigation associated with this foreign key. diff --git a/src/EFCore/Extensions/MutableKeyExtensions.cs b/src/EFCore/Extensions/MutableKeyExtensions.cs index 6ae3b46606d..4d3b84bebe7 100644 --- a/src/EFCore/Extensions/MutableKeyExtensions.cs +++ b/src/EFCore/Extensions/MutableKeyExtensions.cs @@ -22,6 +22,6 @@ public static class MutableKeyExtensions /// The key to find the foreign keys for. /// The foreign keys that reference the given key. public static IEnumerable GetReferencingForeignKeys([NotNull] this IMutableKey key) - => ((IKey)key).GetReferencingForeignKeys().Cast(); + => ((IReadOnlyKey)key).GetReferencingForeignKeys().Cast(); } } diff --git a/src/EFCore/Extensions/MutableModelExtensions.cs b/src/EFCore/Extensions/MutableModelExtensions.cs index c893d72f298..2e59c0d37cb 100644 --- a/src/EFCore/Extensions/MutableModelExtensions.cs +++ b/src/EFCore/Extensions/MutableModelExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -26,7 +27,7 @@ public static class MutableModelExtensions /// /// The model to find the entity type in. /// The type to find the corresponding entity type for. - /// The entity type, or if none if found. + /// The entity type, or if none is found. public static IMutableEntityType? FindEntityType([NotNull] this IMutableModel model, [NotNull] Type type) => ((Model)model).FindEntityType(type); @@ -38,13 +39,13 @@ public static class MutableModelExtensions /// The type of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. public static IMutableEntityType? FindEntityType( [NotNull] this IMutableModel model, [NotNull] Type type, [NotNull] string definingNavigationName, [NotNull] IMutableEntityType definingEntityType) - => (IMutableEntityType?)((IModel)model).FindEntityType(type, definingNavigationName, definingEntityType); + => (IMutableEntityType?)((IReadOnlyModel)model).FindEntityType(type, definingNavigationName, definingEntityType); /// /// Gets the entity types matching the given type. @@ -146,12 +147,12 @@ public static IReadOnlyCollection GetEntityTypes([NotNull] t /// The base type. /// An optional condition for filtering entity types. /// List of entity types corresponding to the least derived types from the given. - public static IReadOnlyList FindLeastDerivedEntityTypes( + public static IEnumerable FindLeastDerivedEntityTypes( [NotNull] this IMutableModel model, [NotNull] Type type, [CanBeNull] Func? condition = null) - => Check.NotNull((Model)model, nameof(model)) - .FindLeastDerivedEntityTypes(type, condition); + => ((IReadOnlyModel)model).FindLeastDerivedEntityTypes(type, condition == null ? null : t => condition((IMutableEntityType)t)) + .Cast(); /// /// Removes the ignored entity type. @@ -264,8 +265,8 @@ public static void AddShared([NotNull] this IMutableModel model, [NotNull] Type /// explicitly in cases where the automatic execution is not possible. /// /// The model to finalize. - /// The finalized . + /// The finalized model. public static IModel FinalizeModel([NotNull] this IMutableModel model) - => ((Model)model).FinalizeModel()!; + => ((Model)model).FinalizeModel(); } } diff --git a/src/EFCore/Extensions/MutablePropertyExtensions.cs b/src/EFCore/Extensions/MutablePropertyExtensions.cs index 84ece9c2e8b..04dd66373e6 100644 --- a/src/EFCore/Extensions/MutablePropertyExtensions.cs +++ b/src/EFCore/Extensions/MutablePropertyExtensions.cs @@ -29,7 +29,7 @@ public static class MutablePropertyExtensions /// The foreign key property. /// The first associated principal property, or if none exists. public static IMutableProperty? FindFirstPrincipal([NotNull] this IMutableProperty property) - => (IMutableProperty?)((IProperty)property).FindFirstPrincipal(); + => (IMutableProperty?)((IReadOnlyProperty)property).FindFirstPrincipal(); /// /// Finds the list of principal properties including the given property that the given property is constrained by @@ -38,7 +38,7 @@ public static class MutablePropertyExtensions /// The foreign key property. /// The list of all associated principal properties including the given property. public static IReadOnlyList FindPrincipals([NotNull] this IMutableProperty property) - => ((IProperty)property).FindPrincipals().Cast().ToList(); + => ((IReadOnlyProperty)property).FindPrincipals().Cast().ToList(); /// /// Gets all foreign keys that use this property (including composite foreign keys in which this property @@ -71,7 +71,7 @@ public static IEnumerable GetContainingIndexes([NotNull] this IMu /// The primary that use this property, or if it is not part of the primary key. /// public static IMutableKey? FindContainingPrimaryKey([NotNull] this IMutableProperty property) - => (IMutableKey?)((IProperty)property).FindContainingPrimaryKey(); + => (IMutableKey?)((IReadOnlyProperty)property).FindContainingPrimaryKey(); /// /// Gets all primary or alternate keys that use this property (including composite keys in which this property diff --git a/src/EFCore/Extensions/NavigationExtensions.cs b/src/EFCore/Extensions/NavigationExtensions.cs index 4a52e42b6b9..c3bfe1fd178 100644 --- a/src/EFCore/Extensions/NavigationExtensions.cs +++ b/src/EFCore/Extensions/NavigationExtensions.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class NavigationExtensions { @@ -31,7 +31,7 @@ public static class NavigationExtensions /// [DebuggerStepThrough] [Obsolete("Use INavigation.IsOnDependent")] - public static bool IsDependentToPrincipal([NotNull] this INavigation navigation) + public static bool IsDependentToPrincipal([NotNull] this IReadOnlyNavigation navigation) => Check.NotNull(navigation, nameof(navigation)).IsOnDependent; /// @@ -43,7 +43,7 @@ public static bool IsDependentToPrincipal([NotNull] this INavigation navigation) /// [DebuggerStepThrough] [Obsolete("Use INavigation.IsCollection")] - public static bool IsCollection([NotNull] this INavigation navigation) + public static bool IsCollection([NotNull] this IReadOnlyNavigation navigation) => Check.NotNull(navigation, nameof(navigation)).IsCollection; /// @@ -56,7 +56,7 @@ public static bool IsCollection([NotNull] this INavigation navigation) /// [DebuggerStepThrough] [Obsolete("Use INavigation.Inverse")] - public static INavigation? FindInverse([NotNull] this INavigation navigation) + public static IReadOnlyNavigation? FindInverse([NotNull] this IReadOnlyNavigation navigation) => Check.NotNull(navigation, nameof(navigation)).Inverse; /// @@ -67,7 +67,7 @@ public static bool IsCollection([NotNull] this INavigation navigation) /// The target entity type. [DebuggerStepThrough] [Obsolete("Use INavigation.TargetEntityType")] - public static IEntityType GetTargetType([NotNull] this INavigation navigation) + public static IReadOnlyEntityType GetTargetType([NotNull] this IReadOnlyNavigation navigation) => Check.NotNull(navigation, nameof(navigation)).TargetEntityType; /// @@ -76,7 +76,7 @@ public static IEntityType GetTargetType([NotNull] this INavigation navigation) /// The navigation property to find whether it should be eager loaded. /// A value indicating whether this navigation should be eager loaded by default. [Obsolete("Use INavigation.IsEagerLoaded")] - public static bool IsEagerLoaded([NotNull] this INavigation navigation) + public static bool IsEagerLoaded([NotNull] this IReadOnlyNavigation navigation) => Check.NotNull(navigation, nameof(navigation)).IsEagerLoaded; /// @@ -93,7 +93,7 @@ public static bool IsEagerLoaded([NotNull] this INavigation navigation) /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this INavigation navigation, + [NotNull] this IReadOnlyNavigation navigation, MetadataDebugStringOptions options, int indent = 0) { @@ -145,9 +145,10 @@ public static string ToDebugString( builder.Append(" PropertyAccessMode.").Append(navigation.GetPropertyAccessMode()); } - if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0) + if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 + && ((Annotatable)navigation).IsReadOnly) { - var indexes = navigation.GetPropertyIndexes(); + var indexes = ((INavigation)navigation).GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); builder.Append(" ").Append(indexes.OriginalValueIndex); builder.Append(" ").Append(indexes.RelationshipIndex); diff --git a/src/EFCore/Extensions/PropertyBaseExtensions.cs b/src/EFCore/Extensions/PropertyBaseExtensions.cs index d0330222c13..48c78e999b2 100644 --- a/src/EFCore/Extensions/PropertyBaseExtensions.cs +++ b/src/EFCore/Extensions/PropertyBaseExtensions.cs @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class PropertyBaseExtensions { @@ -60,30 +60,16 @@ public static MemberInfo GetMemberInfo( /// /// The property. /// The comparer. - public static IComparer GetCurrentValueComparer([NotNull] this IPropertyBase propertyBase) + public static IComparer GetCurrentValueComparer([NotNull] this IReadOnlyPropertyBase propertyBase) => propertyBase.AsPropertyBase().CurrentValueComparer; - /// - /// - /// Gets a for reading the value of this property. - /// - /// - /// Note that it is an error to call this method for a shadow property () since - /// such a property has no associated . - /// - /// - /// The property. - /// The accessor. - public static IClrPropertyGetter GetGetter([NotNull] this IPropertyBase propertyBase) - => propertyBase.AsPropertyBase().Getter; - /// /// Gets the name of the backing field for this property, or if the backing field /// is not known. /// /// The property for which the backing field will be returned. /// The name of the backing field, or . - public static string? GetFieldName([NotNull] this IPropertyBase propertyBase) + public static string? GetFieldName([NotNull] this IReadOnlyPropertyBase propertyBase) => propertyBase.FieldInfo?.GetSimpleMemberName(); /// @@ -95,7 +81,7 @@ public static IClrPropertyGetter GetGetter([NotNull] this IPropertyBase property /// /// if the property is a shadow property, otherwise . /// - public static bool IsShadowProperty([NotNull] this IPropertyBase property) + public static bool IsShadowProperty([NotNull] this IReadOnlyPropertyBase property) => Check.NotNull(property, nameof(property)).GetIdentifyingMemberInfo() == null; /// @@ -106,7 +92,7 @@ public static bool IsShadowProperty([NotNull] this IPropertyBase property) /// /// if the property is an indexer property, otherwise . /// - public static bool IsIndexerProperty([NotNull] this IPropertyBase property) + public static bool IsIndexerProperty([NotNull] this IReadOnlyPropertyBase property) => Check.NotNull(property, nameof(property)).GetIdentifyingMemberInfo() is PropertyInfo propertyInfo && propertyInfo == property.DeclaringType.FindIndexerPropertyInfo(); diff --git a/src/EFCore/Extensions/PropertyExtensions.cs b/src/EFCore/Extensions/PropertyExtensions.cs index 062c47a66f8..c53f3375d27 100644 --- a/src/EFCore/Extensions/PropertyExtensions.cs +++ b/src/EFCore/Extensions/PropertyExtensions.cs @@ -23,7 +23,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class PropertyExtensions { @@ -32,7 +32,7 @@ public static class PropertyExtensions /// /// The property. /// The type mapping. - public static CoreTypeMapping GetTypeMapping([NotNull] this IProperty property) + public static CoreTypeMapping GetTypeMapping([NotNull] this IReadOnlyProperty property) { var mapping = ((Property)property).TypeMapping; if (mapping == null) @@ -48,7 +48,7 @@ public static CoreTypeMapping GetTypeMapping([NotNull] this IProperty property) /// /// The property. /// The type mapping, or if none was found. - public static CoreTypeMapping? FindTypeMapping([NotNull] this IProperty property) + public static CoreTypeMapping? FindTypeMapping([NotNull] this IReadOnlyProperty property) => ((Property)property).TypeMapping; /// @@ -57,7 +57,7 @@ public static CoreTypeMapping GetTypeMapping([NotNull] this IProperty property) /// /// The foreign key property. /// The first associated principal property, or if none exists. - public static IProperty? FindFirstPrincipal([NotNull] this IProperty property) + public static IReadOnlyProperty? FindFirstPrincipal([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -85,14 +85,14 @@ public static CoreTypeMapping GetTypeMapping([NotNull] this IProperty property) /// /// The foreign key property. /// The list of all associated principal properties including the given property. - public static IReadOnlyList FindPrincipals([NotNull] this IProperty property) + public static IReadOnlyList FindPrincipals([NotNull] this IReadOnlyProperty property) { - var principals = new List { property }; + var principals = new List { property }; AddPrincipals(property, principals); return principals; } - private static void AddPrincipals(IProperty property, List visited) + private static void AddPrincipals(IReadOnlyProperty property, List visited) { var concreteProperty = property.AsProperty(); @@ -122,7 +122,7 @@ private static void AddPrincipals(IProperty property, List visited) /// /// The property to check. /// if the property is used as a foreign key, otherwise . - public static bool IsForeignKey([NotNull] this IProperty property) + public static bool IsForeignKey([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).ForeignKeys != null; /// @@ -130,7 +130,7 @@ public static bool IsForeignKey([NotNull] this IProperty property) /// /// The property to check. /// if the property is used as an index, otherwise . - public static bool IsIndex([NotNull] this IProperty property) + public static bool IsIndex([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).Indexes != null; /// @@ -138,7 +138,7 @@ public static bool IsIndex([NotNull] this IProperty property) /// /// The property to check. /// if the property is used as an unique index, otherwise . - public static bool IsUniqueIndex([NotNull] this IProperty property) + public static bool IsUniqueIndex([NotNull] this IReadOnlyProperty property) => Check.NotNull(property, nameof(property)).AsProperty().Indexes?.Any(e => e.IsUnique) == true; /// @@ -146,7 +146,7 @@ public static bool IsUniqueIndex([NotNull] this IProperty property) /// /// The property to check. /// if the property is used as the primary key, otherwise . - public static bool IsPrimaryKey([NotNull] this IProperty property) + public static bool IsPrimaryKey([NotNull] this IReadOnlyProperty property) => FindContainingPrimaryKey(property) != null; /// @@ -155,7 +155,7 @@ public static bool IsPrimaryKey([NotNull] this IProperty property) /// /// The property to check. /// if the property is used as a key, otherwise . - public static bool IsKey([NotNull] this IProperty property) + public static bool IsKey([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).Keys != null; /// @@ -164,7 +164,7 @@ public static bool IsKey([NotNull] this IProperty property) /// /// The property to get foreign keys for. /// The foreign keys that use this property. - public static IEnumerable GetContainingForeignKeys([NotNull] this IProperty property) + public static IEnumerable GetContainingForeignKeys([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).GetContainingForeignKeys(); /// @@ -173,7 +173,7 @@ public static IEnumerable GetContainingForeignKeys([NotNull] this I /// /// The property to get indexes for. /// The indexes that use this property. - public static IEnumerable GetContainingIndexes([NotNull] this IProperty property) + public static IEnumerable GetContainingIndexes([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).GetContainingIndexes(); /// @@ -182,7 +182,7 @@ public static IEnumerable GetContainingIndexes([NotNull] this IProperty /// /// The property to get primary key for. /// The primary that use this property, or if it is not part of the primary key. - public static IKey? FindContainingPrimaryKey([NotNull] this IProperty property) + public static IReadOnlyKey? FindContainingPrimaryKey([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).PrimaryKey; /// @@ -191,7 +191,7 @@ public static IEnumerable GetContainingIndexes([NotNull] this IProperty /// /// The property to get primary and alternate keys for. /// The primary and alternate keys that use this property. - public static IEnumerable GetContainingKeys([NotNull] this IProperty property) + public static IEnumerable GetContainingKeys([NotNull] this IReadOnlyProperty property) => Check.NotNull((Property)property, nameof(property)).GetContainingKeys(); /// @@ -200,7 +200,7 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope /// /// The property to get the maximum length of. /// The maximum length, or if none if defined. - public static int? GetMaxLength([NotNull] this IProperty property) + public static int? GetMaxLength([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -213,7 +213,7 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope /// /// The property to get the precision of. /// The precision, or if none is defined. - public static int? GetPrecision([NotNull] this IProperty property) + public static int? GetPrecision([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -226,7 +226,7 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope /// /// The property to get the scale of. /// The scale, or if none is defined. - public static int? GetScale([NotNull] this IProperty property) + public static int? GetScale([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -238,7 +238,7 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope /// /// The property to get the Unicode setting for. /// The Unicode setting, or if none is defined. - public static bool? IsUnicode([NotNull] this IProperty property) + public static bool? IsUnicode([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -261,7 +261,7 @@ public static IEnumerable GetContainingKeys([NotNull] this IProperty prope /// /// /// The property. - public static PropertySaveBehavior GetBeforeSaveBehavior([NotNull] this IProperty property) + public static PropertySaveBehavior GetBeforeSaveBehavior([NotNull] this IReadOnlyProperty property) => (PropertySaveBehavior?)Check.NotNull(property, nameof(property))[CoreAnnotationNames.BeforeSaveBehavior] ?? (property.ValueGenerated == ValueGenerated.OnAddOrUpdate ? PropertySaveBehavior.Ignore @@ -282,7 +282,7 @@ public static PropertySaveBehavior GetBeforeSaveBehavior([NotNull] this IPropert /// /// /// The property. - public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty property) + public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IReadOnlyProperty property) => (PropertySaveBehavior?)Check.NotNull(property, nameof(property))[CoreAnnotationNames.AfterSaveBehavior] ?? (property.IsKey() ? PropertySaveBehavior.Throw @@ -295,7 +295,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// /// The property to get the value generator factory for. /// The factory, or if no factory has been set. - public static Func? GetValueGeneratorFactory([NotNull] this IProperty property) + public static Func? GetValueGeneratorFactory([NotNull] this IReadOnlyProperty property) => (Func?) Check.NotNull(property, nameof(property))[CoreAnnotationNames.ValueGeneratorFactory]; @@ -304,7 +304,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// /// The property. /// The converter, or if none has been set. - public static ValueConverter? GetValueConverter([NotNull] this IProperty property) + public static ValueConverter? GetValueConverter([NotNull] this IReadOnlyProperty property) => (ValueConverter?)Check.NotNull(property, nameof(property))[CoreAnnotationNames.ValueConverter]; /// @@ -312,7 +312,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// /// The property. /// The provider type, or if none has been set. - public static Type? GetProviderClrType([NotNull] this IProperty property) + public static Type? GetProviderClrType([NotNull] this IReadOnlyProperty property) => (Type?)Check.NotNull(property, nameof(property))[CoreAnnotationNames.ProviderClrType]; /// @@ -320,7 +320,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// /// The property. /// The comparer, or if none has been set. - public static ValueComparer? GetValueComparer([NotNull] this IProperty property) + public static ValueComparer? GetValueComparer([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -333,7 +333,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// /// The property. /// The comparer, or if none has been set. - public static ValueComparer? GetKeyValueComparer([NotNull] this IProperty property) + public static ValueComparer? GetKeyValueComparer([NotNull] this IReadOnlyProperty property) { Check.NotNull(property, nameof(property)); @@ -347,7 +347,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// The property. /// The comparer, or if none has been set. [Obsolete("Use GetKeyValueComparer. A separate structural comparer is no longer supported.")] - public static ValueComparer? GetStructuralValueComparer([NotNull] this IProperty property) + public static ValueComparer? GetStructuralValueComparer([NotNull] this IReadOnlyProperty property) => property.GetKeyValueComparer(); /// @@ -356,7 +356,7 @@ public static PropertySaveBehavior GetAfterSaveBehavior([NotNull] this IProperty /// The property. /// The property type. /// A new equality comparer. - public static IEqualityComparer CreateKeyEqualityComparer([NotNull] this IProperty property) + public static IEqualityComparer CreateKeyEqualityComparer([NotNull] this IReadOnlyProperty property) { var comparer = property.GetKeyValueComparer()!; @@ -389,7 +389,7 @@ public int GetHashCode(TNullableKey obj) /// The properties to format. /// If true, then type names are included in the string. The default is . /// The string representation. - public static string Format([NotNull] this IEnumerable properties, bool includeTypes = false) + public static string Format([NotNull] this IEnumerable properties, bool includeTypes = false) => "{" + string.Join( ", ", @@ -411,7 +411,7 @@ public static string Format([NotNull] this IEnumerable properties /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IProperty property, + [NotNull] this IReadOnlyProperty property, MetadataDebugStringOptions options, int indent = 0) { @@ -511,17 +511,15 @@ public static string ToDebugString( builder.Append(" PropertyAccessMode.").Append(property.GetPropertyAccessMode()); } - if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0) + if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 + && ((Annotatable)property).IsReadOnly) { - var indexes = property.GetPropertyIndexes(); - if (indexes != null) - { - builder.Append(" ").Append(indexes.Index); - builder.Append(" ").Append(indexes.OriginalValueIndex); - builder.Append(" ").Append(indexes.RelationshipIndex); - builder.Append(" ").Append(indexes.ShadowIndex); - builder.Append(" ").Append(indexes.StoreGenerationIndex); - } + var indexes = ((IProperty)property).GetPropertyIndexes(); + builder.Append(" ").Append(indexes.Index); + builder.Append(" ").Append(indexes.OriginalValueIndex); + builder.Append(" ").Append(indexes.RelationshipIndex); + builder.Append(" ").Append(indexes.ShadowIndex); + builder.Append(" ").Append(indexes.StoreGenerationIndex); } if (!singleLine && (options & MetadataDebugStringOptions.IncludeAnnotations) != 0) diff --git a/src/EFCore/Extensions/ServicePropertyExtensions.cs b/src/EFCore/Extensions/ServicePropertyExtensions.cs index e3b315dbb18..179bcd5221d 100644 --- a/src/EFCore/Extensions/ServicePropertyExtensions.cs +++ b/src/EFCore/Extensions/ServicePropertyExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class ServicePropertyExtensions { @@ -30,7 +30,7 @@ public static class ServicePropertyExtensions /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this IServiceProperty serviceProperty, + [NotNull] this IReadOnlyServiceProperty serviceProperty, MetadataDebugStringOptions options, int indent = 0) { diff --git a/src/EFCore/Extensions/SkipNavigationExtensions.cs b/src/EFCore/Extensions/SkipNavigationExtensions.cs index 80e1bf725b6..7655b32917b 100644 --- a/src/EFCore/Extensions/SkipNavigationExtensions.cs +++ b/src/EFCore/Extensions/SkipNavigationExtensions.cs @@ -14,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class SkipNavigationExtensions { @@ -32,7 +32,7 @@ public static class SkipNavigationExtensions /// The number of indent spaces to use before each new line. /// A human-readable representation. public static string ToDebugString( - [NotNull] this ISkipNavigation navigation, + [NotNull] this IReadOnlySkipNavigation navigation, MetadataDebugStringOptions options, int indent = 0) { @@ -82,9 +82,10 @@ public static string ToDebugString( builder.Append(" PropertyAccessMode.").Append(navigation.GetPropertyAccessMode()); } - if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0) + if ((options & MetadataDebugStringOptions.IncludePropertyIndexes) != 0 + && ((Annotatable)navigation).IsReadOnly) { - var indexes = navigation.GetPropertyIndexes(); + var indexes = ((ISkipNavigation)navigation).GetPropertyIndexes(); builder.Append(" ").Append(indexes.Index); builder.Append(" ").Append(indexes.OriginalValueIndex); builder.Append(" ").Append(indexes.RelationshipIndex); diff --git a/src/EFCore/Extensions/TypeBaseExtensions.cs b/src/EFCore/Extensions/TypeBaseExtensions.cs index 9752f1f7b90..a51d7885e08 100644 --- a/src/EFCore/Extensions/TypeBaseExtensions.cs +++ b/src/EFCore/Extensions/TypeBaseExtensions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore { /// - /// Extension methods for . + /// Extension methods for . /// public static class TypeBaseExtensions { @@ -26,9 +26,9 @@ public static class TypeBaseExtensions /// /// /// The type for which to get the access mode. - /// The access mode being used, or null if the default access mode is being used. + /// The access mode being used. public static PropertyAccessMode GetPropertyAccessMode( - [NotNull] this ITypeBase typeBase) + [NotNull] this IReadOnlyTypeBase typeBase) => (PropertyAccessMode?)Check.NotNull(typeBase, nameof(typeBase))[CoreAnnotationNames.PropertyAccessMode] ?? typeBase.Model.GetPropertyAccessMode(); @@ -42,9 +42,9 @@ public static PropertyAccessMode GetPropertyAccessMode( /// /// /// The type for which to get the access mode. - /// The access mode being used, or null if the default access mode is being used. + /// The access mode being used. public static PropertyAccessMode GetNavigationAccessMode( - [NotNull] this ITypeBase typeBase) + [NotNull] this IReadOnlyTypeBase typeBase) => (PropertyAccessMode?)Check.NotNull(typeBase, nameof(typeBase))[CoreAnnotationNames.NavigationAccessMode] ?? typeBase.GetPropertyAccessMode(); } diff --git a/src/EFCore/Infrastructure/Annotatable.cs b/src/EFCore/Infrastructure/Annotatable.cs index f321efa7cbb..b8c54b39516 100644 --- a/src/EFCore/Infrastructure/Annotatable.cs +++ b/src/EFCore/Infrastructure/Annotatable.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using System.Threading; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Internal; @@ -26,7 +25,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure /// not used in application code. /// /// - public class Annotatable : IMutableAnnotatable + public class Annotatable : IAnnotatable, IMutableAnnotatable { private SortedDictionary? _annotations; private ConcurrentDictionary? _runtimeAnnotations; @@ -38,7 +37,7 @@ public class Annotatable : IMutableAnnotatable /// Runtime annotations cannot be changed when the object is not read-only. /// /// - protected virtual bool IsReadOnly => false; + public virtual bool IsReadOnly => false; /// /// Throws if the model is not read-only. @@ -379,12 +378,12 @@ private ConcurrentDictionary GetOrCreateRuntimeAnnotations() /// [DebuggerStepThrough] - IEnumerable IAnnotatable.GetAnnotations() + IEnumerable IReadOnlyAnnotatable.GetAnnotations() => GetAnnotations(); /// [DebuggerStepThrough] - IAnnotation? IAnnotatable.FindAnnotation(string name) + IAnnotation? IReadOnlyAnnotatable.FindAnnotation(string name) => FindAnnotation(name); /// diff --git a/src/EFCore/Infrastructure/AnnotatableExtensions.cs b/src/EFCore/Infrastructure/AnnotatableExtensions.cs index 17cd3b648e5..136a1937879 100644 --- a/src/EFCore/Infrastructure/AnnotatableExtensions.cs +++ b/src/EFCore/Infrastructure/AnnotatableExtensions.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// - /// Extension methods for . + /// Extension methods for . /// public static class AnnotatableExtensions { @@ -23,7 +23,7 @@ public static class AnnotatableExtensions /// The object to find the annotation on. /// The key of the annotation to find. /// The annotation with the specified name. - public static IAnnotation GetAnnotation([NotNull] this IAnnotatable annotatable, [NotNull] string annotationName) + public static IAnnotation GetAnnotation([NotNull] this IReadOnlyAnnotatable annotatable, [NotNull] string annotationName) { Check.NotNull(annotatable, nameof(annotatable)); Check.NotEmpty(annotationName, nameof(annotationName)); @@ -43,7 +43,7 @@ public static IAnnotation GetAnnotation([NotNull] this IAnnotatable annotatable, /// The object to get the annotations to print in debug string. /// The number of indent spaces to use before each new line. /// Debug string representation of all annotations. - public static string AnnotationsToDebugString([NotNull] this IAnnotatable annotatable, int indent = 0) + public static string AnnotationsToDebugString([NotNull] this IReadOnlyAnnotatable annotatable, int indent = 0) { var annotations = annotatable.GetAnnotations().ToList(); if (annotations.Count == 0) diff --git a/src/EFCore/Infrastructure/Annotation.cs b/src/EFCore/Infrastructure/Annotation.cs index fd3daf9393c..f4149b3a23e 100644 --- a/src/EFCore/Infrastructure/Annotation.cs +++ b/src/EFCore/Infrastructure/Annotation.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// /// - /// An arbitrary piece of metadata that can be stored on an object that implements . + /// An arbitrary piece of metadata that can be stored on an object that implements . /// /// /// This type is typically used by database providers (and other extensions). It is generally diff --git a/src/EFCore/Infrastructure/IAnnotatable.cs b/src/EFCore/Infrastructure/IAnnotatable.cs index ad64dff61be..c09caded5e6 100644 --- a/src/EFCore/Infrastructure/IAnnotatable.cs +++ b/src/EFCore/Infrastructure/IAnnotatable.cs @@ -11,38 +11,15 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// /// - /// A class that exposes annotations. Annotations allow for arbitrary metadata to be stored on an object. + /// A class that exposes build-time and run-time annotations. Annotations allow for arbitrary metadata to be stored on an object. /// /// /// This interface is typically used by database providers (and other extensions). It is generally /// not used in application code. /// /// - public interface IAnnotatable + public interface IAnnotatable : IReadOnlyAnnotatable { - /// - /// Gets the value of the annotation with the given name, returning if it does not exist. - /// - /// The name of the annotation to find. - /// - /// The value of the existing annotation if an annotation with the specified name already exists. Otherwise, . - /// - object? this[[NotNull] string name] { get; } - - /// - /// Gets the annotation with the given name, returning if it does not exist. - /// - /// The name of the annotation to find. - /// - /// The existing annotation if an annotation with the specified name already exists. Otherwise, . - /// - IAnnotation? FindAnnotation([NotNull] string name); - - /// - /// Gets all annotations on the current object. - /// - IEnumerable GetAnnotations(); - /// /// Gets the runtime annotation with the given name, returning if it does not exist. /// @@ -60,8 +37,8 @@ public interface IAnnotatable /// The value of the existing runtime annotation if an annotation with the specified name already exists. /// Otherwise, . /// - object? FindRuntimeAnnotationValue([NotNull] string name) => - FindRuntimeAnnotation(name)?.Value; + object? FindRuntimeAnnotationValue([NotNull] string name) + => FindRuntimeAnnotation(name)?.Value; /// /// Gets all the runtime annotations on the current object. diff --git a/src/EFCore/Infrastructure/IAnnotation.cs b/src/EFCore/Infrastructure/IAnnotation.cs index 611a3e8f920..e4d330e0248 100644 --- a/src/EFCore/Infrastructure/IAnnotation.cs +++ b/src/EFCore/Infrastructure/IAnnotation.cs @@ -5,7 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// /// - /// An arbitrary piece of metadata that can be stored on an object that implements . + /// An arbitrary piece of metadata that can be stored on an object that implements . /// /// /// This interface is typically used by database providers (and other extensions). It is generally diff --git a/src/EFCore/Infrastructure/IModelRuntimeInitializer.cs b/src/EFCore/Infrastructure/IModelRuntimeInitializer.cs index 497991bf3e2..c0552c3d9ce 100644 --- a/src/EFCore/Infrastructure/IModelRuntimeInitializer.cs +++ b/src/EFCore/Infrastructure/IModelRuntimeInitializer.cs @@ -34,6 +34,8 @@ public interface IModelRuntimeInitializer /// The model to initialize. /// The validation logger. /// The initialized model. - IModel Initialize([NotNull] IModel model, [NotNull] IDiagnosticsLogger? validationLogger); + IModel Initialize( + [NotNull] IModel model, + [NotNull] IDiagnosticsLogger? validationLogger); } } diff --git a/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs b/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs new file mode 100644 index 00000000000..8b9cfd5bc0f --- /dev/null +++ b/src/EFCore/Infrastructure/IReadOnlyAnnotatable.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using JetBrains.Annotations; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Infrastructure +{ + /// + /// + /// A class that supports annotations. Annotations allow for arbitrary metadata to be stored on an object. + /// + /// + /// This interface is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + public interface IReadOnlyAnnotatable + { + /// + /// Gets the value of the annotation with the given name, returning if it does not exist. + /// + /// The name of the annotation to find. + /// + /// The value of the existing annotation if an annotation with the specified name already exists. Otherwise, . + /// + object? this[[NotNull] string name] { get; } + + /// + /// Gets the annotation with the given name, returning if it does not exist. + /// + /// The name of the annotation to find. + /// + /// The existing annotation if an annotation with the specified name already exists. Otherwise, . + /// + IAnnotation? FindAnnotation([NotNull] string name); + + /// + /// Gets all annotations on the current object. + /// + IEnumerable GetAnnotations(); + } +} diff --git a/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs b/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs index 71a51627c4a..1b3db088bfc 100644 --- a/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs +++ b/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs @@ -49,7 +49,9 @@ public ModelRuntimeInitializer([NotNull] ModelRuntimeInitializerDependencies dep /// The model to initialize. /// The validation logger. /// The initialized model. - public virtual IModel Initialize(IModel model, IDiagnosticsLogger? validationLogger) + public virtual IModel Initialize( + IModel model, + IDiagnosticsLogger? validationLogger) { if (model.SetModelDependencies(Dependencies.ModelDependencies)) { diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index 405f614e1fc..10b6bf9e5e0 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -1122,7 +1122,7 @@ protected virtual void LogShadowProperties( { if (property.IsImplicitlyCreated()) { - logger.ShadowPropertyCreated(property); + logger.ShadowPropertyCreated((IProperty)property); } } } diff --git a/src/EFCore/Infrastructure/NavigationBaseExtensions.cs b/src/EFCore/Infrastructure/NavigationBaseExtensions.cs deleted file mode 100644 index d80a68559fe..00000000000 --- a/src/EFCore/Infrastructure/NavigationBaseExtensions.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Linq; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Utilities; - -namespace Microsoft.EntityFrameworkCore.Infrastructure -{ - /// - /// - /// Extension methods for . - /// - /// - /// This type is typically used by database providers (and other extensions). It is generally - /// not used in application code. - /// - /// - public static class NavigationBaseExtensions - { - /// - /// Calls for a to mark it as loaded - /// when a no-tracking query has eagerly loaded this relationship. - /// - /// The navigation loaded. - /// The entity for which the navigation has been loaded. - public static void SetIsLoadedWhenNoTracking([NotNull] this INavigationBase navigation, [NotNull] object entity) - { - Check.NotNull(navigation, nameof(navigation)); - Check.NotNull(entity, nameof(entity)); - - var serviceProperties = navigation - .DeclaringEntityType - .GetDerivedTypesInclusive() - .Where(t => t.ClrType.IsInstanceOfType(entity)) - .SelectMany(e => e.GetServiceProperties()) - .Where(p => p.ClrType == typeof(ILazyLoader)); - - foreach (var serviceProperty in serviceProperties) - { - ((ILazyLoader)serviceProperty.GetGetter().GetClrValue(entity))?.SetLoaded(entity, navigation.Name); - } - } - } -} diff --git a/src/EFCore/Infrastructure/SingletonModelDependencies.cs b/src/EFCore/Infrastructure/SingletonModelDependencies.cs index aaafa61b2e3..8f8d73286bd 100644 --- a/src/EFCore/Infrastructure/SingletonModelDependencies.cs +++ b/src/EFCore/Infrastructure/SingletonModelDependencies.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure { /// /// - /// Service dependencies parameter class for + /// Service dependencies parameter class for /// /// /// This type is typically used by database providers (and other extensions). It is generally diff --git a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs index cfeb1929b97..2062d315fcd 100644 --- a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs @@ -601,7 +601,7 @@ IConventionEntityTypeBuilder RemoveUnusedShadowProperties( /// The same builder instance if the skip navigation was removed, /// otherwise. /// - IConventionEntityTypeBuilder? HasNoSkipNavigation([NotNull] ISkipNavigation skipNavigation, bool fromDataAnnotation = false); + IConventionEntityTypeBuilder? HasNoSkipNavigation([NotNull] IReadOnlySkipNavigation skipNavigation, bool fromDataAnnotation = false); /// /// Returns a value indicating whether the skip navigation can be removed from this entity type. @@ -609,7 +609,7 @@ IConventionEntityTypeBuilder RemoveUnusedShadowProperties( /// The skip navigation to be removed. /// Indicates whether the configuration was specified using a data annotation. /// if the skip navigation can be removed from this entity type. - bool CanRemoveSkipNavigation([NotNull] ISkipNavigation skipNavigation, bool fromDataAnnotation = false); + bool CanRemoveSkipNavigation([NotNull] IReadOnlySkipNavigation skipNavigation, bool fromDataAnnotation = false); /// /// Returns a value indicating whether the given navigation can be added to this entity type. diff --git a/src/EFCore/Metadata/Builders/NavigationBuilder.cs b/src/EFCore/Metadata/Builders/NavigationBuilder.cs index 4e052bf1cc0..7039303d872 100644 --- a/src/EFCore/Metadata/Builders/NavigationBuilder.cs +++ b/src/EFCore/Metadata/Builders/NavigationBuilder.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders { /// /// - /// Provides a simple API for configuring a or . + /// Provides a simple API for configuring a or . /// /// /// Instances of this class are returned from methods when using the API diff --git a/src/EFCore/Metadata/Builders/NavigationBuilder`.cs b/src/EFCore/Metadata/Builders/NavigationBuilder`.cs index 51f6d7d44f4..63103341a92 100644 --- a/src/EFCore/Metadata/Builders/NavigationBuilder`.cs +++ b/src/EFCore/Metadata/Builders/NavigationBuilder`.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Builders { /// /// - /// Provides a simple API for configuring a or . + /// Provides a simple API for configuring a or a . /// /// /// Instances of this class are returned from methods when using the API diff --git a/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs b/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs index cf986f31682..12183b86f4b 100644 --- a/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs +++ b/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs @@ -158,7 +158,7 @@ public virtual void ProcessModelFinalizing( } } - private static string FormatConstructorString(IEntityType entityType, InstantiationBinding binding) + private static string FormatConstructorString(IReadOnlyEntityType entityType, InstantiationBinding binding) => entityType.ClrType.ShortDisplayName() + "(" + string.Join(", ", binding.ParameterBindings.Select(b => b.ParameterType.ShortDisplayName())) diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs index 5a17850fc3d..9189a1e7687 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyPropertyDiscoveryConvention.cs @@ -872,7 +872,7 @@ public virtual void ProcessModelFinalizing( /// /// The foreign key. /// The string that should be used as part of the shadow properties created for the given foreign key. - public static string GetPropertyBaseName([NotNull] IForeignKey foreignKey) + public static string GetPropertyBaseName([NotNull] IReadOnlyForeignKey foreignKey) => foreignKey.DependentToPrincipal?.Name ?? foreignKey.GetReferencingSkipNavigations().FirstOrDefault()?.Inverse?.Name ?? foreignKey.PrincipalEntityType.ShortName(); diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs index 78d3f5129d0..0003db732a6 100644 --- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs +++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs @@ -66,18 +66,18 @@ public ImmediateConventionScope([NotNull] ConventionSet conventionSet, Conventio public override void Run(ConventionDispatcher dispatcher) => Check.DebugAssert(false, "Immediate convention scope cannot be run again."); - public IConventionModelBuilder? OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder) + public IConventionModelBuilder OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder) { _modelBuilderConventionContext.ResetState(modelBuilder); foreach (var modelConvention in _conventionSet.ModelFinalizingConventions) { - // Execute each convention in a separate batch so model validation will get an up-to-date model + // Execute each convention in a separate batch so each will get an up-to-date model as they are meant to be only run once using (_dispatcher.DelayConventions()) { modelConvention.ProcessModelFinalizing(modelBuilder, _modelBuilderConventionContext); if (_modelBuilderConventionContext.ShouldStopProcessing()) { - return _modelBuilderConventionContext.Result; + return _modelBuilderConventionContext.Result!; } } } @@ -85,7 +85,7 @@ public override void Run(ConventionDispatcher dispatcher) return modelBuilder; } - public IModel? OnModelFinalized([NotNull] IModel model) + public IModel OnModelFinalized([NotNull] IModel model) { foreach (var modelConvention in _conventionSet.ModelFinalizedConventions) { @@ -95,7 +95,7 @@ public override void Run(ConventionDispatcher dispatcher) return model; } - public IConventionModelBuilder? OnModelInitialized([NotNull] IConventionModelBuilder modelBuilder) + public IConventionModelBuilder OnModelInitialized([NotNull] IConventionModelBuilder modelBuilder) { using (_dispatcher.DelayConventions()) { @@ -105,7 +105,7 @@ public override void Run(ConventionDispatcher dispatcher) modelConvention.ProcessModelInitialized(modelBuilder, _modelBuilderConventionContext); if (_modelBuilderConventionContext.ShouldStopProcessing()) { - return _modelBuilderConventionContext.Result; + return _modelBuilderConventionContext.Result!; } } } diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs index d02a732acad..1871f56708f 100644 --- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs +++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs @@ -54,7 +54,7 @@ public ConventionDispatcher([NotNull] ConventionSet conventionSet) /// 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 IConventionModelBuilder? OnModelInitialized([NotNull] IConventionModelBuilder modelBuilder) + public virtual IConventionModelBuilder OnModelInitialized([NotNull] IConventionModelBuilder modelBuilder) => _immediateConventionScope.OnModelInitialized(modelBuilder); /// @@ -63,7 +63,7 @@ public ConventionDispatcher([NotNull] ConventionSet conventionSet) /// 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 IConventionModelBuilder? OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder) + public virtual IConventionModelBuilder OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder) => _immediateConventionScope.OnModelFinalizing(modelBuilder); /// @@ -72,7 +72,7 @@ public ConventionDispatcher([NotNull] ConventionSet conventionSet) /// 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 IModel? OnModelFinalized([NotNull] IModel model) + public virtual IModel OnModelFinalized([NotNull] IModel model) => _immediateConventionScope.OnModelFinalized(model); /// diff --git a/src/EFCore/Metadata/Conventions/InversePropertyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/InversePropertyAttributeConvention.cs index 9dfdffb62f4..f4b84219f7f 100644 --- a/src/EFCore/Metadata/Conventions/InversePropertyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/InversePropertyAttributeConvention.cs @@ -309,11 +309,14 @@ public override void ProcessEntityTypeRemoved( return; } - var leastDerivedEntityTypes = modelBuilder.Metadata.FindLeastDerivedEntityTypes( - declaringType, - t => !t.Builder.IsIgnored(navigationMemberInfo.GetSimpleMemberName(), fromDataAnnotation: true)); + var leastDerivedEntityTypes = modelBuilder.Metadata.FindLeastDerivedEntityTypes(declaringType); foreach (var leastDerivedEntityType in leastDerivedEntityTypes) { + if (leastDerivedEntityType.Builder.IsIgnored(navigationMemberInfo.GetSimpleMemberName(), fromDataAnnotation: true)) + { + continue; + } + Process(leastDerivedEntityType.Builder, navigationMemberInfo, targetClrType, attribute); } } diff --git a/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs b/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs index 970932ab64f..5ce3c93f3b7 100644 --- a/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs +++ b/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs @@ -61,7 +61,7 @@ public virtual void ProcessModelFinalizing( protected class DbSetAccessRewritingExpressionVisitor : ExpressionVisitor { private readonly Type _contextType; - private IModel? _model; + private IReadOnlyModel? _model; /// /// Creates a new instance of . @@ -77,7 +77,7 @@ public DbSetAccessRewritingExpressionVisitor(Type contextType) /// /// The model to look for entity types. /// The query expression to rewrite. - public Expression Rewrite(IModel model, Expression expression) + public Expression Rewrite(IReadOnlyModel model, Expression expression) { _model = model; @@ -121,7 +121,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp } private IEntityType? FindEntityType(Type dbSetType) - => _model!.FindRuntimeEntityType(dbSetType.GetGenericArguments()[0]); + => ((IModel)_model!).FindRuntimeEntityType(dbSetType.GetGenericArguments()[0]); } } } diff --git a/src/EFCore/Metadata/Conventions/TypeMappingConvention.cs b/src/EFCore/Metadata/Conventions/TypeMappingConvention.cs index 27bfda97277..cdf1830e19d 100644 --- a/src/EFCore/Metadata/Conventions/TypeMappingConvention.cs +++ b/src/EFCore/Metadata/Conventions/TypeMappingConvention.cs @@ -38,7 +38,7 @@ public virtual void ProcessModelFinalizing( { foreach (var property in modelBuilder.Metadata.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties())) { - property.Builder.HasTypeMapping(Dependencies.TypeMappingSource.FindMapping(property)); + property.Builder.HasTypeMapping(Dependencies.TypeMappingSource.FindMapping((IProperty)property)); } } } diff --git a/src/EFCore/Metadata/Conventions/ValueGenerationConvention.cs b/src/EFCore/Metadata/Conventions/ValueGenerationConvention.cs index 69e262f1fb2..2875e02ed6b 100644 --- a/src/EFCore/Metadata/Conventions/ValueGenerationConvention.cs +++ b/src/EFCore/Metadata/Conventions/ValueGenerationConvention.cs @@ -185,21 +185,21 @@ public virtual void ProcessEntityTypeBaseTypeChanged( /// The property. /// The store value generation strategy to set for the given property. protected virtual ValueGenerated? GetValueGenerated([NotNull] IConventionProperty property) - => GetValueGenerated((IProperty)property); + => GetValueGenerated((IReadOnlyProperty)property); /// /// Returns the store value generation strategy to set for the given property. /// /// The property. /// The store value generation strategy to set for the given property. - public static ValueGenerated? GetValueGenerated([NotNull] IProperty property) + public static ValueGenerated? GetValueGenerated([NotNull] IReadOnlyProperty property) => !property.GetContainingForeignKeys().Any(fk => !fk.IsBaseLinking()) && ShouldHaveGeneratedProperty(property.FindContainingPrimaryKey()) && CanBeGenerated(property) ? ValueGenerated.OnAdd : (ValueGenerated?)null; - private static bool ShouldHaveGeneratedProperty(IKey? key) + private static bool ShouldHaveGeneratedProperty(IReadOnlyKey? key) => key != null && key.DeclaringEntityType.IsOwned() is var onOwnedType && (onOwnedType && key.Properties.Count(p => !p.IsForeignKey()) == 1 @@ -211,7 +211,7 @@ private static bool ShouldHaveGeneratedProperty(IKey? key) /// /// The key property that might be store generated. /// A value indicating whether the specified property should have the value generated by the store. - private static bool CanBeGenerated(IProperty property) + private static bool CanBeGenerated(IReadOnlyProperty property) { var propertyType = property.ClrType.UnwrapNullableType(); return (propertyType.IsInteger() diff --git a/src/EFCore/Metadata/EntityTypeFullNameComparer.cs b/src/EFCore/Metadata/EntityTypeFullNameComparer.cs index 0919a88ca7d..d546fbdfffa 100644 --- a/src/EFCore/Metadata/EntityTypeFullNameComparer.cs +++ b/src/EFCore/Metadata/EntityTypeFullNameComparer.cs @@ -11,14 +11,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// An implementation of and to compare - /// instances by name including the defining entity type when present. + /// instances by name including the defining entity type when present. /// /// /// This type is typically used by database providers (and other extensions). It is generally /// not used in application code. /// /// - public sealed class EntityTypeFullNameComparer : IComparer, IEqualityComparer + public sealed class EntityTypeFullNameComparer : IComparer, IEqualityComparer { private EntityTypeFullNameComparer() { @@ -35,7 +35,7 @@ private EntityTypeFullNameComparer() /// The first object to compare. /// The second object to compare. /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. - public int Compare(IEntityType? x, IEntityType? y) + public int Compare(IReadOnlyEntityType? x, IReadOnlyEntityType? y) { if (ReferenceEquals(x, y)) { @@ -61,7 +61,7 @@ public int Compare(IEntityType? x, IEntityType? y) /// The first object to compare. /// The second object to compare. /// if the specified objects are equal; otherwise, . - public bool Equals(IEntityType? x, IEntityType? y) + public bool Equals(IReadOnlyEntityType? x, IReadOnlyEntityType? y) => Compare(x, y) == 0; /// @@ -69,7 +69,7 @@ public bool Equals(IEntityType? x, IEntityType? y) /// /// The for which a hash code is to be returned. /// A hash code for the specified object. - public int GetHashCode(IEntityType obj) + public int GetHashCode(IReadOnlyEntityType obj) => obj.Name.GetHashCode(); } } diff --git a/src/EFCore/Metadata/ForeignKeyComparer.cs b/src/EFCore/Metadata/ForeignKeyComparer.cs index 68c517c45c0..0281a067dbf 100644 --- a/src/EFCore/Metadata/ForeignKeyComparer.cs +++ b/src/EFCore/Metadata/ForeignKeyComparer.cs @@ -12,14 +12,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// An implementation of and to compare - /// instances. + /// instances. /// /// /// This type is typically used by database providers (and other extensions). It is generally /// not used in application code. /// /// - public sealed class ForeignKeyComparer : IEqualityComparer, IComparer + public sealed class ForeignKeyComparer : IEqualityComparer, IComparer { private ForeignKeyComparer() { @@ -36,7 +36,7 @@ private ForeignKeyComparer() /// The first object to compare. /// The second object to compare. /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. - public int Compare(IForeignKey? x, IForeignKey? y) + public int Compare(IReadOnlyForeignKey? x, IReadOnlyForeignKey? y) { var result = PropertyListComparer.Instance.Compare(x?.Properties, y?.Properties); if (result != 0) @@ -60,7 +60,7 @@ public int Compare(IForeignKey? x, IForeignKey? y) /// The first object to compare. /// The second object to compare. /// if the specified objects are equal; otherwise, . - public bool Equals(IForeignKey? x, IForeignKey? y) + public bool Equals(IReadOnlyForeignKey? x, IReadOnlyForeignKey? y) => Compare(x, y) == 0; /// @@ -68,7 +68,7 @@ public bool Equals(IForeignKey? x, IForeignKey? y) /// /// The for which a hash code is to be returned. /// A hash code for the specified object. - public int GetHashCode(IForeignKey obj) + public int GetHashCode(IReadOnlyForeignKey obj) { var hashCode = new HashCode(); hashCode.Add(obj.PrincipalKey.Properties, PropertyListComparer.Instance); diff --git a/src/EFCore/Metadata/IConstructorBindingFactory.cs b/src/EFCore/Metadata/IConstructorBindingFactory.cs index d6a70ac3fd1..9e3fbfde1ca 100644 --- a/src/EFCore/Metadata/IConstructorBindingFactory.cs +++ b/src/EFCore/Metadata/IConstructorBindingFactory.cs @@ -25,7 +25,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata public interface IConstructorBindingFactory { /// - /// Attempts to create a for the given and + /// Attempts to create a for the given entity type and /// /// /// The entity type. @@ -40,7 +40,7 @@ bool TryBindConstructor( [CanBeNull, CA.NotNullWhen(false)] out IEnumerable? unboundParameters); /// - /// Attempts to create a for the given and + /// Attempts to create a for the given entity type and /// /// /// The entity type. diff --git a/src/EFCore/Metadata/IConventionAnnotatable.cs b/src/EFCore/Metadata/IConventionAnnotatable.cs index 7d9bf1853a0..91d0c5f5d98 100644 --- a/src/EFCore/Metadata/IConventionAnnotatable.cs +++ b/src/EFCore/Metadata/IConventionAnnotatable.cs @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// not used in application code. /// /// - public interface IConventionAnnotatable : IAnnotatable + public interface IConventionAnnotatable : IReadOnlyAnnotatable { /// /// Gets the builder that can be used to configure this object. diff --git a/src/EFCore/Metadata/IConventionEntityType.cs b/src/EFCore/Metadata/IConventionEntityType.cs index e9ec3de6160..e1f9f07bf48 100644 --- a/src/EFCore/Metadata/IConventionEntityType.cs +++ b/src/EFCore/Metadata/IConventionEntityType.cs @@ -14,14 +14,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// Represents an entity in an . + /// Represents an entity type in an . /// /// /// This interface is used during model creation and allows the metadata to be modified. /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionEntityType : IEntityType, IConventionTypeBase + public interface IConventionEntityType : IReadOnlyEntityType, IConventionTypeBase { /// /// Gets the configuration source for this entity type. @@ -152,7 +152,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// /// The properties that make up the key. /// The key, or if none is defined. - new IConventionKey? FindKey([NotNull] IReadOnlyList properties); + new IConventionKey? FindKey([NotNull] IReadOnlyList properties); /// /// Gets the primary and alternate keys for this entity type. @@ -202,9 +202,9 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// /// The foreign key, or if none is defined. new IConventionForeignKey? FindForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType); + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType); /// /// Gets the foreign keys defined on this entity type. @@ -252,7 +252,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// The navigation property on the entity class. /// The navigation property, or if none is found. new IConventionSkipNavigation? FindSkipNavigation([NotNull] MemberInfo memberInfo) - => (IConventionSkipNavigation?)((IEntityType)this).FindSkipNavigation(memberInfo); + => (IConventionSkipNavigation?)((IReadOnlyEntityType)this).FindSkipNavigation(memberInfo); /// /// Gets a skip navigation property on this entity type. Returns if no skip navigation property is found. @@ -268,7 +268,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. new IConventionSkipNavigation? FindDeclaredSkipNavigation([NotNull] string name) - => (IConventionSkipNavigation?)((IEntityType)this).FindDeclaredSkipNavigation(name); + => (IConventionSkipNavigation?)((IReadOnlyEntityType)this).FindDeclaredSkipNavigation(name); /// /// @@ -282,7 +282,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// /// Declared foreign keys. new IEnumerable GetDeclaredSkipNavigations() - => ((IEntityType)this).GetDeclaredSkipNavigations().Cast(); + => ((IReadOnlyEntityType)this).GetDeclaredSkipNavigations().Cast(); /// /// Gets all skip navigation properties on this entity type. @@ -329,7 +329,7 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false) /// /// The properties to find the index on. /// The index, or if none is found. - new IConventionIndex? FindIndex([NotNull] IReadOnlyList properties); + new IConventionIndex? FindIndex([NotNull] IReadOnlyList properties); /// /// Gets the index with the given name. Returns if no such index exists. diff --git a/src/EFCore/Metadata/IConventionForeignKey.cs b/src/EFCore/Metadata/IConventionForeignKey.cs index fb6ca4257b2..52bd964f0be 100644 --- a/src/EFCore/Metadata/IConventionForeignKey.cs +++ b/src/EFCore/Metadata/IConventionForeignKey.cs @@ -22,7 +22,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionForeignKey : IForeignKey, IConventionAnnotatable + public interface IConventionForeignKey : IReadOnlyForeignKey, IConventionAnnotatable { /// /// Gets the builder that can be used to configure this foreign key. @@ -83,21 +83,21 @@ IReadOnlyList SetProperties( bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetPropertiesConfigurationSource(); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetPrincipalKeyConfigurationSource(); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetPrincipalEndConfigurationSource(); /// @@ -109,9 +109,9 @@ IReadOnlyList SetProperties( bool? SetIsUnique(bool? unique, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsUniqueConfigurationSource(); /// @@ -124,9 +124,9 @@ IReadOnlyList SetProperties( bool? SetIsRequired(bool? required, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsRequiredConfigurationSource(); /// @@ -139,9 +139,9 @@ IReadOnlyList SetProperties( bool? SetIsRequiredDependent(bool? required, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsRequiredDependentConfigurationSource(); /// @@ -154,9 +154,9 @@ IReadOnlyList SetProperties( bool? SetIsOwnership(bool? ownership, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsOwnershipConfigurationSource(); /// @@ -172,9 +172,9 @@ IReadOnlyList SetProperties( DeleteBehavior? SetDeleteBehavior(DeleteBehavior? deleteBehavior, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetDeleteBehaviorConfigurationSource(); /// @@ -226,9 +226,9 @@ IReadOnlyList SetProperties( => SetDependentToPrincipal(property, fromDataAnnotation); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetDependentToPrincipalConfigurationSource(); /// @@ -280,9 +280,9 @@ IReadOnlyList SetProperties( => SetPrincipalToDependent(property, fromDataAnnotation); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetPrincipalToDependentConfigurationSource(); /// @@ -290,6 +290,6 @@ IReadOnlyList SetProperties( /// /// The skip navigations using this foreign key. new IEnumerable GetReferencingSkipNavigations() - => ((IForeignKey)this).GetReferencingSkipNavigations().Cast(); + => ((IReadOnlyForeignKey)this).GetReferencingSkipNavigations().Cast(); } } diff --git a/src/EFCore/Metadata/IConventionIndex.cs b/src/EFCore/Metadata/IConventionIndex.cs index a5babbab0d7..7c9a5c8cd1c 100644 --- a/src/EFCore/Metadata/IConventionIndex.cs +++ b/src/EFCore/Metadata/IConventionIndex.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionIndex : IIndex, IConventionAnnotatable + public interface IConventionIndex : IReadOnlyIndex, IConventionAnnotatable { /// /// Gets the builder that can be used to configure this index. @@ -53,9 +53,9 @@ public interface IConventionIndex : IIndex, IConventionAnnotatable bool? SetIsUnique(bool? unique, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsUniqueConfigurationSource(); } } diff --git a/src/EFCore/Metadata/IConventionKey.cs b/src/EFCore/Metadata/IConventionKey.cs index bd99d40e537..c87adc053c5 100644 --- a/src/EFCore/Metadata/IConventionKey.cs +++ b/src/EFCore/Metadata/IConventionKey.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionKey : IConventionAnnotatable, IKey + public interface IConventionKey : IReadOnlyKey, IConventionAnnotatable { /// /// Gets the builder that can be used to configure this key. diff --git a/src/EFCore/Metadata/IConventionModel.cs b/src/EFCore/Metadata/IConventionModel.cs index b7a1943edc7..784289a92e8 100644 --- a/src/EFCore/Metadata/IConventionModel.cs +++ b/src/EFCore/Metadata/IConventionModel.cs @@ -23,7 +23,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionModel : IModel, IConventionAnnotatable + public interface IConventionModel : IReadOnlyModel, IConventionAnnotatable { /// /// Gets the builder that can be used to configure this model. @@ -113,7 +113,7 @@ public interface IConventionModel : IModel, IConventionAnnotatable /// or the entity type has a defining navigation. /// /// The name of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. new IConventionEntityType? FindEntityType([NotNull] string name); /// @@ -123,7 +123,7 @@ public interface IConventionModel : IModel, IConventionAnnotatable /// The name of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. IConventionEntityType? FindEntityType( [NotNull] string name, [NotNull] string definingNavigationName, diff --git a/src/EFCore/Metadata/IConventionNavigation.cs b/src/EFCore/Metadata/IConventionNavigation.cs index d759ba0587c..3a5f6e69df5 100644 --- a/src/EFCore/Metadata/IConventionNavigation.cs +++ b/src/EFCore/Metadata/IConventionNavigation.cs @@ -20,7 +20,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionNavigation : INavigation, IConventionNavigationBase + public interface IConventionNavigation : IReadOnlyNavigation, IConventionNavigationBase { /// /// Gets the builder that can be used to configure this navigation. @@ -34,7 +34,7 @@ public interface IConventionNavigation : INavigation, IConventionNavigationBase new IConventionEntityType DeclaringEntityType { [DebuggerStepThrough] - get => (IConventionEntityType)((INavigationBase)this).DeclaringEntityType; + get => (IConventionEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; } /// @@ -43,7 +43,7 @@ public interface IConventionNavigation : INavigation, IConventionNavigationBase new IConventionEntityType TargetEntityType { [DebuggerStepThrough] - get => (IConventionEntityType)((INavigationBase)this).TargetEntityType; + get => (IConventionEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// @@ -61,7 +61,7 @@ ConfigurationSource IConventionPropertyBase.GetConfigurationSource() new IConventionForeignKey ForeignKey { [DebuggerStepThrough] - get => (IConventionForeignKey)((INavigation)this).ForeignKey; + get => (IConventionForeignKey)((IReadOnlyNavigation)this).ForeignKey; } /// @@ -70,7 +70,7 @@ ConfigurationSource IConventionPropertyBase.GetConfigurationSource() new IConventionNavigation? Inverse { [DebuggerStepThrough] - get => (IConventionNavigation?)((INavigation)this).Inverse; + get => (IConventionNavigation?)((IReadOnlyNavigation)this).Inverse; } /// diff --git a/src/EFCore/Metadata/IConventionNavigationBase.cs b/src/EFCore/Metadata/IConventionNavigationBase.cs index a6d856274fb..bfb0a14270b 100644 --- a/src/EFCore/Metadata/IConventionNavigationBase.cs +++ b/src/EFCore/Metadata/IConventionNavigationBase.cs @@ -13,10 +13,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionNavigationBase : INavigationBase, IConventionPropertyBase + public interface IConventionNavigationBase : IReadOnlyNavigationBase, IConventionPropertyBase { /// /// Sets a value indicating whether this navigation should be eager loaded by default. diff --git a/src/EFCore/Metadata/IConventionProperty.cs b/src/EFCore/Metadata/IConventionProperty.cs index 6ad6c5e3753..07058e7730b 100644 --- a/src/EFCore/Metadata/IConventionProperty.cs +++ b/src/EFCore/Metadata/IConventionProperty.cs @@ -10,14 +10,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// Represents a scalar property of an entity. + /// Represents a scalar property of an entity type. /// /// /// This interface is used during model creation and allows the metadata to be modified. /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionProperty : IProperty, IConventionPropertyBase + public interface IConventionProperty : IReadOnlyProperty, IConventionPropertyBase { /// /// Gets the builder that can be used to configure this property. @@ -31,9 +31,9 @@ public interface IConventionProperty : IProperty, IConventionPropertyBase new IConventionEntityType DeclaringEntityType { get; } /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetTypeConfigurationSource(); /// @@ -48,9 +48,9 @@ public interface IConventionProperty : IProperty, IConventionPropertyBase bool? SetIsNullable(bool? nullable, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsNullableConfigurationSource(); /// @@ -69,9 +69,9 @@ public interface IConventionProperty : IProperty, IConventionPropertyBase ValueGenerated? SetValueGenerated(ValueGenerated? valueGenerated, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetValueGeneratedConfigurationSource(); /// @@ -90,9 +90,9 @@ public interface IConventionProperty : IProperty, IConventionPropertyBase bool? SetIsConcurrencyToken(bool? concurrencyToken, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetIsConcurrencyTokenConfigurationSource(); /// diff --git a/src/EFCore/Metadata/IConventionPropertyBase.cs b/src/EFCore/Metadata/IConventionPropertyBase.cs index 96eacc7fa8a..ca730594b22 100644 --- a/src/EFCore/Metadata/IConventionPropertyBase.cs +++ b/src/EFCore/Metadata/IConventionPropertyBase.cs @@ -16,10 +16,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionPropertyBase : IPropertyBase, IConventionAnnotatable + public interface IConventionPropertyBase : IReadOnlyPropertyBase, IConventionAnnotatable { /// /// Gets the type that this property belongs to. @@ -91,9 +91,9 @@ void SetField([CanBeNull] FieldInfo? fieldInfo, bool fromDataAnnotation = false) .SetField(fieldName, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetFieldInfoConfigurationSource(); } } diff --git a/src/EFCore/Metadata/IConventionServiceProperty.cs b/src/EFCore/Metadata/IConventionServiceProperty.cs index 18d839d004a..38a66dc9b42 100644 --- a/src/EFCore/Metadata/IConventionServiceProperty.cs +++ b/src/EFCore/Metadata/IConventionServiceProperty.cs @@ -11,7 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// A in the Entity Framework model that represents an + /// A in the Entity Framework model that represents an /// injected service from the . /// /// @@ -19,7 +19,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionServiceProperty : IServiceProperty, IConventionPropertyBase + public interface IConventionServiceProperty : IReadOnlyServiceProperty, IConventionPropertyBase { /// /// Gets the builder that can be used to configure this service property. @@ -41,9 +41,9 @@ public interface IConventionServiceProperty : IServiceProperty, IConventionPrope ServiceParameterBinding? SetParameterBinding([CanBeNull] ServiceParameterBinding? parameterBinding, bool fromDataAnnotation = false); /// - /// Returns the configuration source for . + /// Returns the configuration source for . /// - /// The configuration source for . + /// The configuration source for . ConfigurationSource? GetParameterBindingConfigurationSource(); } } diff --git a/src/EFCore/Metadata/IConventionSkipNavigation.cs b/src/EFCore/Metadata/IConventionSkipNavigation.cs index d7a154304e1..e08e1b2b1e0 100644 --- a/src/EFCore/Metadata/IConventionSkipNavigation.cs +++ b/src/EFCore/Metadata/IConventionSkipNavigation.cs @@ -20,7 +20,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigationBase + public interface IConventionSkipNavigation : IReadOnlySkipNavigation, IConventionNavigationBase { /// /// Gets the builder that can be used to configure this property. @@ -34,7 +34,7 @@ public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigat new IConventionEntityType DeclaringEntityType { [DebuggerStepThrough] - get => (IConventionEntityType)((INavigationBase)this).DeclaringEntityType; + get => (IConventionEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; } /// @@ -43,7 +43,7 @@ public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigat new IConventionEntityType TargetEntityType { [DebuggerStepThrough] - get => (IConventionEntityType)((INavigationBase)this).TargetEntityType; + get => (IConventionEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// @@ -52,7 +52,7 @@ public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigat new IConventionEntityType? JoinEntityType { [DebuggerStepThrough] - get => (IConventionEntityType?)((ISkipNavigation)this).JoinEntityType; + get => (IConventionEntityType?)((IReadOnlySkipNavigation)this).JoinEntityType; } /// @@ -61,7 +61,7 @@ public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigat new IConventionForeignKey? ForeignKey { [DebuggerStepThrough] - get => (IConventionForeignKey?)((ISkipNavigation)this).ForeignKey; + get => (IConventionForeignKey?)((IReadOnlySkipNavigation)this).ForeignKey; } /// @@ -86,7 +86,7 @@ public interface IConventionSkipNavigation : ISkipNavigation, IConventionNavigat new IConventionSkipNavigation? Inverse { [DebuggerStepThrough] - get => (IConventionSkipNavigation?)((ISkipNavigation)this).Inverse; + get => (IConventionSkipNavigation?)((IReadOnlySkipNavigation)this).Inverse; } /// diff --git a/src/EFCore/Metadata/IConventionTypeBase.cs b/src/EFCore/Metadata/IConventionTypeBase.cs index 1aa7923328c..0daca1a9c12 100644 --- a/src/EFCore/Metadata/IConventionTypeBase.cs +++ b/src/EFCore/Metadata/IConventionTypeBase.cs @@ -14,10 +14,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IConventionTypeBase : ITypeBase, IConventionAnnotatable + public interface IConventionTypeBase : IReadOnlyTypeBase, IConventionAnnotatable { /// /// Gets the model that this type belongs to. diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs index dd85398ed9d..ea11a369fde 100644 --- a/src/EFCore/Metadata/IEntityType.cs +++ b/src/EFCore/Metadata/IEntityType.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -13,39 +12,21 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents an entity type in an . + /// Represents an entity type in a model. /// - public interface IEntityType : ITypeBase + public interface IEntityType : IReadOnlyEntityType, ITypeBase { /// - /// 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. /// - IEntityType? BaseType { get; } - - /// - /// Gets the name of the defining navigation. - /// - [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] - string? DefiningNavigationName => null; - - /// - /// Gets the defining entity type. - /// - [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] - IEntityType? DefiningEntityType => null; - - /// - /// Gets a value indicating whether this entity type has a defining navigation. - /// - /// if this entity type has a defining navigation. - [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] - public bool HasDefiningNavigation() => HasSharedClrType; + new IEntityType? BaseType { get; } /// /// Gets primary key for this entity type. Returns if no primary key is defined. /// /// The primary key, or if none is defined. - IKey? FindPrimaryKey(); + new IKey? FindPrimaryKey(); /// /// Gets the primary or alternate key that is defined on the given properties. @@ -53,13 +34,13 @@ public interface IEntityType : ITypeBase /// /// The properties that make up the key. /// The key, or if none is defined. - IKey? FindKey([NotNull] IReadOnlyList properties); + new IKey? FindKey([NotNull] IReadOnlyList properties); /// /// Gets the primary and alternate keys for this entity type. /// /// The primary and alternate keys. - IEnumerable GetKeys(); + new IEnumerable GetKeys(); /// /// Gets the foreign key for the given properties that points to a given primary or alternate key. @@ -73,48 +54,40 @@ public interface IEntityType : ITypeBase /// base type of the hierarchy). /// /// The foreign key, or if none is defined. - IForeignKey? FindForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType); + new IForeignKey? FindForeignKey( + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType); /// /// Gets the foreign keys defined on this entity type. /// /// The foreign keys defined on this entity type. - IEnumerable GetForeignKeys(); + new IEnumerable GetForeignKeys(); /// /// Gets a skip navigation property on this entity type. Returns if no navigation property is found. /// /// The navigation property on the entity class. /// The navigation property, or if none is found. - ISkipNavigation? FindSkipNavigation([NotNull] MemberInfo memberInfo) - => FindSkipNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); + new ISkipNavigation? FindSkipNavigation([NotNull] MemberInfo memberInfo) + => (ISkipNavigation?)((IReadOnlyEntityType)this).FindSkipNavigation(memberInfo); /// /// Gets a skip navigation property on this entity type. Returns if no skip navigation property is found. /// /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. - ISkipNavigation? FindSkipNavigation([NotNull] string name); + new ISkipNavigation? FindSkipNavigation([NotNull] string name); /// - /// - /// Gets a skip navigation property on this entity type. - /// - /// - /// Does not return skip navigation properties defined on a base type. - /// Returns if no skip navigation property is found. - /// + /// Gets a skip navigation property on this entity type. Does not return skip navigation properties defined on a base type. + /// Returns if no skip navigation property is found. /// /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. - ISkipNavigation? FindDeclaredSkipNavigation([NotNull] string name) - { - var navigation = FindSkipNavigation(name); - return navigation?.DeclaringEntityType == this ? navigation : null; - } + new ISkipNavigation? FindDeclaredSkipNavigation([NotNull] string name) + => (ISkipNavigation?)((IReadOnlyEntityType)this).FindDeclaredSkipNavigation(name); /// /// @@ -127,14 +100,14 @@ public interface IEntityType : ITypeBase /// /// /// Declared foreign keys. - IEnumerable GetDeclaredSkipNavigations() - => GetSkipNavigations().Where(n => n.DeclaringEntityType == this); + new IEnumerable GetDeclaredSkipNavigations() + => ((IReadOnlyEntityType)this).GetDeclaredSkipNavigations().Cast(); /// /// Gets the skip navigation properties on this entity type. /// - /// All skip navigation properties on this entity type. - IEnumerable GetSkipNavigations(); + /// The skip navigation properties on this entity type. + new IEnumerable GetSkipNavigations(); /// /// @@ -146,20 +119,20 @@ IEnumerable GetDeclaredSkipNavigations() /// /// The properties to find the index on. /// The index, or if none is found. - IIndex? FindIndex([NotNull] IReadOnlyList properties); + new IIndex? FindIndex([NotNull] IReadOnlyList properties); /// /// Gets the index with the given name. Returns if no such index exists. /// - /// The name of the index to find. + /// The name of the index. /// The index, or if none is found. - IIndex? FindIndex([NotNull] string name); + new IIndex? FindIndex([NotNull] string name); /// /// Gets the indexes defined on this entity type. /// /// The indexes defined on this entity type. - IEnumerable GetIndexes(); + new IEnumerable GetIndexes(); /// /// @@ -167,12 +140,12 @@ IEnumerable GetDeclaredSkipNavigations() /// /// /// This API only finds scalar properties and does not find navigation properties. Use - /// to find a navigation property. + /// to find a navigation property. /// /// /// The name of the property. /// The property, or if none is found. - IProperty? FindProperty([NotNull] string name); + new IProperty? FindProperty([NotNull] string name); /// /// @@ -180,11 +153,24 @@ IEnumerable GetDeclaredSkipNavigations() /// /// /// This API only returns scalar properties and does not return navigation properties. Use - /// to get navigation properties. + /// to get navigation properties. /// /// /// The properties defined on this entity type. - IEnumerable GetProperties(); + new IEnumerable GetProperties(); + + /// + /// Returns the properties contained in foreign keys. + /// + /// The properties contained in foreign keys. + IEnumerable GetForeignKeyProperties(); + + /// + /// Returns the properties that need a value to be generated when the entity entry transitions to the + /// state. + /// + /// The properties that need a value to be generated on add. + IEnumerable GetValueGeneratingProperties(); /// /// @@ -197,7 +183,7 @@ IEnumerable GetDeclaredSkipNavigations() /// /// The name of the property. /// The service property, or if none is found. - IServiceProperty? FindServiceProperty([NotNull] string name); + new IServiceProperty? FindServiceProperty([NotNull] string name); /// /// @@ -208,6 +194,331 @@ IEnumerable GetDeclaredSkipNavigations() /// /// /// The service properties defined on this entity type. - IEnumerable GetServiceProperties(); + new IEnumerable GetServiceProperties(); + + /// + /// Returns all the derived types of the given , including the type itself, + /// which are not . + /// + /// Non-abstract, derived types. + IEnumerable GetConcreteDerivedTypesInclusive() + => ((IReadOnlyEntityType)this).GetConcreteDerivedTypesInclusive().Cast(); + + /// + /// Returns the that will be used for storing a discriminator value. + /// + IProperty? GetDiscriminatorProperty() + => (IProperty?)((IReadOnlyEntityType)this).GetDiscriminatorProperty(); + + /// + /// Gets the root base type for a given entity type. + /// + /// + /// The root base type. If the given entity type is not a derived type, then the same entity type is returned. + /// + IEntityType GetRootType() + => (IEntityType)((IReadOnlyEntityType)this).GetRootType(); + + /// + /// Gets all types in the model from which a given entity type derives, starting with the root. + /// + /// The base types. + IEnumerable GetAllBaseTypes() + => GetAllBaseTypesAscending().Reverse(); + + /// + /// Returns all base types of the given entity type, including the type itself, top to bottom. + /// + /// The base types. + IEnumerable GetAllBaseTypesInclusive() + => GetAllBaseTypesInclusiveAscending().Reverse(); + + /// + /// Gets all types in the model from which a given entity type derives, starting with the closest one. + /// + /// The base types. + IEnumerable GetAllBaseTypesAscending() + => GetAllBaseTypesInclusiveAscending().Skip(1); + + /// + /// Returns all base types of the given entity type, including the type itself, bottom to top. + /// + /// The base types. + IEnumerable GetAllBaseTypesInclusiveAscending() + => ((IReadOnlyEntityType)this).GetAllBaseTypesInclusiveAscending().Cast(); + + /// + /// Gets all types in the model that derive from a given entity type. + /// + /// The derived types. + IEnumerable GetDerivedTypes() + => ((IReadOnlyEntityType)this).GetDerivedTypes().Cast(); + + /// + /// Returns all derived types of the given , including the type itself. + /// + /// Derived types. + IEnumerable GetDerivedTypesInclusive() + => ((IReadOnlyEntityType)this).GetDerivedTypesInclusive().Cast(); + + /// + /// Gets all types in the model that directly derive from a given entity type. + /// + /// The derived types. + IEnumerable GetDirectlyDerivedTypes(); + + /// + /// + /// Gets all keys declared on the given . + /// + /// + /// This method does not return keys declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same key more than once. + /// Use to also return keys declared on base types. + /// + /// + /// Declared keys. + IEnumerable GetDeclaredKeys(); + + /// + /// Gets the primary or alternate key that is defined on the given property. Returns if no key is defined + /// for the given property. + /// /// + /// The property that the key is defined on. + /// The key, or null if none is defined. + IKey? FindKey([NotNull] IReadOnlyProperty property) => FindKey(new[] { property }); + + /// + /// + /// Gets all non-navigation properties declared on the given . + /// + /// + /// This method does not return properties declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same property more than once. + /// Use to also return properties declared on base types. + /// + /// + /// Declared non-navigation properties. + IEnumerable GetDeclaredProperties(); + + /// + /// + /// Gets all navigation properties declared on the given . + /// + /// + /// This method does not return navigation properties declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same navigation property more than once. + /// Use to also return navigation properties declared on base types. + /// + /// + /// Declared navigation properties. + IEnumerable GetDeclaredNavigations(); + + /// + /// + /// Gets all service properties declared on the given . + /// + /// + /// This method does not return properties declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same property more than once. + /// Use to also return properties declared on base types. + /// + /// + /// Declared service properties. + IEnumerable GetDeclaredServiceProperties(); + + /// + /// + /// Gets all indexes declared on the given . + /// + /// + /// This method does not return indexes declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same index more than once. + /// Use to also return indexes declared on base types. + /// + /// + /// Declared indexes. + IEnumerable GetDeclaredIndexes(); + + /// + /// + /// Gets all indexes declared on the types derived from the given . + /// + /// + /// Derived indexes. + IEnumerable GetDerivedIndexes(); + + /// + /// + /// Gets all foreign keys declared on the given . + /// + /// + /// This method does not return foreign keys declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once. + /// Use to also return foreign keys declared on base types. + /// + /// + /// Declared foreign keys. + IEnumerable GetDeclaredForeignKeys(); + + /// + /// + /// Gets all foreign keys declared on the types derived from the given . + /// + /// + /// Derived foreign keys. + IEnumerable GetDerivedForeignKeys(); + + /// + /// Gets the foreign keys defined on the given property. Only foreign keys that are defined on exactly the specified + /// property are returned. Composite foreign keys that include the specified property are not returned. + /// + /// The property to find the foreign keys on. + /// The foreign keys. + IEnumerable FindForeignKeys([NotNull] IReadOnlyProperty property) + => FindForeignKeys(new[] { property }); + + /// + /// Gets the foreign keys defined on the given properties. Only foreign keys that are defined on exactly the specified + /// set of properties are returned. + /// + /// The properties to find the foreign keys on. + /// The foreign keys. + IEnumerable FindForeignKeys([NotNull] IReadOnlyList properties); + + /// + /// Gets the foreign key for the given properties that points to a given primary or alternate key. Returns + /// if no foreign key is found. + /// + /// The property that the foreign key is defined on. + /// The primary or alternate key that is referenced. + /// + /// The entity type that the relationship targets. This may be different from the type that + /// is defined on when the relationship targets a derived type in an inheritance hierarchy (since the key is defined on the + /// base type of the hierarchy). + /// + /// The foreign key, or if none is defined. + IForeignKey? FindForeignKey( + [NotNull] IReadOnlyProperty property, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) + => FindForeignKey(new[] { property }, principalKey, principalEntityType); + + /// + /// Gets the foreign keys declared on the given using the given properties. + /// + /// The properties to find the foreign keys on. + /// Declared foreign keys. + IEnumerable FindDeclaredForeignKeys([NotNull] IReadOnlyList properties); + + /// + /// Gets all foreign keys that target a given entity type (i.e. foreign keys where the given entity type + /// or a type it's derived from is the principal). + /// + /// The foreign keys that reference the given entity type. + IEnumerable GetReferencingForeignKeys(); + + /// + /// Gets all foreign keys that target a given entity type (i.e. foreign keys where the given entity type + /// is the principal). + /// + /// The foreign keys that reference the given entity type. + IEnumerable GetDeclaredReferencingForeignKeys(); + + /// + /// Returns the relationship to the owner if this is an owned type or otherwise. + /// + /// The relationship to the owner if this is an owned type or otherwise. + IForeignKey? FindOwnership() + => (IForeignKey?)((IReadOnlyEntityType)this).FindOwnership(); + + /// + /// Gets a navigation property on the given entity type. Returns if no navigation property is found. + /// + /// The navigation property on the entity class. + /// The navigation property, or if none is found. + INavigation? FindNavigation([NotNull] MemberInfo memberInfo) + => FindNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); + + /// + /// Gets a navigation property on the given entity type. Returns if no navigation property is found. + /// + /// The name of the navigation property on the entity class. + /// The navigation property, or if none is found. + INavigation? FindNavigation([NotNull] string name) + => (INavigation?)((IReadOnlyEntityType)this).FindNavigation(name); + + /// + /// Gets a navigation property on the given entity type. Does not return navigation properties defined on a base type. + /// Returns if no navigation property is found. + /// + /// The name of the navigation property on the entity class. + /// The navigation property, or if none is found. + INavigation? FindDeclaredNavigation([NotNull] string name); + + /// + /// Gets all navigation properties on the given entity type. + /// + /// All navigation properties on the given entity type. + IEnumerable GetNavigations() + => ((IReadOnlyEntityType)this).GetNavigations().Cast(); + + /// + /// + /// Gets a property on the given entity type. Returns if no property is found. + /// + /// + /// This API only finds scalar properties and does not find navigation properties. Use + /// to find a navigation property. + /// + /// + /// The property on the entity class. + /// The property, or if none is found. + IProperty? FindProperty([NotNull] MemberInfo memberInfo) + => (IProperty?)((IReadOnlyEntityType)this).FindProperty(memberInfo); + + /// + /// + /// 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. + /// + /// + /// The property names. + /// The properties, or if any property is not found. + IReadOnlyList? FindProperties( + [NotNull] IReadOnlyList propertyNames) + => (IReadOnlyList?)((IReadOnlyEntityType)this).FindProperties(propertyNames); + + /// + /// + /// Gets a property with the given name. + /// + /// + /// This API only finds scalar properties and does not find navigation properties. Use + /// to find a navigation property. + /// + /// + /// The property name. + /// The property, or if none is found. + IProperty GetProperty([NotNull] string name) + => (IProperty)((IReadOnlyEntityType)this).GetProperty(name); + + /// + /// Finds a property declared on the type with the given name. + /// Does not return properties defined on a base type. + /// + /// The property name. + /// The property, or if none is found. + IProperty? FindDeclaredProperty([NotNull] string name); + + /// + /// Gets the index defined on the given property. Returns null if no index is defined. + /// + /// The property to find the index on. + /// The index, or null if none is found. + IIndex? FindIndex([NotNull] IReadOnlyProperty property) + => FindIndex(new[] { property }); } } diff --git a/src/EFCore/Metadata/IForeignKey.cs b/src/EFCore/Metadata/IForeignKey.cs index 9db25bc2cc8..23694968eeb 100644 --- a/src/EFCore/Metadata/IForeignKey.cs +++ b/src/EFCore/Metadata/IForeignKey.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; #nullable enable @@ -10,80 +11,70 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a relationship where a foreign key property(s) in a dependent entity type - /// reference a corresponding primary or alternate key in a principal entity type. + /// Represents a relationship where a foreign key composed of properties on the dependent entity type + /// references a corresponding primary or alternate key on the principal entity type. /// - public interface IForeignKey : IAnnotatable + public interface IForeignKey : IReadOnlyForeignKey, IAnnotatable { - /// - /// Gets the dependent entity type. This may be different from the type that - /// are defined on when the relationship is defined a derived type in an inheritance hierarchy (since the properties - /// may be defined on a base type). - /// - IEntityType DeclaringEntityType { get; } - /// /// Gets the foreign key properties in the dependent entity. /// - IReadOnlyList Properties { get; } - - /// - /// Gets the principal entity type that this relationship targets. This may be different from the type that - /// is defined on when the relationship targets a derived type in an inheritance - /// hierarchy (since the key is defined on the base type of the hierarchy). - /// - IEntityType PrincipalEntityType { get; } + new IReadOnlyList Properties { get; } /// /// Gets the primary or alternate key that the relationship targets. /// - IKey PrincipalKey { get; } + new IKey PrincipalKey { get; } /// - /// Gets the navigation property on the dependent entity type that points to the principal entity. - /// - INavigation? DependentToPrincipal { get; } - - /// - /// Gets the navigation property on the principal entity type that points to the dependent entity. + /// Gets the dependent entity type. This may be different from the type that + /// are defined on when the relationship is defined a derived type in an inheritance hierarchy (since the properties + /// may be defined on a base type). /// - INavigation? PrincipalToDependent { get; } + new IEntityType DeclaringEntityType { get; } /// - /// Gets a value indicating whether the values assigned to the foreign key properties are unique. + /// Gets the principal entity type that this relationship targets. This may be different from the type that + /// is defined on when the relationship targets a derived type in an inheritance + /// hierarchy (since the key is defined on the base type of the hierarchy). /// - bool IsUnique { get; } + new IEntityType PrincipalEntityType { get; } /// - /// Gets a value indicating whether the principal entity is required. - /// If , the dependent entity must always be assigned to a valid principal entity. + /// Gets the navigation property on the dependent entity type that points to the principal entity. /// - bool IsRequired { get; } + new INavigation? DependentToPrincipal { get; } /// - /// Gets a value indicating whether the dependent entity is required. - /// If , the principal entity must always have a valid dependent entity assigned. + /// Gets the navigation property on the principal entity type that points to the dependent entity. /// - bool IsRequiredDependent { get; } + new INavigation? PrincipalToDependent { get; } /// - /// Gets or sets a value indicating whether this relationship defines an ownership. - /// If , the dependent entity must always be accessed via the navigation from the principal entity. + /// Gets all skip navigations using this foreign key. /// - bool IsOwnership { get; } + /// The skip navigations using this foreign key. + new IEnumerable GetReferencingSkipNavigations() + => ((IReadOnlyForeignKey)this).GetReferencingSkipNavigations().Cast(); /// - /// Gets a value indicating how a delete operation is applied to dependent entities in the relationship when the - /// principal is deleted or the relationship is severed. + /// Gets the entity type related to the given one. /// - DeleteBehavior DeleteBehavior { get; } + /// One of the entity types related by the foreign key. + /// The entity type related to the given one. + IEntityType GetRelatedEntityType([NotNull] IEntityType entityType) + => (IEntityType)((IReadOnlyForeignKey)this).GetRelatedEntityType(entityType); /// - /// Gets the skip navigations using this foreign key. + /// Returns a navigation associated with this foreign key. /// - /// The skip navigations using this foreign key. - IEnumerable GetReferencingSkipNavigations() - => PrincipalEntityType.GetSkipNavigations().Where(n => !n.IsOnDependent && n.ForeignKey == this) - .Concat(DeclaringEntityType.GetSkipNavigations().Where(n => n.IsOnDependent && n.ForeignKey == this)); + /// + /// A value indicating whether the navigation is on the dependent type pointing to the principal type. + /// + /// + /// A navigation associated with this foreign key or . + /// + INavigation? GetNavigation(bool pointsToPrincipal) + => pointsToPrincipal ? DependentToPrincipal : PrincipalToDependent; } } diff --git a/src/EFCore/Metadata/IIndex.cs b/src/EFCore/Metadata/IIndex.cs index 453cc2daf4f..5bf0739353f 100644 --- a/src/EFCore/Metadata/IIndex.cs +++ b/src/EFCore/Metadata/IIndex.cs @@ -11,28 +11,18 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents an index on a set of properties. /// - public interface IIndex : IAnnotatable + public interface IIndex : IReadOnlyIndex, IAnnotatable { /// /// Gets the properties that this index is defined on. /// - IReadOnlyList Properties { get; } - - /// - /// Gets the name of this index. - /// - string? Name { get; } - - /// - /// Gets a value indicating whether the values assigned to the indexed properties are unique. - /// - bool IsUnique { get; } + new IReadOnlyList Properties { get; } /// /// Gets the entity type the index is defined on. This may be different from the type that /// are defined on when the index is defined a derived type in an inheritance hierarchy (since the properties /// may be defined on a base type). /// - IEntityType DeclaringEntityType { get; } + new IEntityType DeclaringEntityType { get; } } } diff --git a/src/EFCore/Metadata/IKey.cs b/src/EFCore/Metadata/IKey.cs index a2a7a1ee832..de28742c03b 100644 --- a/src/EFCore/Metadata/IKey.cs +++ b/src/EFCore/Metadata/IKey.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.Linq; using Microsoft.EntityFrameworkCore.Infrastructure; #nullable enable @@ -9,20 +10,27 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a primary or alternate key on an entity. + /// Represents a primary or alternate key on an entity type. /// - public interface IKey : IAnnotatable + public interface IKey : IReadOnlyKey, IAnnotatable { /// /// Gets the properties that make up the key. /// - IReadOnlyList Properties { get; } + new IReadOnlyList Properties { get; } /// /// Gets the entity type the key is defined on. This may be different from the type that /// are defined on when the key is defined a derived type in an inheritance hierarchy (since the properties /// may be defined on a base type). /// - IEntityType DeclaringEntityType { get; } + new IEntityType DeclaringEntityType { get; } + + /// + /// Gets all foreign keys that target a given primary or alternate key. + /// + /// The foreign keys that reference the given key. + IEnumerable GetReferencingForeignKeys() + => ((IReadOnlyKey)this).GetReferencingForeignKeys().Cast(); } } diff --git a/src/EFCore/Metadata/IModel.cs b/src/EFCore/Metadata/IModel.cs index 35fdb85e01f..0e7190a9dcd 100644 --- a/src/EFCore/Metadata/IModel.cs +++ b/src/EFCore/Metadata/IModel.cs @@ -1,10 +1,14 @@ // 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.Linq; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; #nullable enable @@ -25,36 +29,54 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// The implementation does not need to be thread-safe. /// /// - public interface IModel : IAnnotatable + public interface IModel : IReadOnlyModel, IAnnotatable { /// - /// Gets all entity types defined in the model. - /// - /// All entity types defined in the model. - IEnumerable GetEntityTypes(); - - /// - /// Gets the entity type with the given name. Returns if no entity type with the given name is found + /// Gets the entity with the given name. Returns if no entity type with the given name is found /// or the given CLR type is being used by shared type entity type /// or the entity type has a defining navigation. /// /// The name of the entity type to find. - /// The entity type, or if none are found. - IEntityType? FindEntityType([NotNull] string name); + /// The entity type, or if none is found. + new IEntityType? FindEntityType([NotNull] string name); /// - /// Gets the entity type for the given base name, defining navigation name - /// and the defining entity type. Returns if no matching entity type is found. + /// Gets the entity type for the given name, defining navigation name + /// and the defining entity type. Returns if no matching entity type is found. /// /// The name of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. IEntityType? FindEntityType( [NotNull] string name, [NotNull] string definingNavigationName, [NotNull] IEntityType definingEntityType); + /// + /// Gets the entity that maps the given entity class, where the class may be a proxy derived from the + /// actual entity type. Returns if no entity type with the given CLR type is found + /// or the given CLR type is being used by shared type entity type + /// or the entity type has a defining navigation. + /// + /// The type to find the corresponding entity type for. + /// The entity type, or if none is found. + IEntityType? FindRuntimeEntityType([NotNull] Type type) + { + Check.NotNull(type, nameof(type)); + + return FindEntityType(type) + ?? (type.BaseType == null + ? null + : FindEntityType(type.BaseType)); + } + + /// + /// Gets all entity types defined in the model. + /// + /// All entity types defined in the model. + new IEnumerable GetEntityTypes(); + /// /// The runtime service dependencies. /// @@ -77,5 +99,48 @@ bool SetModelDependencies([NotNull] SingletonModelDependencies modelDependencies return true; } + + /// + /// Gets the entity that maps the given entity class. Returns if no entity type with + /// the given CLR type is found or the given CLR type is being used by shared type entity type + /// or the entity type has a defining navigation. + /// + /// The type to find the corresponding entity type for. + /// The entity type, or if none is found. + IEntityType? FindEntityType([NotNull] Type type); + + /// + /// Gets the entity type for the given name, defining navigation name + /// and the defining entity type. Returns if no matching entity type is found. + /// + /// The type of the entity type to find. + /// The defining navigation of the entity type to find. + /// The defining entity type of the entity type to find. + /// The entity type, or if none is found. + IEntityType? FindEntityType( + [NotNull] Type type, + [NotNull] string definingNavigationName, + [NotNull] IEntityType definingEntityType) + => (IEntityType?)((IReadOnlyModel)this).FindEntityType(type, definingNavigationName, definingEntityType); + + /// + /// Gets the entity types matching the given type. + /// + /// The type of the entity type to find. + /// The entity types found. + [DebuggerStepThrough] + IEnumerable GetEntityTypes([NotNull] Type type); + + /// + /// Returns the entity types corresponding to the least derived types from the given. + /// + /// The base type. + /// An optional condition for filtering entity types. + /// List of entity types corresponding to the least derived types from the given. + IEnumerable FindLeastDerivedEntityTypes( + [NotNull] Type type, + [CanBeNull] Func? condition = null) + => ((IReadOnlyModel)this).FindLeastDerivedEntityTypes(type, condition == null ? null : t => condition((IEntityType)t)) + .Cast(); } } diff --git a/src/EFCore/Metadata/IMutableAnnotatable.cs b/src/EFCore/Metadata/IMutableAnnotatable.cs index 65f081dcba3..1951cd37750 100644 --- a/src/EFCore/Metadata/IMutableAnnotatable.cs +++ b/src/EFCore/Metadata/IMutableAnnotatable.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// not used in application code. /// /// - public interface IMutableAnnotatable : IAnnotatable + public interface IMutableAnnotatable : IReadOnlyAnnotatable { /// /// Gets or sets the value of the annotation with the given name. diff --git a/src/EFCore/Metadata/IMutableEntityType.cs b/src/EFCore/Metadata/IMutableEntityType.cs index b7c6511ced2..14c7045abba 100644 --- a/src/EFCore/Metadata/IMutableEntityType.cs +++ b/src/EFCore/Metadata/IMutableEntityType.cs @@ -13,14 +13,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// Represents an entity in an . + /// Represents an entity type in an . /// /// /// This interface is used during model creation and allows the metadata to be modified. /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableEntityType : IEntityType, IMutableTypeBase + public interface IMutableEntityType : IReadOnlyEntityType, IMutableTypeBase { /// /// Gets the model this entity belongs to. @@ -79,7 +79,7 @@ public interface IMutableEntityType : IEntityType, IMutableTypeBase /// /// The properties that make up the key. /// The key, or if none is defined. - new IMutableKey? FindKey([NotNull] IReadOnlyList properties); + new IMutableKey? FindKey([NotNull] IReadOnlyList properties); /// /// Gets the primary and alternate keys for this entity type. @@ -123,9 +123,9 @@ IMutableForeignKey AddForeignKey( /// /// The foreign key, or if none is defined. new IMutableForeignKey? FindForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType); + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType); /// /// Gets the foreign keys defined on this entity type. @@ -171,7 +171,7 @@ IMutableSkipNavigation AddSkipNavigation( /// The navigation property on the entity class. /// The navigation property, or if none is found. new IMutableSkipNavigation? FindSkipNavigation([NotNull] MemberInfo memberInfo) - => (IMutableSkipNavigation?)((IEntityType)this).FindSkipNavigation(memberInfo); + => (IMutableSkipNavigation?)((IReadOnlyEntityType)this).FindSkipNavigation(memberInfo); /// /// Gets a skip navigation property on this entity type. Returns if no skip navigation property is found. @@ -187,7 +187,7 @@ IMutableSkipNavigation AddSkipNavigation( /// The name of the navigation property on the entity class. /// The navigation property, or if none is found. new IMutableSkipNavigation? FindDeclaredSkipNavigation([NotNull] string name) - => (IMutableSkipNavigation?)((IEntityType)this).FindDeclaredSkipNavigation(name); + => (IMutableSkipNavigation?)((IReadOnlyEntityType)this).FindDeclaredSkipNavigation(name); /// /// @@ -201,7 +201,7 @@ IMutableSkipNavigation AddSkipNavigation( /// /// Declared foreign keys. new IEnumerable GetDeclaredSkipNavigations() - => ((IEntityType)this).GetDeclaredSkipNavigations().Cast(); + => ((IReadOnlyEntityType)this).GetDeclaredSkipNavigations().Cast(); /// /// Gets the skip navigation properties on this entity type. @@ -243,7 +243,7 @@ IMutableIndex AddIndex( /// /// The properties to find the index on. /// The index, or if none is found. - new IMutableIndex? FindIndex([NotNull] IReadOnlyList properties); + new IMutableIndex? FindIndex([NotNull] IReadOnlyList properties); /// /// Gets the index with the given name. Returns if no such index exists. diff --git a/src/EFCore/Metadata/IMutableForeignKey.cs b/src/EFCore/Metadata/IMutableForeignKey.cs index bc674a0250e..ce7f005ea23 100644 --- a/src/EFCore/Metadata/IMutableForeignKey.cs +++ b/src/EFCore/Metadata/IMutableForeignKey.cs @@ -21,7 +21,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableForeignKey : IForeignKey, IMutableAnnotatable + public interface IMutableForeignKey : IReadOnlyForeignKey, IMutableAnnotatable { /// /// Gets the foreign key properties in the dependent entity. @@ -186,6 +186,6 @@ public interface IMutableForeignKey : IForeignKey, IMutableAnnotatable /// /// The skip navigations using this foreign key. new IEnumerable GetReferencingSkipNavigations() - => ((IForeignKey)this).GetReferencingSkipNavigations().Cast(); + => ((IReadOnlyForeignKey)this).GetReferencingSkipNavigations().Cast(); } } diff --git a/src/EFCore/Metadata/IMutableIndex.cs b/src/EFCore/Metadata/IMutableIndex.cs index bfce47e9129..2ea68fa1316 100644 --- a/src/EFCore/Metadata/IMutableIndex.cs +++ b/src/EFCore/Metadata/IMutableIndex.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableIndex : IIndex, IMutableAnnotatable + public interface IMutableIndex : IReadOnlyIndex, IMutableAnnotatable { /// /// Gets or sets a value indicating whether the values assigned to the indexed properties are unique. diff --git a/src/EFCore/Metadata/IMutableKey.cs b/src/EFCore/Metadata/IMutableKey.cs index e5f7254ed9c..51273eea325 100644 --- a/src/EFCore/Metadata/IMutableKey.cs +++ b/src/EFCore/Metadata/IMutableKey.cs @@ -16,7 +16,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableKey : IMutableAnnotatable, IKey + public interface IMutableKey : IMutableAnnotatable, IReadOnlyKey { /// /// Gets the properties that make up the key. diff --git a/src/EFCore/Metadata/IMutableModel.cs b/src/EFCore/Metadata/IMutableModel.cs index 65e42720164..beb0b1cf422 100644 --- a/src/EFCore/Metadata/IMutableModel.cs +++ b/src/EFCore/Metadata/IMutableModel.cs @@ -22,7 +22,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableModel : IModel, IMutableAnnotatable + public interface IMutableModel : IReadOnlyModel, IMutableAnnotatable { /// /// @@ -100,7 +100,7 @@ IMutableEntityType AddEntityType( /// or the entity type has a defining navigation. /// /// The name of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. new IMutableEntityType? FindEntityType([NotNull] string name); /// @@ -110,7 +110,7 @@ IMutableEntityType AddEntityType( /// The name of the entity type to find. /// The defining navigation of the entity type to find. /// The defining entity type of the entity type to find. - /// The entity type, or if none are found. + /// The entity type, or if none is found. IMutableEntityType? FindEntityType( [NotNull] string name, [NotNull] string definingNavigationName, diff --git a/src/EFCore/Metadata/IMutableNavigation.cs b/src/EFCore/Metadata/IMutableNavigation.cs index b111c72a7e3..f6ff4c58354 100644 --- a/src/EFCore/Metadata/IMutableNavigation.cs +++ b/src/EFCore/Metadata/IMutableNavigation.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableNavigation : INavigation, IMutableNavigationBase + public interface IMutableNavigation : IReadOnlyNavigation, IMutableNavigationBase { /// /// Gets the type that this navigation property belongs to. @@ -26,7 +26,7 @@ public interface IMutableNavigation : INavigation, IMutableNavigationBase new IMutableEntityType DeclaringEntityType { [DebuggerStepThrough] - get => (IMutableEntityType)((INavigationBase)this).DeclaringEntityType; + get => (IMutableEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; } /// @@ -35,7 +35,7 @@ public interface IMutableNavigation : INavigation, IMutableNavigationBase new IMutableEntityType TargetEntityType { [DebuggerStepThrough] - get => (IMutableEntityType)((INavigationBase)this).TargetEntityType; + get => (IMutableEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// @@ -44,7 +44,7 @@ public interface IMutableNavigation : INavigation, IMutableNavigationBase new IMutableForeignKey ForeignKey { [DebuggerStepThrough] - get => (IMutableForeignKey)((INavigation)this).ForeignKey; + get => (IMutableForeignKey)((IReadOnlyNavigation)this).ForeignKey; } /// @@ -53,7 +53,7 @@ public interface IMutableNavigation : INavigation, IMutableNavigationBase new IMutableNavigation? Inverse { [DebuggerStepThrough] - get => (IMutableNavigation?)((INavigation)this).Inverse; + get => (IMutableNavigation?)((IReadOnlyNavigation)this).Inverse; } /// diff --git a/src/EFCore/Metadata/IMutableNavigationBase.cs b/src/EFCore/Metadata/IMutableNavigationBase.cs index 8f5d7a2d10a..43de45380a2 100644 --- a/src/EFCore/Metadata/IMutableNavigationBase.cs +++ b/src/EFCore/Metadata/IMutableNavigationBase.cs @@ -13,10 +13,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableNavigationBase : INavigationBase, IMutablePropertyBase + public interface IMutableNavigationBase : IReadOnlyNavigationBase, IMutablePropertyBase { /// /// Sets a value indicating whether this navigation should be eager loaded by default. diff --git a/src/EFCore/Metadata/IMutableProperty.cs b/src/EFCore/Metadata/IMutableProperty.cs index b92295b75dd..b77d81e3d12 100644 --- a/src/EFCore/Metadata/IMutableProperty.cs +++ b/src/EFCore/Metadata/IMutableProperty.cs @@ -7,14 +7,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// Represents a scalar property of an entity. + /// Represents a scalar property of an entity type. /// /// /// This interface is used during model creation and allows the metadata to be modified. /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableProperty : IProperty, IMutablePropertyBase + public interface IMutableProperty : IReadOnlyProperty, IMutablePropertyBase { /// /// Gets the type that this property belongs to. diff --git a/src/EFCore/Metadata/IMutablePropertyBase.cs b/src/EFCore/Metadata/IMutablePropertyBase.cs index f4c74e9c472..dd317804c9f 100644 --- a/src/EFCore/Metadata/IMutablePropertyBase.cs +++ b/src/EFCore/Metadata/IMutablePropertyBase.cs @@ -15,10 +15,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutablePropertyBase : IPropertyBase, IMutableAnnotatable + public interface IMutablePropertyBase : IReadOnlyPropertyBase, IMutableAnnotatable { /// /// Gets the type that this property belongs to. diff --git a/src/EFCore/Metadata/IMutableServiceProperty.cs b/src/EFCore/Metadata/IMutableServiceProperty.cs index 47d3bc55085..ecc98ba9619 100644 --- a/src/EFCore/Metadata/IMutableServiceProperty.cs +++ b/src/EFCore/Metadata/IMutableServiceProperty.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// /// - /// A in the Entity Framework model that represents an + /// A in the Entity Framework model that represents an /// injected service from the . /// /// @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableServiceProperty : IServiceProperty, IMutablePropertyBase + public interface IMutableServiceProperty : IReadOnlyServiceProperty, IMutablePropertyBase { /// /// Gets the type that this property belongs to. diff --git a/src/EFCore/Metadata/IMutableSkipNavigation.cs b/src/EFCore/Metadata/IMutableSkipNavigation.cs index 58468c9f1da..cce318ee7f7 100644 --- a/src/EFCore/Metadata/IMutableSkipNavigation.cs +++ b/src/EFCore/Metadata/IMutableSkipNavigation.cs @@ -18,7 +18,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBase + public interface IMutableSkipNavigation : IReadOnlySkipNavigation, IMutableNavigationBase { /// /// Gets the type that this navigation property belongs to. @@ -26,7 +26,7 @@ public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBas new IMutableEntityType DeclaringEntityType { [DebuggerStepThrough] - get => (IMutableEntityType)((INavigationBase)this).DeclaringEntityType; + get => (IMutableEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; } /// @@ -35,7 +35,7 @@ public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBas new IMutableEntityType TargetEntityType { [DebuggerStepThrough] - get => (IMutableEntityType)((INavigationBase)this).TargetEntityType; + get => (IMutableEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// @@ -44,7 +44,7 @@ public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBas new IMutableEntityType? JoinEntityType { [DebuggerStepThrough] - get => (IMutableEntityType?)((ISkipNavigation)this).JoinEntityType; + get => (IMutableEntityType?)((IReadOnlySkipNavigation)this).JoinEntityType; } /// @@ -53,7 +53,7 @@ public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBas new IMutableForeignKey? ForeignKey { [DebuggerStepThrough] - get => (IMutableForeignKey?)((ISkipNavigation)this).ForeignKey; + get => (IMutableForeignKey?)((IReadOnlySkipNavigation)this).ForeignKey; } /// @@ -70,7 +70,7 @@ public interface IMutableSkipNavigation : ISkipNavigation, IMutableNavigationBas new IMutableSkipNavigation? Inverse { [DebuggerStepThrough] - get => (IMutableSkipNavigation?)((ISkipNavigation)this).Inverse; + get => (IMutableSkipNavigation?)((IReadOnlySkipNavigation)this).Inverse; } /// diff --git a/src/EFCore/Metadata/IMutableTypeBase.cs b/src/EFCore/Metadata/IMutableTypeBase.cs index e1a422d3bbe..c6c76212cca 100644 --- a/src/EFCore/Metadata/IMutableTypeBase.cs +++ b/src/EFCore/Metadata/IMutableTypeBase.cs @@ -14,10 +14,10 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// This interface is used during model creation and allows the metadata to be modified. - /// Once the model is built, represents a read-only view of the same metadata. + /// Once the model is built, represents a read-only view of the same metadata. /// /// - public interface IMutableTypeBase : ITypeBase, IMutableAnnotatable + public interface IMutableTypeBase : IReadOnlyTypeBase, IMutableAnnotatable { /// /// Gets the model that this type belongs to. diff --git a/src/EFCore/Metadata/INavigation.cs b/src/EFCore/Metadata/INavigation.cs index 2f1d54d275d..5bd6f7373f8 100644 --- a/src/EFCore/Metadata/INavigation.cs +++ b/src/EFCore/Metadata/INavigation.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; -using Microsoft.EntityFrameworkCore.Metadata.Internal; #nullable enable @@ -11,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a navigation property which can be used to navigate a relationship. /// - public interface INavigation : INavigationBase + public interface INavigation : IReadOnlyNavigation, INavigationBase { /// /// Gets the entity type that this navigation property belongs to. @@ -19,7 +18,7 @@ public interface INavigation : INavigationBase new IEntityType DeclaringEntityType { [DebuggerStepThrough] - get => IsOnDependent ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType; + get => (IEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; } /// @@ -28,93 +27,25 @@ public interface INavigation : INavigationBase new IEntityType TargetEntityType { [DebuggerStepThrough] - get => IsOnDependent ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType; - } - - /// - /// Gets the inverse navigation. - /// - new INavigation? Inverse - { - [DebuggerStepThrough] - get => IsOnDependent ? ForeignKey.PrincipalToDependent : ForeignKey.DependentToPrincipal; - } - - /// - /// Gets a value indicating whether the navigation property is a collection property. - /// - new bool IsCollection - { - [DebuggerStepThrough] - get => !IsOnDependent && !ForeignKey.IsUnique; + get => (IEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// /// Gets the foreign key that defines the relationship this navigation property will navigate. /// - IForeignKey ForeignKey { get; } - - /// - /// Gets a value indicating whether the navigation property is defined on the dependent side of the underlying foreign key. - /// - bool IsOnDependent - { - [DebuggerStepThrough] - get => ForeignKey.DependentToPrincipal == this; - } - - /// - /// Gets the for this navigation property, if it's a collection - /// navigation. - /// - /// The accessor. - [DebuggerStepThrough] - new IClrCollectionAccessor? GetCollectionAccessor() - => ((Navigation)this).CollectionAccessor; - - /// - /// Gets the entity type that this navigation property belongs to. - /// - IEntityType INavigationBase.DeclaringEntityType - { - [DebuggerStepThrough] - get => DeclaringEntityType; - } - - /// - /// Gets the entity type that this navigation property will hold an instance(s) of. - /// - IEntityType INavigationBase.TargetEntityType + new IForeignKey ForeignKey { [DebuggerStepThrough] - get => TargetEntityType; + get => (IForeignKey)((IReadOnlyNavigation)this).ForeignKey; } /// /// Gets the inverse navigation. /// - INavigationBase? INavigationBase.Inverse - { - [DebuggerStepThrough] - get => Inverse; - } - - /// - /// Gets a value indicating whether the navigation property is a collection property. - /// - bool INavigationBase.IsCollection + new INavigation? Inverse { [DebuggerStepThrough] - get => IsCollection; + get => (INavigation?)((IReadOnlyNavigation)this).Inverse; } - - /// - /// Gets the for this navigation property, if it's a collection - /// navigation. - /// - /// The accessor. - [DebuggerStepThrough] - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() - => GetCollectionAccessor(); } } diff --git a/src/EFCore/Metadata/INavigationBase.cs b/src/EFCore/Metadata/INavigationBase.cs index c1dc068e03b..0c986dcb864 100644 --- a/src/EFCore/Metadata/INavigationBase.cs +++ b/src/EFCore/Metadata/INavigationBase.cs @@ -1,7 +1,11 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.EntityFrameworkCore.Metadata.Internal; +using System.Diagnostics; +using System.Linq; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Utilities; #nullable enable @@ -10,50 +14,54 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// Represents a navigation property which can be used to navigate a relationship. /// - public interface INavigationBase : IPropertyBase + public interface INavigationBase : IReadOnlyNavigationBase, IPropertyBase { /// /// Gets the entity type that this navigation property belongs to. /// - IEntityType DeclaringEntityType { get; } + new IEntityType DeclaringEntityType + { + [DebuggerStepThrough] + get => (IEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; + } /// /// Gets the entity type that this navigation property will hold an instance(s) of. /// - IEntityType TargetEntityType { get; } + new IEntityType TargetEntityType + { + [DebuggerStepThrough] + get => (IEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; + } /// /// Gets the inverse navigation. /// - INavigationBase? Inverse { get; } + new INavigationBase? Inverse + { + [DebuggerStepThrough] + get => (INavigationBase?)((IReadOnlyNavigationBase)this).Inverse; + } /// - /// Gets a value indicating whether the navigation property is a collection property. + /// Calls for a to mark it as loaded + /// when a no-tracking query has eagerly loaded this relationship. /// - bool IsCollection { get; } + /// The entity for which the navigation has been loaded. + void SetIsLoadedWhenNoTracking([NotNull] object entity) + { + Check.NotNull(entity, nameof(entity)); - /// - /// Gets a value indicating whether this navigation should be eager loaded by default. - /// - bool IsEagerLoaded - => (bool?)this[CoreAnnotationNames.EagerLoaded] ?? false; + var serviceProperties = DeclaringEntityType + .GetDerivedTypesInclusive() + .Where(t => t.ClrType.IsInstanceOfType(entity)) + .SelectMany(e => e.GetServiceProperties()) + .Where(p => p.ClrType == typeof(ILazyLoader)); - /// - /// Gets the for this navigation property, if it's a collection - /// navigation. - /// - /// The accessor. - IClrCollectionAccessor? GetCollectionAccessor(); - - /// - /// - /// Gets the being used for this property. - /// indicates that the default property access mode is being used. - /// - /// - /// The access mode being used, or if the default access mode is being used. - PropertyAccessMode IPropertyBase.GetPropertyAccessMode() - => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] - ?? DeclaringType.GetNavigationAccessMode()); + foreach (var serviceProperty in serviceProperties) + { + ((ILazyLoader?)serviceProperty.GetGetter().GetClrValue(entity))?.SetLoaded(entity, Name); + } + } } } diff --git a/src/EFCore/Metadata/IProperty.cs b/src/EFCore/Metadata/IProperty.cs index cdbff052e38..06b556806ab 100644 --- a/src/EFCore/Metadata/IProperty.cs +++ b/src/EFCore/Metadata/IProperty.cs @@ -1,54 +1,74 @@ // 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.Metadata.Internal; +using System.Collections.Generic; +using System.Linq; #nullable enable namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a scalar property of an entity. + /// Represents a scalar property of an entity type. /// - public interface IProperty : IPropertyBase + public interface IProperty : IReadOnlyProperty, IPropertyBase { /// - /// Gets the entity type that this property belongs to. + /// Gets the type that this property belongs to. /// - IEntityType DeclaringEntityType { get; } + new IEntityType DeclaringEntityType { get; } /// - /// Gets a value indicating whether this property can contain . + /// Finds the first principal property that the given property is constrained by + /// if the given property is part of a foreign key. /// - bool IsNullable { get; } + /// The first associated principal property, or if none exists. + IProperty? FindFirstPrincipal() + => (IProperty?)((IReadOnlyProperty)this).FindFirstPrincipal(); /// - /// Gets a value indicating when a value for this property will be generated by the database. Even when the - /// property is set to be generated by the database, EF may still attempt to save a specific value (rather than - /// having one generated by the database) when the entity is added and a value is assigned, or the property is - /// marked as modified for an existing entity. See - /// and for more information. + /// Finds the list of principal properties including the given property that the given property is constrained by + /// if the given property is part of a foreign key. /// - ValueGenerated ValueGenerated { get; } + /// The list of all associated principal properties including the given property. + IReadOnlyList FindPrincipals() + => ((IReadOnlyProperty)this).FindPrincipals().Cast().ToList(); /// - /// Gets a value indicating whether this property is used as a concurrency token. When a property is configured - /// as a concurrency token the value in the database will be checked when an instance of this entity type - /// is updated or deleted during to ensure it has not changed since - /// the instance was retrieved from the database. If it has changed, an exception will be thrown and the - /// changes will not be applied to the database. + /// Gets all foreign keys that use this property (including composite foreign keys in which this property + /// is included). /// - bool IsConcurrencyToken { get; } + /// + /// The foreign keys that use this property. + /// + IEnumerable GetContainingForeignKeys(); /// - /// - /// Gets the being used for this property. - /// indicates that the default property access mode is being used. - /// + /// Gets all indexes that use this property (including composite indexes in which this property + /// is included). /// - /// The access mode being used, or if the default access mode is being used. - PropertyAccessMode IPropertyBase.GetPropertyAccessMode() - => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] - ?? DeclaringType.GetPropertyAccessMode()); + /// + /// The indexes that use this property. + /// + IEnumerable GetContainingIndexes(); + + /// + /// Gets the primary key that uses this property (including a composite primary key in which this property + /// is included). + /// + /// + /// The primary that use this property, or if it is not part of the primary key. + /// + IKey? FindContainingPrimaryKey() + => (IKey?)((IReadOnlyProperty)this).FindContainingPrimaryKey(); + + /// + /// Gets all primary or alternate keys that use this property (including composite keys in which this property + /// is included). + /// + /// + /// The primary and alternate keys that use this property. + /// + IEnumerable GetContainingKeys(); } } diff --git a/src/EFCore/Metadata/IPropertyBase.cs b/src/EFCore/Metadata/IPropertyBase.cs index 97853ead3b4..9abe1a4f6a2 100644 --- a/src/EFCore/Metadata/IPropertyBase.cs +++ b/src/EFCore/Metadata/IPropertyBase.cs @@ -1,56 +1,38 @@ // 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 System.Reflection; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata.Internal; #nullable enable namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Base type for navigation and scalar properties. + /// Base type for navigations and properties. /// - public interface IPropertyBase : IAnnotatable + public interface IPropertyBase : IReadOnlyPropertyBase, IAnnotatable { - /// - /// Gets the name of this property-like object. - /// - string Name { get; } - /// /// Gets the type that this property-like object belongs to. /// - ITypeBase DeclaringType { get; } - - /// - /// Gets the type of value that this property-like object holds. - /// - Type ClrType { get; } - - /// - /// Gets the for the underlying CLR property for this property-like object. - /// This may be for shadow properties or if mapped directly to a field. - /// - PropertyInfo? PropertyInfo { get; } - - /// - /// Gets the for the underlying CLR field for this property-like object. - /// This may be for shadow properties or if the backing field is not known. - /// - FieldInfo? FieldInfo { get; } + new ITypeBase DeclaringType + { + [DebuggerStepThrough] + get => (ITypeBase) ((IReadOnlyPropertyBase)this).DeclaringType; + } /// /// - /// Gets the being used for this property. - /// indicates that the default property access mode is being used. + /// Gets a for reading the value of this property. + /// + /// + /// Note that it is an error to call this method for a shadow property () + /// since such a property has no associated . /// /// - /// The access mode being used, or if the default access mode is being used. - PropertyAccessMode GetPropertyAccessMode() - => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] - ?? PropertyAccessMode.PreferField); + /// The accessor. + IClrPropertyGetter GetGetter(); } } diff --git a/src/EFCore/Metadata/IReadOnlyEntityType.cs b/src/EFCore/Metadata/IReadOnlyEntityType.cs new file mode 100644 index 00000000000..7e30a84776d --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyEntityType.cs @@ -0,0 +1,213 @@ +// 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.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Utilities; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents an entity type in a model. + /// + public interface IReadOnlyEntityType : IReadOnlyTypeBase + { + /// + /// Gets the base type of this entity type. Returns if this is not a derived type in an inheritance hierarchy. + /// + IReadOnlyEntityType? BaseType { get; } + + /// + /// Gets the name of the defining navigation. + /// + [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] + string? DefiningNavigationName => null; + + /// + /// Gets the defining entity type. + /// + [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] + IReadOnlyEntityType? DefiningEntityType => null; + + /// + /// Gets a value indicating whether this entity type has a defining navigation. + /// + /// if this entity type has a defining navigation. + [Obsolete("Entity types with defining navigations have been replaced by shared-type entity types")] + public bool HasDefiningNavigation() => HasSharedClrType; + + /// + /// Gets primary key for this entity type. Returns if no primary key is defined. + /// + /// The primary key, or if none is defined. + IReadOnlyKey? FindPrimaryKey(); + + /// + /// Gets the primary or alternate key that is defined on the given properties. + /// Returns if no key is defined for the given properties. + /// + /// The properties that make up the key. + /// The key, or if none is defined. + IReadOnlyKey? FindKey([NotNull] IReadOnlyList properties); + + /// + /// Gets the primary and alternate keys for this entity type. + /// + /// The primary and alternate keys. + IEnumerable GetKeys(); + + /// + /// Gets the foreign key for the given properties that points to a given primary or alternate key. + /// Returns if no foreign key is found. + /// + /// The properties that the foreign key is defined on. + /// The primary or alternate key that is referenced. + /// + /// The entity type that the relationship targets. This may be different from the type that + /// is defined on when the relationship targets a derived type in an inheritance hierarchy (since the key is defined on the + /// base type of the hierarchy). + /// + /// The foreign key, or if none is defined. + IReadOnlyForeignKey? FindForeignKey( + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType); + + /// + /// Gets the foreign keys defined on this entity type. + /// + /// The foreign keys defined on this entity type. + IEnumerable GetForeignKeys(); + + /// + /// Gets a skip navigation property on this entity type. Returns if no navigation property is found. + /// + /// The navigation property on the entity class. + /// The navigation property, or if none is found. + IReadOnlySkipNavigation? FindSkipNavigation([NotNull] MemberInfo memberInfo) + => FindSkipNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); + + /// + /// Gets a skip navigation property on this entity type. Returns if no skip navigation property is found. + /// + /// The name of the navigation property on the entity class. + /// The navigation property, or if none is found. + IReadOnlySkipNavigation? FindSkipNavigation([NotNull] string name); + + /// + /// + /// Gets a skip navigation property on this entity type. + /// + /// + /// Does not return skip navigation properties defined on a base type. + /// Returns if no skip navigation property is found. + /// + /// + /// The name of the navigation property on the entity class. + /// The navigation property, or if none is found. + IReadOnlySkipNavigation? FindDeclaredSkipNavigation([NotNull] string name) + { + var navigation = FindSkipNavigation(name); + return navigation?.DeclaringEntityType == this ? navigation : null; + } + + /// + /// + /// Gets all skip navigation properties declared on this entity type. + /// + /// + /// This method does not return skip navigation properties declared declared on base types. + /// It is useful when iterating over all entity types to avoid processing the same foreign key more than once. + /// Use to also return skip navigation properties declared on base types. + /// + /// + /// Declared skip navigations. + IEnumerable GetDeclaredSkipNavigations() + => GetSkipNavigations().Where(n => n.DeclaringEntityType == this); + + /// + /// Gets the skip navigation properties on this entity type. + /// + /// All skip navigation properties on this entity type. + IEnumerable GetSkipNavigations(); + + /// + /// + /// 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. + IReadOnlyIndex? FindIndex([NotNull] IReadOnlyList properties); + + /// + /// Gets the index with the given name. Returns if no such index exists. + /// + /// The name of the index to find. + /// The index, or if none is found. + IReadOnlyIndex? FindIndex([NotNull] string name); + + /// + /// Gets the indexes defined on this entity type. + /// + /// The indexes defined on this entity type. + IEnumerable GetIndexes(); + + /// + /// + /// Gets the property with a given name. Returns if no property with the given name is defined. + /// + /// + /// This API only finds scalar properties and does not find navigation properties. Use + /// to find a navigation property. + /// + /// + /// The name of the property. + /// The property, or if none is found. + IReadOnlyProperty? FindProperty([NotNull] string name); + + /// + /// + /// Gets the properties defined on this entity type. + /// + /// + /// This API only returns scalar properties and does not return navigation properties. Use + /// to get navigation properties. + /// + /// + /// The properties defined on this entity type. + IEnumerable GetProperties(); + + /// + /// + /// Gets the 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 service property, or if none is found. + IReadOnlyServiceProperty? FindServiceProperty([NotNull] string name); + + /// + /// + /// Gets all the defined on this entity type. + /// + /// + /// This API only returns service properties and does not return scalar or navigation properties. + /// + /// + /// The service properties defined on this entity type. + IEnumerable GetServiceProperties(); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyForeignKey.cs b/src/EFCore/Metadata/IReadOnlyForeignKey.cs new file mode 100644 index 00000000000..d733f95e44a --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyForeignKey.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a relationship where a foreign key composed of properties on the dependent entity type + /// references a corresponding primary or alternate key on the principal entity type. + /// + public interface IReadOnlyForeignKey : IReadOnlyAnnotatable + { + /// + /// Gets the dependent entity type. This may be different from the type that + /// are defined on when the relationship is defined a derived type in an inheritance hierarchy (since the properties + /// may be defined on a base type). + /// + IReadOnlyEntityType DeclaringEntityType { get; } + + /// + /// Gets the foreign key properties in the dependent entity. + /// + IReadOnlyList Properties { get; } + + /// + /// Gets the principal entity type that this relationship targets. This may be different from the type that + /// is defined on when the relationship targets a derived type in an inheritance + /// hierarchy (since the key is defined on the base type of the hierarchy). + /// + IReadOnlyEntityType PrincipalEntityType { get; } + + /// + /// Gets the primary or alternate key that the relationship targets. + /// + IReadOnlyKey PrincipalKey { get; } + + /// + /// Gets the navigation property on the dependent entity type that points to the principal entity. + /// + IReadOnlyNavigation? DependentToPrincipal { get; } + + /// + /// Gets the navigation property on the principal entity type that points to the dependent entity. + /// + IReadOnlyNavigation? PrincipalToDependent { get; } + + /// + /// Gets a value indicating whether the values assigned to the foreign key properties are unique. + /// + bool IsUnique { get; } + + /// + /// Gets a value indicating whether the principal entity is required. + /// If , the dependent entity must always be assigned to a valid principal entity. + /// + bool IsRequired { get; } + + /// + /// Gets a value indicating whether the dependent entity is required. + /// If , the principal entity must always have a valid dependent entity assigned. + /// + bool IsRequiredDependent { get; } + + /// + /// Gets or sets 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; } + + /// + /// Gets a value indicating how a delete operation is applied to dependent entities in the relationship when the + /// principal is deleted or the relationship is severed. + /// + DeleteBehavior DeleteBehavior { get; } + + /// + /// Gets the skip navigations using this foreign key. + /// + /// The skip navigations using this foreign key. + IEnumerable GetReferencingSkipNavigations() + => PrincipalEntityType.GetSkipNavigations().Where(n => !n.IsOnDependent && n.ForeignKey == this) + .Concat(DeclaringEntityType.GetSkipNavigations().Where(n => n.IsOnDependent && n.ForeignKey == this)); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyIndex.cs b/src/EFCore/Metadata/IReadOnlyIndex.cs new file mode 100644 index 00000000000..0c382a2811d --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyIndex.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents an index on a set of properties. + /// + public interface IReadOnlyIndex : IReadOnlyAnnotatable + { + /// + /// Gets the properties that this index is defined on. + /// + IReadOnlyList Properties { get; } + + /// + /// Gets the name of this index. + /// + string? Name { get; } + + /// + /// Gets a value indicating whether the values assigned to the indexed properties are unique. + /// + bool IsUnique { get; } + + /// + /// Gets the entity type the index is defined on. This may be different from the type that + /// are defined on when the index is defined a derived type in an inheritance hierarchy (since the properties + /// may be defined on a base type). + /// + IReadOnlyEntityType DeclaringEntityType { get; } + } +} diff --git a/src/EFCore/Metadata/IReadOnlyKey.cs b/src/EFCore/Metadata/IReadOnlyKey.cs new file mode 100644 index 00000000000..b1892d3adc6 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyKey.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a primary or alternate key on an entity type. + /// + public interface IReadOnlyKey : IReadOnlyAnnotatable + { + /// + /// Gets the properties that make up the key. + /// + IReadOnlyList Properties { get; } + + /// + /// Gets the entity type the key is defined on. This may be different from the type that + /// are defined on when the key is defined a derived type in an inheritance hierarchy (since the properties + /// may be defined on a base type). + /// + IReadOnlyEntityType DeclaringEntityType { get; } + } +} diff --git a/src/EFCore/Metadata/IReadOnlyModel.cs b/src/EFCore/Metadata/IReadOnlyModel.cs new file mode 100644 index 00000000000..09ba523a63b --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyModel.cs @@ -0,0 +1,47 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Metadata about the shape of entities, the relationships between them, and how they map to + /// the database. A model is typically created by overriding the + /// method on a derived . + /// + public interface IReadOnlyModel : IReadOnlyAnnotatable + { + /// + /// Gets all entity types defined in the model. + /// + /// All entity types defined in the model. + IEnumerable GetEntityTypes(); + + /// + /// Gets the entity type with the given name. Returns if no entity type with the given name is found + /// or the given CLR type is being used by shared type entity type + /// or the entity type has a defining navigation. + /// + /// The name of the entity type to find. + /// The entity type, or if none is found. + IReadOnlyEntityType? FindEntityType([NotNull] string name); + + /// + /// Gets the entity type for the given base name, defining navigation name + /// and the defining entity type. Returns if no matching entity type is found. + /// + /// The name of the entity type to find. + /// The defining navigation of the entity type to find. + /// The defining entity type of the entity type to find. + /// The entity type, or if none is found. + IReadOnlyEntityType? FindEntityType( + [NotNull] string name, + [NotNull] string definingNavigationName, + [NotNull] IReadOnlyEntityType definingEntityType); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyNavigation.cs b/src/EFCore/Metadata/IReadOnlyNavigation.cs new file mode 100644 index 00000000000..762bcce3525 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyNavigation.cs @@ -0,0 +1,120 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a navigation property which can be used to navigate a relationship. + /// + public interface IReadOnlyNavigation : IReadOnlyNavigationBase + { + /// + /// Gets the entity type that this navigation property belongs to. + /// + new IReadOnlyEntityType DeclaringEntityType + { + [DebuggerStepThrough] + get => IsOnDependent ? ForeignKey.DeclaringEntityType : ForeignKey.PrincipalEntityType; + } + + /// + /// Gets the entity type that this navigation property will hold an instance(s) of. + /// + new IReadOnlyEntityType TargetEntityType + { + [DebuggerStepThrough] + get => IsOnDependent ? ForeignKey.PrincipalEntityType : ForeignKey.DeclaringEntityType; + } + + /// + /// Gets the inverse navigation. + /// + new IReadOnlyNavigation? Inverse + { + [DebuggerStepThrough] + get => IsOnDependent ? ForeignKey.PrincipalToDependent : ForeignKey.DependentToPrincipal; + } + + /// + /// Gets a value indicating whether the navigation property is a collection property. + /// + new bool IsCollection + { + [DebuggerStepThrough] + get => !IsOnDependent && !ForeignKey.IsUnique; + } + + /// + /// Gets the foreign key that defines the relationship this navigation property will navigate. + /// + IReadOnlyForeignKey ForeignKey { get; } + + /// + /// Gets a value indicating whether the navigation property is defined on the dependent side of the underlying foreign key. + /// + bool IsOnDependent + { + [DebuggerStepThrough] + get => ForeignKey.DependentToPrincipal == this; + } + + /// + /// Gets the for this navigation property, if it's a collection + /// navigation. + /// + /// The accessor. + [DebuggerStepThrough] + new IClrCollectionAccessor? GetCollectionAccessor() + => ((Navigation)this).CollectionAccessor; + + /// + /// Gets the entity type that this navigation property belongs to. + /// + IReadOnlyEntityType IReadOnlyNavigationBase.DeclaringEntityType + { + [DebuggerStepThrough] + get => DeclaringEntityType; + } + + /// + /// Gets the entity type that this navigation property will hold an instance(s) of. + /// + IReadOnlyEntityType IReadOnlyNavigationBase.TargetEntityType + { + [DebuggerStepThrough] + get => TargetEntityType; + } + + /// + /// Gets the inverse navigation. + /// + IReadOnlyNavigationBase? IReadOnlyNavigationBase.Inverse + { + [DebuggerStepThrough] + get => Inverse; + } + + /// + /// Gets a value indicating whether the navigation property is a collection property. + /// + bool IReadOnlyNavigationBase.IsCollection + { + [DebuggerStepThrough] + get => IsCollection; + } + + /// + /// Gets the for this navigation property, if it's a collection + /// navigation. + /// + /// The accessor. + [DebuggerStepThrough] + IClrCollectionAccessor? IReadOnlyNavigationBase.GetCollectionAccessor() + => GetCollectionAccessor(); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyNavigationBase.cs b/src/EFCore/Metadata/IReadOnlyNavigationBase.cs new file mode 100644 index 00000000000..0925e495958 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyNavigationBase.cs @@ -0,0 +1,59 @@ +// 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.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a navigation property which can be used to navigate a relationship. + /// + public interface IReadOnlyNavigationBase : IReadOnlyPropertyBase + { + /// + /// Gets the entity type that this navigation property belongs to. + /// + IReadOnlyEntityType DeclaringEntityType { get; } + + /// + /// Gets the entity type that this navigation property will hold an instance(s) of. + /// + IReadOnlyEntityType TargetEntityType { get; } + + /// + /// Gets the inverse navigation. + /// + IReadOnlyNavigationBase? Inverse { get; } + + /// + /// Gets a value indicating whether the navigation property is a collection property. + /// + bool IsCollection { get; } + + /// + /// Gets a value indicating whether this navigation should be eager loaded by default. + /// + bool IsEagerLoaded + => (bool?)this[CoreAnnotationNames.EagerLoaded] ?? false; + + /// + /// Gets the for this navigation property, if it's a collection + /// navigation. + /// + /// The accessor. + IClrCollectionAccessor? GetCollectionAccessor(); + + /// + /// + /// Gets the being used for this property. + /// indicates that the default property access mode is being used. + /// + /// + /// The access mode being used. + PropertyAccessMode IReadOnlyPropertyBase.GetPropertyAccessMode() + => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] + ?? DeclaringType.GetNavigationAccessMode()); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyProperty.cs b/src/EFCore/Metadata/IReadOnlyProperty.cs new file mode 100644 index 00000000000..8568e03caa6 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyProperty.cs @@ -0,0 +1,54 @@ +// 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.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a scalar property of an entity type. + /// + public interface IReadOnlyProperty : IReadOnlyPropertyBase + { + /// + /// Gets the entity type that this property belongs to. + /// + IReadOnlyEntityType DeclaringEntityType { get; } + + /// + /// Gets a value indicating whether this property can contain . + /// + bool IsNullable { get; } + + /// + /// Gets a value indicating when a value for this property will be generated by the database. Even when the + /// property is set to be generated by the database, EF may still attempt to save a specific value (rather than + /// having one generated by the database) when the entity is added and a value is assigned, or the property is + /// marked as modified for an existing entity. See + /// and for more information. + /// + ValueGenerated ValueGenerated { get; } + + /// + /// Gets a value indicating whether this property is used as a concurrency token. When a property is configured + /// as a concurrency token the value in the database will be checked when an instance of this entity type + /// is updated or deleted during to ensure it has not changed since + /// the instance was retrieved from the database. If it has changed, an exception will be thrown and the + /// changes will not be applied to the database. + /// + bool IsConcurrencyToken { get; } + + /// + /// + /// Gets the being used for this property. + /// indicates that the default property access mode is being used. + /// + /// + /// The access mode being used, or if the default access mode is being used. + PropertyAccessMode IReadOnlyPropertyBase.GetPropertyAccessMode() + => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] + ?? DeclaringType.GetPropertyAccessMode()); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyPropertyBase.cs b/src/EFCore/Metadata/IReadOnlyPropertyBase.cs new file mode 100644 index 00000000000..4f71f975375 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyPropertyBase.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Base type for navigations and properties. + /// + public interface IReadOnlyPropertyBase : IReadOnlyAnnotatable + { + /// + /// Gets the name of this property-like object. + /// + string Name { get; } + + /// + /// Gets the type that this property-like object belongs to. + /// + IReadOnlyTypeBase DeclaringType { get; } + + /// + /// Gets the type of value that this property-like object holds. + /// + Type ClrType { get; } + + /// + /// Gets the for the underlying CLR property for this property-like object. + /// This may be for shadow properties or if mapped directly to a field. + /// + PropertyInfo? PropertyInfo { get; } + + /// + /// Gets the for the underlying CLR field for this property-like object. + /// This may be for shadow properties or if the backing field is not known. + /// + FieldInfo? FieldInfo { get; } + + /// + /// + /// Gets the being used for this property. + /// indicates that the default property access mode is being used. + /// + /// + /// The access mode being used. + PropertyAccessMode GetPropertyAccessMode() + => (PropertyAccessMode)(this[CoreAnnotationNames.PropertyAccessMode] + ?? PropertyAccessMode.PreferField); + } +} diff --git a/src/EFCore/Metadata/IReadOnlyServiceProperty.cs b/src/EFCore/Metadata/IReadOnlyServiceProperty.cs new file mode 100644 index 00000000000..2fb7e894d14 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyServiceProperty.cs @@ -0,0 +1,24 @@ +// 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 + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a property on an entity type that represents an + /// injected service from the . + /// + public interface IReadOnlyServiceProperty : IReadOnlyPropertyBase + { + /// + /// Gets the entity type that this property belongs to. + /// + IReadOnlyEntityType DeclaringEntityType { get; } + + /// + /// The for this property. + /// + ServiceParameterBinding? ParameterBinding { get; } + } +} diff --git a/src/EFCore/Metadata/IReadOnlySkipNavigation.cs b/src/EFCore/Metadata/IReadOnlySkipNavigation.cs new file mode 100644 index 00000000000..c1179d75311 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlySkipNavigation.cs @@ -0,0 +1,55 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a navigation property that is part of a relationship + /// that is forwarded through a third entity type. + /// + public interface IReadOnlySkipNavigation : IReadOnlyNavigationBase + { + /// + /// Gets the join type used by the foreign key. + /// + IReadOnlyEntityType? JoinEntityType + => IsOnDependent ? ForeignKey?.PrincipalEntityType : ForeignKey?.DeclaringEntityType; + + /// + /// Gets the inverse skip navigation. + /// + new IReadOnlySkipNavigation Inverse { get; } + + /// + /// Gets the inverse navigation. + /// + IReadOnlyNavigationBase IReadOnlyNavigationBase.Inverse + { + [DebuggerStepThrough] + get => Inverse; + } + + /// + /// Gets the foreign key to the join type. + /// + IReadOnlyForeignKey? ForeignKey { get; } + + /// + /// Gets a value indicating whether the navigation property is defined on the dependent side of the underlying foreign key. + /// + bool IsOnDependent { get; } + + /// + /// Gets the for this navigation property, if it's a collection + /// navigation. + /// + /// The accessor. + IClrCollectionAccessor? IReadOnlyNavigationBase.GetCollectionAccessor() + => ((SkipNavigation)this).CollectionAccessor; + } +} diff --git a/src/EFCore/Metadata/IReadOnlyTypeBase.cs b/src/EFCore/Metadata/IReadOnlyTypeBase.cs new file mode 100644 index 00000000000..936a968ebe3 --- /dev/null +++ b/src/EFCore/Metadata/IReadOnlyTypeBase.cs @@ -0,0 +1,48 @@ +// 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 Microsoft.EntityFrameworkCore.Infrastructure; + +#nullable enable + +namespace Microsoft.EntityFrameworkCore.Metadata +{ + /// + /// Represents a type in the model. + /// + public interface IReadOnlyTypeBase : IReadOnlyAnnotatable + { + /// + /// Gets the model that this type belongs to. + /// + IReadOnlyModel Model { get; } + + /// + /// Gets the name of this type. + /// + string Name { get; } + + /// + /// + /// Gets the CLR class that is used to represent instances of this type. + /// Returns if the type does not have a corresponding CLR class (known as a shadow type). + /// + /// + /// Shadow types are not currently supported in a model that is used at runtime with a . + /// Therefore, shadow types will only exist in migration model snapshots, etc. + /// + /// + Type ClrType { get; } + + /// + /// Gets whether this entity type can share its ClrType with other entities. + /// + bool HasSharedClrType { get; } + + /// + /// Gets whether this entity type has an indexer which is able to contain arbitrary properties. + /// + bool IsPropertyBag { get; } + } +} diff --git a/src/EFCore/Metadata/IServiceProperty.cs b/src/EFCore/Metadata/IServiceProperty.cs index 0445539cebb..505b57abcd2 100644 --- a/src/EFCore/Metadata/IServiceProperty.cs +++ b/src/EFCore/Metadata/IServiceProperty.cs @@ -6,19 +6,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// A in the Entity Framework model that represents an + /// Represents a property on an entity type that represents an /// injected service from the . /// - public interface IServiceProperty : IPropertyBase + public interface IServiceProperty : IReadOnlyServiceProperty, IPropertyBase { /// /// Gets the entity type that this property belongs to. /// - IEntityType DeclaringEntityType { get; } - - /// - /// The for this property. - /// - ServiceParameterBinding? ParameterBinding { get; } + new IEntityType DeclaringEntityType { get; } } } diff --git a/src/EFCore/Metadata/ISkipNavigation.cs b/src/EFCore/Metadata/ISkipNavigation.cs index 9d2287c0393..533a128244f 100644 --- a/src/EFCore/Metadata/ISkipNavigation.cs +++ b/src/EFCore/Metadata/ISkipNavigation.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics; -using Microsoft.EntityFrameworkCore.Metadata.Internal; #nullable enable @@ -12,44 +11,51 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// Represents a navigation property that is part of a relationship /// that is forwarded through a third entity type. /// - public interface ISkipNavigation : INavigationBase + public interface ISkipNavigation : IReadOnlySkipNavigation, INavigationBase { /// - /// Gets the join type used by the foreign key. + /// Gets the entity type that this navigation property belongs to. /// - IEntityType? JoinEntityType - => IsOnDependent ? ForeignKey?.PrincipalEntityType : ForeignKey?.DeclaringEntityType; + new IEntityType DeclaringEntityType + { + [DebuggerStepThrough] + get => (IEntityType)((IReadOnlyNavigationBase)this).DeclaringEntityType; + } /// - /// Gets the inverse skip navigation. + /// Gets the entity type that this navigation property will hold an instance(s) of. /// - new ISkipNavigation Inverse { get; } + new IEntityType TargetEntityType + { + [DebuggerStepThrough] + get => (IEntityType)((IReadOnlyNavigationBase)this).TargetEntityType; + } /// - /// Gets the inverse navigation. + /// Gets the join type used by the foreign key. /// - INavigationBase INavigationBase.Inverse + new IEntityType JoinEntityType { [DebuggerStepThrough] - get => Inverse; + get => (IEntityType)((IReadOnlySkipNavigation)this).JoinEntityType!; } /// /// Gets the foreign key to the join type. /// - IForeignKey ForeignKey { get; } - - /// - /// Gets a value indicating whether the navigation property is defined on the dependent side of the underlying foreign key. - /// - bool IsOnDependent { get; } + new IForeignKey ForeignKey + { + [DebuggerStepThrough] + get => (IForeignKey)((IReadOnlySkipNavigation)this).ForeignKey!; + } /// - /// Gets the for this navigation property, if it's a collection - /// navigation. + /// Gets the inverse skip navigation. /// - /// The accessor. - IClrCollectionAccessor? INavigationBase.GetCollectionAccessor() - => ((SkipNavigation)this).CollectionAccessor; + new ISkipNavigation Inverse + { + [DebuggerStepThrough] + get => (ISkipNavigation)((IReadOnlySkipNavigation)this).Inverse!; + } } } diff --git a/src/EFCore/Metadata/ITypeBase.cs b/src/EFCore/Metadata/ITypeBase.cs index afb6f56e4b5..1be61b3e757 100644 --- a/src/EFCore/Metadata/ITypeBase.cs +++ b/src/EFCore/Metadata/ITypeBase.cs @@ -1,50 +1,20 @@ // 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 Microsoft.EntityFrameworkCore.Infrastructure; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable namespace Microsoft.EntityFrameworkCore.Metadata { /// - /// Represents a type in an . + /// Represents a type in the model. /// - public interface ITypeBase : IAnnotatable + public interface ITypeBase : IReadOnlyTypeBase, IAnnotatable { /// /// Gets the model that this type belongs to. /// - IModel Model { get; } - - /// - /// Gets the name of this type. - /// - string Name { get; } - - /// - /// - /// Gets the CLR class that is used to represent instances of this type. - /// Returns if the type does not have a corresponding CLR class (known as a shadow type). - /// - /// - /// Shadow types are not currently supported in a model that is used at runtime with a . - /// Therefore, shadow types will only exist in migration model snapshots, etc. - /// - /// - Type ClrType { get; } - - /// - /// Gets whether this entity type can share its ClrType with other entities. - /// - [CA.MemberNotNullWhen(true, nameof(ClrType))] - bool HasSharedClrType { get; } - - /// - /// Gets whether this entity type has an indexer which is able to contain arbitrary properties. - /// - bool IsPropertyBag { get; } + new IModel Model { get; } } } diff --git a/src/EFCore/Metadata/IndexComparer.cs b/src/EFCore/Metadata/IndexComparer.cs index fadad5c1963..1153b85fac1 100644 --- a/src/EFCore/Metadata/IndexComparer.cs +++ b/src/EFCore/Metadata/IndexComparer.cs @@ -12,14 +12,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// An implementation of and to compare - /// instances. + /// instances. /// /// /// This type is typically used by database providers (and other extensions). It is generally /// not used in application code. /// /// - public sealed class IndexComparer : IEqualityComparer, IComparer + public sealed class IndexComparer : IEqualityComparer, IComparer { private IndexComparer() { @@ -36,7 +36,7 @@ private IndexComparer() /// The first object to compare. /// The second object to compare. /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. - public int Compare(IIndex? x, IIndex? y) + public int Compare(IReadOnlyIndex? x, IReadOnlyIndex? y) { var result = PropertyListComparer.Instance.Compare(x?.Properties, y?.Properties); return result != 0 ? result : EntityTypeFullNameComparer.Instance.Compare(x?.DeclaringEntityType, y?.DeclaringEntityType); @@ -48,7 +48,7 @@ public int Compare(IIndex? x, IIndex? y) /// The first object to compare. /// The second object to compare. /// if the specified objects are equal; otherwise, . - public bool Equals(IIndex? x, IIndex? y) + public bool Equals(IReadOnlyIndex? x, IReadOnlyIndex? y) => Compare(x, y) == 0; /// @@ -56,7 +56,7 @@ public bool Equals(IIndex? x, IIndex? y) /// /// The for which a hash code is to be returned. /// A hash code for the specified object. - public int GetHashCode(IIndex obj) + public int GetHashCode(IReadOnlyIndex obj) { var hashCode = new HashCode(); hashCode.Add(obj.Properties, PropertyListComparer.Instance); diff --git a/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs b/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs index a9b89856260..427a225d98c 100644 --- a/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs +++ b/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs @@ -82,9 +82,9 @@ public virtual bool TryBindConstructor( out unboundParameters); private bool TryBindConstructor( - IEntityType entityType, + IReadOnlyEntityType entityType, ConstructorInfo constructor, - Func bind, + Func bind, [CA.NotNullWhen(true)] out InstantiationBinding? binding, [CA.NotNullWhen(false)] out IEnumerable? unboundParameters) { @@ -92,7 +92,7 @@ private bool TryBindConstructor( = constructor.GetParameters().Select( p => (p, string.IsNullOrEmpty(p.Name) ? null - : _propertyFactory.FindParameter(entityType, p.ParameterType, p.Name) + : _propertyFactory.FindParameter((IEntityType)entityType, p.ParameterType, p.Name) ?? bind(_factories.FindFactory(p.ParameterType, p.Name), entityType, p.ParameterType, p.Name))) .ToList(); diff --git a/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs index 1878edc1ffe..9c87cbb2509 100644 --- a/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs +++ b/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs @@ -36,7 +36,7 @@ public virtual bool CanBind( public virtual ParameterBinding Bind(IMutableEntityType entityType, Type parameterType, string parameterName) => new ContextParameterBinding( parameterType, - entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -50,6 +50,6 @@ public virtual ParameterBinding Bind( string parameterName) => new ContextParameterBinding( parameterType, - entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); } } diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index bce31c04e93..474d697f744 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -28,7 +28,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 EntityType : TypeBase, IMutableEntityType, IConventionEntityType + public class EntityType : TypeBase, IMutableEntityType, IConventionEntityType, IEntityType { private const string DynamicProxyGenAssemblyName = "DynamicProxyGenAssembly2"; @@ -41,7 +41,7 @@ private readonly SortedDictionary _navigations private readonly SortedDictionary _skipNavigations = new(StringComparer.Ordinal); - private readonly SortedDictionary, Index> _unnamedIndexes + private readonly SortedDictionary, Index> _unnamedIndexes = new(PropertyListComparer.Instance); private readonly SortedDictionary _namedIndexes @@ -49,7 +49,7 @@ private readonly SortedDictionary _namedIndexes private readonly SortedDictionary _properties; - private readonly SortedDictionary, Key> _keys + private readonly SortedDictionary, Key> _keys = new(PropertyListComparer.Instance); private readonly SortedDictionary _serviceProperties @@ -77,8 +77,8 @@ private readonly SortedDictionary _serviceProperties private Func? _shadowValuesFactory; private Func? _emptyShadowValuesFactory; private Func? _instanceFactory; - private IProperty[]? _propagatingProperties; - private IProperty[]? _generatingProperties; + private IProperty[]? _foreignKeyProperties; + private IProperty[]? _valueGeneratingProperties; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -403,6 +403,11 @@ public virtual ISet GetDirectlyDerivedTypes() /// public virtual IEnumerable GetDerivedTypes() { + if (_directlyDerivedTypes.Count == 0) + { + return Enumerable.Empty(); + } + var derivedTypes = new List(); var type = this; var currentTypeIndex = 0; @@ -466,7 +471,7 @@ private bool InheritsFrom(EntityType entityType) /// [DebuggerStepThrough] public virtual EntityType RootType() - => (EntityType)((IEntityType)this).GetRootType(); + => (EntityType)((IReadOnlyEntityType)this).GetRootType(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -779,7 +784,7 @@ private void UpdatePrimaryKeyConfigurationSource(ConfigurationSource configurati /// 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? FindKey([NotNull] IProperty property) + public virtual Key? FindKey([NotNull] IReadOnlyProperty property) => FindKey(new[] { property }); /// @@ -788,7 +793,7 @@ private void UpdatePrimaryKeyConfigurationSource(ConfigurationSource configurati /// 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? FindKey([NotNull] IReadOnlyList properties) + public virtual Key? FindKey([NotNull] IReadOnlyList properties) { Check.HasNoNulls(properties, nameof(properties)); Check.NotEmpty(properties, nameof(properties)); @@ -811,7 +816,7 @@ public virtual IEnumerable GetDeclaredKeys() /// 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? FindDeclaredKey([NotNull] IReadOnlyList properties) + public virtual Key? FindDeclaredKey([NotNull] IReadOnlyList properties) => _keys.TryGetValue(Check.NotEmpty(properties, nameof(properties)), out var key) ? key : null; @@ -822,7 +827,7 @@ public virtual IEnumerable GetDeclaredKeys() /// 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? RemoveKey([NotNull] IReadOnlyList properties) + public virtual Key? RemoveKey([NotNull] IReadOnlyList properties) { Check.NotEmpty(properties, nameof(properties)); @@ -1043,7 +1048,7 @@ public virtual void OnForeignKeyUpdated([NotNull] ForeignKey foreignKey) /// 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 FindForeignKeys([NotNull] IProperty property) + public virtual IEnumerable FindForeignKeys([NotNull] IReadOnlyProperty property) => FindForeignKeys(new[] { property }); /// @@ -1052,7 +1057,7 @@ public virtual IEnumerable FindForeignKeys([NotNull] IProperty prope /// 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 FindForeignKeys([NotNull] IReadOnlyList properties) + public virtual IEnumerable FindForeignKeys([NotNull] IReadOnlyList properties) { Check.HasNoNulls(properties, nameof(properties)); Check.NotEmpty(properties, nameof(properties)); @@ -1071,9 +1076,9 @@ public virtual IEnumerable FindForeignKeys([NotNull] IReadOnlyList public virtual ForeignKey? FindForeignKey( - [NotNull] IProperty property, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyProperty property, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) => FindForeignKey( new[] { property }, principalKey, principalEntityType); @@ -1084,9 +1089,9 @@ public virtual IEnumerable FindForeignKeys([NotNull] IReadOnlyList public virtual ForeignKey? FindForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) { Check.HasNoNulls(properties, nameof(properties)); Check.NotEmpty(properties, nameof(properties)); @@ -1174,7 +1179,7 @@ public virtual IEnumerable GetForeignKeys() /// 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 FindDeclaredForeignKeys([NotNull] IReadOnlyList properties) + public virtual IEnumerable FindDeclaredForeignKeys([NotNull] IReadOnlyList properties) { Check.NotEmpty(properties, nameof(properties)); @@ -1190,9 +1195,9 @@ public virtual IEnumerable FindDeclaredForeignKeys([NotNull] IReadOn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual ForeignKey? FindDeclaredForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) { Check.NotEmpty(properties, nameof(properties)); Check.NotNull(principalKey, nameof(principalKey)); @@ -1222,7 +1227,7 @@ public virtual IEnumerable FindDeclaredForeignKeys([NotNull] IReadOn /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindDerivedForeignKeys( - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => _directlyDerivedTypes.Count == 0 ? Enumerable.Empty() : GetDerivedTypes().SelectMany(et => et.FindDeclaredForeignKeys(properties)); @@ -1234,9 +1239,9 @@ public virtual IEnumerable FindDerivedForeignKeys( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindDerivedForeignKeys( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) => _directlyDerivedTypes.Count == 0 ? Enumerable.Empty() : (IEnumerable)GetDerivedTypes().Select(et => et.FindDeclaredForeignKey(properties, principalKey, principalEntityType)) @@ -1249,7 +1254,7 @@ public virtual IEnumerable FindDerivedForeignKeys( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindForeignKeysInHierarchy( - [NotNull] IReadOnlyList properties) + [NotNull] IReadOnlyList properties) => _directlyDerivedTypes.Count == 0 ? FindForeignKeys(properties) : FindForeignKeys(properties).Concat(FindDerivedForeignKeys(properties)); @@ -1261,9 +1266,9 @@ public virtual IEnumerable FindForeignKeysInHierarchy( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindForeignKeysInHierarchy( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) => _directlyDerivedTypes.Count == 0 ? ToEnumerable(FindForeignKey(properties, principalKey, principalEntityType)) : ToEnumerable(FindForeignKey(properties, principalKey, principalEntityType)) @@ -1276,9 +1281,9 @@ public virtual IEnumerable FindForeignKeysInHierarchy( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual ForeignKey? RemoveForeignKey( - [NotNull] IReadOnlyList properties, - [NotNull] IKey principalKey, - [NotNull] IEntityType principalEntityType) + [NotNull] IReadOnlyList properties, + [NotNull] IReadOnlyKey principalKey, + [NotNull] IReadOnlyEntityType principalEntityType) { Check.NotEmpty(properties, nameof(properties)); @@ -1506,11 +1511,7 @@ private Navigation AddNavigation(MemberIdentity navigationMember, ForeignKey for /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual Navigation? FindNavigation([NotNull] string name) - { - Check.NotEmpty(name, nameof(name)); - - return FindDeclaredNavigation(name) ?? _baseType?.FindNavigation(name); - } + => (Navigation?)((IReadOnlyEntityType)this).FindNavigation(name); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1519,7 +1520,7 @@ private Navigation AddNavigation(MemberIdentity navigationMember, ForeignKey for /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual Navigation? FindNavigation([NotNull] MemberInfo memberInfo) - => FindNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); + => (Navigation?)((IReadOnlyEntityType)this).FindNavigation(Check.NotNull(memberInfo, nameof(memberInfo)).GetSimpleMemberName()); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -2048,7 +2049,7 @@ private void UpdatePropertyIndexes(IReadOnlyList properties, Index ind /// 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 Index? FindIndex([NotNull] IProperty property) + public virtual Index? FindIndex([NotNull] IReadOnlyProperty property) => FindIndex(new[] { property }); /// @@ -2057,7 +2058,7 @@ private void UpdatePropertyIndexes(IReadOnlyList properties, Index ind /// 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 Index? FindIndex([NotNull] IReadOnlyList properties) + public virtual Index? FindIndex([NotNull] IReadOnlyList properties) { Check.HasNoNulls(properties, nameof(properties)); Check.NotEmpty(properties, nameof(properties)); @@ -2106,7 +2107,7 @@ public virtual IEnumerable GetDerivedIndexes() /// 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 Index? FindDeclaredIndex([NotNull] IReadOnlyList properties) + public virtual Index? FindDeclaredIndex([NotNull] IReadOnlyList properties) => _unnamedIndexes.TryGetValue(Check.NotEmpty(properties, nameof(properties)), out var index) ? index : null; @@ -2128,7 +2129,7 @@ public virtual IEnumerable GetDerivedIndexes() /// 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 FindDerivedIndexes([NotNull] IReadOnlyList properties) + public virtual IEnumerable FindDerivedIndexes([NotNull] IReadOnlyList properties) => _directlyDerivedTypes.Count == 0 ? Enumerable.Empty() : (IEnumerable)GetDerivedTypes().Select(et => et.FindDeclaredIndex(properties)).Where(i => i != null); @@ -2152,7 +2153,7 @@ public virtual IEnumerable FindDerivedIndexes([NotNull] string name) /// 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 FindIndexesInHierarchy([NotNull] IReadOnlyList properties) + public virtual IEnumerable FindIndexesInHierarchy([NotNull] IReadOnlyList properties) => _directlyDerivedTypes.Count == 0 ? ToEnumerable(FindIndex(properties)) : ToEnumerable(FindIndex(properties)).Concat(FindDerivedIndexes(properties)); @@ -2174,7 +2175,7 @@ public virtual IEnumerable FindIndexesInHierarchy([NotNull] string name) /// 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 Index? RemoveIndex([NotNull] IReadOnlyList properties) + public virtual Index? RemoveIndex([NotNull] IReadOnlyList properties) { Check.NotEmpty(properties, nameof(properties)); @@ -2470,6 +2471,8 @@ public virtual IEnumerable FindPropertiesInHierarchy([NotNull] string /// public virtual IReadOnlyList? FindProperties([NotNull] IReadOnlyList propertyNames) { + Check.NotNull(propertyNames, nameof(propertyNames)); + var properties = new List(propertyNames.Count); foreach (var propertyName in propertyNames) { @@ -2708,9 +2711,9 @@ public virtual Func InstanceFactory /// 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 IReadOnlyList PropagatingProperties + public virtual IReadOnlyList ForeignKeyProperties => NonCapturingLazyInitializer.EnsureInitialized( - ref _propagatingProperties, this, + ref _foreignKeyProperties, this, static entityType => { entityType.EnsureReadOnly(); @@ -2724,9 +2727,9 @@ public virtual IReadOnlyList PropagatingProperties /// 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 IReadOnlyList GeneratingProperties + public virtual IReadOnlyList ValueGeneratingProperties => NonCapturingLazyInitializer.EnsureInitialized( - ref _generatingProperties, this, + ref _valueGeneratingProperties, this, static entityType => { entityType.EnsureReadOnly(); @@ -2970,7 +2973,7 @@ public virtual IEnumerable GetDeclaredServiceProperties() var data = new List>(); var valueConverters = new Dictionary(StringComparer.Ordinal); var properties = GetProperties() - .Concat(GetNavigations()) + .Concat(GetNavigations()) .Concat(GetSkipNavigations()) .ToDictionary(p => p.Name); foreach (var rawSeed in _data) @@ -2986,7 +2989,7 @@ public virtual IEnumerable GetDeclaredServiceProperties() { ValueConverter? valueConverter = null; if (providerValues - && propertyBase is IProperty property + && propertyBase is IReadOnlyProperty property && !valueConverters.TryGetValue(propertyBase.Name, out valueConverter)) { valueConverter = property.GetTypeMapping().Converter; @@ -3040,7 +3043,7 @@ public virtual IEnumerable GetDeclaredServiceProperties() if (providerValues && !valueConverters.TryGetValue(propertyBase.Name, out valueConverter)) { - if (propertyBase is IProperty property) + if (propertyBase is IReadOnlyProperty property) { valueConverter = property.GetTypeMapping().Converter; } @@ -3280,7 +3283,7 @@ private void CheckDiscriminatorProperty(Property? property) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual void CheckDiscriminatorValue([NotNull] IEntityType entityType, [CanBeNull] object? value) + public virtual void CheckDiscriminatorValue([NotNull] IReadOnlyEntityType entityType, [CanBeNull] object? value) { if (value is null) { @@ -3345,7 +3348,7 @@ 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. /// - IModel ITypeBase.Model + IReadOnlyModel IReadOnlyTypeBase.Model { [DebuggerStepThrough] get => Model; @@ -3393,7 +3396,19 @@ IConventionModel IConventionEntityType.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. /// - IEntityType? IEntityType.BaseType + IModel ITypeBase.Model + { + [DebuggerStepThrough] + get => 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. + /// + IReadOnlyEntityType? IReadOnlyEntityType.BaseType { [DebuggerStepThrough] get => _baseType; @@ -3423,6 +3438,28 @@ IConventionModel IConventionEntityType.Model get => BaseType; } + /// + /// 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. + /// + IEntityType? IEntityType.BaseType + { + [DebuggerStepThrough] + get => BaseType; + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDirectlyDerivedTypes() + => GetDirectlyDerivedTypes(); + /// /// 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 @@ -3472,7 +3509,7 @@ IConventionModel IConventionEntityType.Model /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IKey? IEntityType.FindPrimaryKey() + IReadOnlyKey? IReadOnlyEntityType.FindPrimaryKey() => FindPrimaryKey(); /// @@ -3495,6 +3532,16 @@ IConventionModel IConventionEntityType.Model IConventionKey? IConventionEntityType.FindPrimaryKey() => FindPrimaryKey(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IKey? IEntityType.FindPrimaryKey() + => FindPrimaryKey(); + /// /// 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 @@ -3524,7 +3571,7 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IKey? IEntityType.FindKey(IReadOnlyList properties) + IReadOnlyKey? IReadOnlyEntityType.FindKey(IReadOnlyList properties) => FindKey(properties); /// @@ -3534,7 +3581,7 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IMutableKey? IMutableEntityType.FindKey(IReadOnlyList properties) + IMutableKey? IMutableEntityType.FindKey(IReadOnlyList properties) => FindKey(properties); /// @@ -3544,7 +3591,7 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IConventionKey? IConventionEntityType.FindKey(IReadOnlyList properties) + IConventionKey? IConventionEntityType.FindKey(IReadOnlyList properties) => FindKey(properties); /// @@ -3554,7 +3601,27 @@ IMutableKey IMutableEntityType.AddKey(IReadOnlyList properties /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetKeys() + IKey? IEntityType.FindKey(IReadOnlyList properties) + => FindKey(properties); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredKeys() + => GetDeclaredKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IReadOnlyEntityType.GetKeys() => GetKeys(); /// @@ -3577,6 +3644,16 @@ IEnumerable IMutableEntityType.GetKeys() IEnumerable IConventionEntityType.GetKeys() => GetKeys(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetKeys() + => GetKeys(); + /// /// 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 @@ -3644,10 +3721,10 @@ IMutableForeignKey IMutableEntityType.AddForeignKey( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IForeignKey? IEntityType.FindForeignKey( - IReadOnlyList properties, - IKey principalKey, - IEntityType principalEntityType) + IReadOnlyForeignKey? IReadOnlyEntityType.FindForeignKey( + IReadOnlyList properties, + IReadOnlyKey principalKey, + IReadOnlyEntityType principalEntityType) => FindForeignKey(properties, principalKey, principalEntityType); /// @@ -3658,9 +3735,9 @@ IMutableForeignKey IMutableEntityType.AddForeignKey( /// [DebuggerStepThrough] IMutableForeignKey? IMutableEntityType.FindForeignKey( - IReadOnlyList properties, - IKey principalKey, - IEntityType principalEntityType) + IReadOnlyList properties, + IReadOnlyKey principalKey, + IReadOnlyEntityType principalEntityType) => FindForeignKey(properties, principalKey, principalEntityType); /// @@ -3671,9 +3748,9 @@ IMutableForeignKey IMutableEntityType.AddForeignKey( /// [DebuggerStepThrough] IConventionForeignKey? IConventionEntityType.FindForeignKey( - IReadOnlyList properties, - IKey principalKey, - IEntityType principalEntityType) + IReadOnlyList properties, + IReadOnlyKey principalKey, + IReadOnlyEntityType principalEntityType) => FindForeignKey(properties, principalKey, principalEntityType); /// @@ -3683,7 +3760,40 @@ IMutableForeignKey IMutableEntityType.AddForeignKey( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetForeignKeys() + IForeignKey? IEntityType.FindForeignKey( + IReadOnlyList properties, + IReadOnlyKey principalKey, + IReadOnlyEntityType principalEntityType) + => FindForeignKey(properties, principalKey, principalEntityType); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.FindForeignKeys(IReadOnlyList properties) + => FindForeignKeys(properties); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.FindDeclaredForeignKeys(IReadOnlyList properties) + => FindDeclaredForeignKeys(properties); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IReadOnlyEntityType.GetForeignKeys() => GetForeignKeys(); /// @@ -3706,6 +3816,56 @@ IEnumerable IMutableEntityType.GetForeignKeys() IEnumerable IConventionEntityType.GetForeignKeys() => GetForeignKeys(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetForeignKeys() + => GetForeignKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredForeignKeys() + => GetDeclaredForeignKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDerivedForeignKeys() + => GetDerivedForeignKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredReferencingForeignKeys() + => GetDeclaredReferencingForeignKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetReferencingForeignKeys() + => GetReferencingForeignKeys(); + /// /// 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 @@ -3726,6 +3886,26 @@ IEnumerable IConventionEntityType.GetForeignKeys() IConventionForeignKey? IConventionEntityType.RemoveForeignKey(IConventionForeignKey foreignKey) => RemoveForeignKey((ForeignKey)foreignKey); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredNavigations() + => GetDeclaredNavigations(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + INavigation? IEntityType.FindDeclaredNavigation(string name) + => FindDeclaredNavigation(name); + /// /// 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 @@ -3768,7 +3948,7 @@ IMutableSkipNavigation IMutableEntityType.AddSkipNavigation( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - ISkipNavigation? IEntityType.FindSkipNavigation(MemberInfo memberInfo) + IReadOnlySkipNavigation? IReadOnlyEntityType.FindSkipNavigation(MemberInfo memberInfo) => FindSkipNavigation(memberInfo); /// @@ -3778,7 +3958,7 @@ IMutableSkipNavigation IMutableEntityType.AddSkipNavigation( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - ISkipNavigation? IEntityType.FindSkipNavigation(string name) + IReadOnlySkipNavigation? IReadOnlyEntityType.FindSkipNavigation(string name) => FindSkipNavigation(name); /// @@ -3808,7 +3988,17 @@ IMutableSkipNavigation IMutableEntityType.AddSkipNavigation( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - ISkipNavigation? IEntityType.FindDeclaredSkipNavigation(string name) + ISkipNavigation? IEntityType.FindSkipNavigation(string name) + => FindSkipNavigation(name); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IReadOnlySkipNavigation? IReadOnlyEntityType.FindDeclaredSkipNavigation(string name) => FindDeclaredSkipNavigation(name); /// @@ -3818,7 +4008,7 @@ IMutableSkipNavigation IMutableEntityType.AddSkipNavigation( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetDeclaredSkipNavigations() + IEnumerable IReadOnlyEntityType.GetDeclaredSkipNavigations() => GetDeclaredSkipNavigations(); /// @@ -3828,7 +4018,7 @@ IEnumerable IEntityType.GetDeclaredSkipNavigations() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetSkipNavigations() + IEnumerable IReadOnlyEntityType.GetSkipNavigations() => GetSkipNavigations(); /// @@ -3851,6 +4041,16 @@ IEnumerable IMutableEntityType.GetSkipNavigations() IEnumerable IConventionEntityType.GetSkipNavigations() => GetSkipNavigations(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetSkipNavigations() + => GetSkipNavigations(); + /// /// 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 @@ -3926,7 +4126,7 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IIndex? IEntityType.FindIndex(IReadOnlyList properties) + IReadOnlyIndex? IReadOnlyEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties); /// @@ -3936,8 +4136,8 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IIndex? IEntityType.FindIndex(string name) - => FindIndex(name); + IMutableIndex? IMutableEntityType.FindIndex(IReadOnlyList properties) + => FindIndex(properties); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -3946,7 +4146,7 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IMutableIndex? IMutableEntityType.FindIndex(IReadOnlyList properties) + IConventionIndex? IConventionEntityType.FindIndex(IReadOnlyList properties) => FindIndex(properties); /// @@ -3956,7 +4156,17 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IMutableIndex? IMutableEntityType.FindIndex(string name) + IIndex? IEntityType.FindIndex(IReadOnlyList properties) + => FindIndex(properties); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IReadOnlyIndex? IReadOnlyEntityType.FindIndex(string name) => FindIndex(name); /// @@ -3966,8 +4176,8 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IConventionIndex? IConventionEntityType.FindIndex(IReadOnlyList properties) - => FindIndex(properties); + IMutableIndex? IMutableEntityType.FindIndex(string name) + => FindIndex(name); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -3986,7 +4196,37 @@ IMutableIndex IMutableEntityType.AddIndex(IReadOnlyList proper /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetIndexes() + IIndex? IEntityType.FindIndex(string name) + => FindIndex(name); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredIndexes() + => GetDeclaredIndexes(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDerivedIndexes() + => GetDerivedIndexes(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IReadOnlyEntityType.GetIndexes() => GetIndexes(); /// @@ -4009,6 +4249,16 @@ IEnumerable IMutableEntityType.GetIndexes() IEnumerable IConventionEntityType.GetIndexes() => GetIndexes(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetIndexes() + => GetIndexes(); + /// /// 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 @@ -4070,7 +4320,17 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType, /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IProperty? IEntityType.FindProperty(string name) + IProperty? IEntityType.FindDeclaredProperty(string name) + => FindDeclaredProperty(name); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IReadOnlyProperty? IReadOnlyEntityType.FindProperty(string name) => FindProperty(name); /// @@ -4100,7 +4360,27 @@ IMutableProperty IMutableEntityType.AddProperty(string name, Type propertyType, /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetProperties() + IProperty? IEntityType.FindProperty(string name) + => FindProperty(name); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredProperties() + => GetDeclaredProperties(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IReadOnlyEntityType.GetProperties() => GetProperties(); /// @@ -4123,6 +4403,36 @@ IEnumerable IMutableEntityType.GetProperties() IEnumerable IConventionEntityType.GetProperties() => GetProperties(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetProperties() + => GetProperties(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetForeignKeyProperties() + => ForeignKeyProperties; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetValueGeneratingProperties() + => ValueGeneratingProperties; + /// /// 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 @@ -4170,7 +4480,7 @@ IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo m /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IServiceProperty? IEntityType.FindServiceProperty(string name) + IReadOnlyServiceProperty? IReadOnlyEntityType.FindServiceProperty(string name) => FindServiceProperty(name); /// @@ -4200,7 +4510,27 @@ IConventionServiceProperty IConventionEntityType.AddServiceProperty(MemberInfo m /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - IEnumerable IEntityType.GetServiceProperties() + IServiceProperty? IEntityType.FindServiceProperty(string name) + => FindServiceProperty(name); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetDeclaredServiceProperties() + => GetDeclaredServiceProperties(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IReadOnlyEntityType.GetServiceProperties() => GetServiceProperties(); /// @@ -4223,6 +4553,16 @@ IEnumerable IMutableEntityType.GetServiceProperties() IEnumerable IConventionEntityType.GetServiceProperties() => GetServiceProperties(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IEntityType.GetServiceProperties() + => GetServiceProperties(); + /// /// 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/EntityTypeExtensions.cs b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs index 0e4315b4d3a..0999cfc9a8d 100644 --- a/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs +++ b/src/EFCore/Metadata/Internal/EntityTypeExtensions.cs @@ -34,7 +34,7 @@ public static class EntityTypeExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static MemberInfo GetNavigationMemberInfo( - [NotNull] this IEntityType entityType, + [NotNull] this IReadOnlyEntityType entityType, [NotNull] string navigationName) { var memberInfo = entityType.ClrType.GetMembersInHierarchy(navigationName).FirstOrDefault(); @@ -54,7 +54,7 @@ public static MemberInfo GetNavigationMemberInfo( /// 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 IForeignKey? FindDeclaredOwnership([NotNull] this IEntityType entityType) + public static IReadOnlyForeignKey? FindDeclaredOwnership([NotNull] this IReadOnlyEntityType entityType) => ((EntityType)entityType).FindDeclaredOwnership(); /// @@ -63,7 +63,7 @@ public static MemberInfo GetNavigationMemberInfo( /// 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 IEntityType? FindInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] Type targetType) + public static IReadOnlyEntityType? FindInOwnershipPath([NotNull] this IReadOnlyEntityType entityType, [NotNull] Type targetType) { if (entityType.ClrType == targetType) { @@ -93,7 +93,7 @@ public static MemberInfo GetNavigationMemberInfo( /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [NotNull] Type targetType) + public static bool IsInOwnershipPath([NotNull] this IReadOnlyEntityType entityType, [NotNull] Type targetType) => entityType.FindInOwnershipPath(targetType) != null; /// @@ -103,7 +103,7 @@ public static bool IsInOwnershipPath([NotNull] this IEntityType entityType, [Not /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [DebuggerStepThrough] - public static string GetOwnedName([NotNull] this ITypeBase type, [NotNull] string simpleName, [NotNull] string ownershipNavigation) + public static string GetOwnedName([NotNull] this IReadOnlyTypeBase type, [NotNull] string simpleName, [NotNull] string ownershipNavigation) => type.Name + "." + ownershipNavigation + "#" + simpleName; /// @@ -112,7 +112,7 @@ public static string GetOwnedName([NotNull] this ITypeBase type, [NotNull] strin /// 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 UseEagerSnapshots([NotNull] this IEntityType entityType) + public static bool UseEagerSnapshots([NotNull] this IReadOnlyEntityType entityType) { var changeTrackingStrategy = entityType.GetChangeTrackingStrategy(); @@ -230,7 +230,7 @@ public static PropertyCounts CalculateCounts([NotNull] this EntityType entityTyp index: navigationIndex++, originalValueIndex: -1, shadowIndex: navigation.IsShadowProperty() ? shadowIndex++ : -1, - relationshipIndex: ((INavigationBase)navigation).IsCollection && isNotifying ? -1 : relationshipIndex++, + relationshipIndex: ((IReadOnlyNavigationBase)navigation).IsCollection && isNotifying ? -1 : relationshipIndex++, storeGenerationIndex: -1); navigation.PropertyIndexes = indexes; @@ -275,24 +275,6 @@ public static Func GetInstanceFactory([NotNull] public static Func GetEmptyShadowValuesFactory([NotNull] this IEntityType entityType) => entityType.AsEntityType().EmptyShadowValuesFactory; - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public static IReadOnlyList GetPropagatingProperties([NotNull] this IEntityType entityType) - => entityType.AsEntityType().PropagatingProperties; - - /// - /// 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 IReadOnlyList GetGeneratingProperties([NotNull] this IEntityType entityType) - => entityType.AsEntityType().GeneratingProperties; - /// /// 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 @@ -300,7 +282,7 @@ public static IReadOnlyList GetGeneratingProperties([NotNull] this IE /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static EntityType? LeastDerivedType([NotNull] this EntityType entityType, [NotNull] EntityType otherEntityType) - => (EntityType?)((IEntityType)entityType).LeastDerivedType(otherEntityType); + => (EntityType?)((IReadOnlyEntityType)entityType).LeastDerivedType(otherEntityType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -308,7 +290,7 @@ public static IReadOnlyList GetGeneratingProperties([NotNull] this IE /// 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 IKey? FindDeclaredPrimaryKey([NotNull] this IEntityType entityType) + public static IReadOnlyKey? FindDeclaredPrimaryKey([NotNull] this IReadOnlyEntityType entityType) => entityType.BaseType == null ? entityType.FindPrimaryKey() : null; /// @@ -317,8 +299,8 @@ public static IReadOnlyList GetGeneratingProperties([NotNull] this IE /// 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 FindDerivedNavigations( - [NotNull] this IEntityType entityType, + public static IEnumerable FindDerivedNavigations( + [NotNull] this IReadOnlyEntityType entityType, [NotNull] string navigationName) => entityType.GetDerivedTypes().SelectMany( et => et.GetDeclaredNavigations().Where(navigation => navigationName == navigation.Name)); @@ -329,7 +311,7 @@ public static IEnumerable FindDerivedNavigations( /// 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 GetDerivedNavigations([NotNull] this IEntityType entityType) + public static IEnumerable GetDerivedNavigations([NotNull] this IEntityType entityType) => entityType.AsEntityType().GetDerivedNavigations(); /// @@ -338,7 +320,7 @@ public static IEnumerable GetDerivedNavigations([NotNull] this IEnti /// 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 GetDerivedSkipNavigations([NotNull] this IEntityType entityType) + public static IEnumerable GetDerivedSkipNavigations([NotNull] this IEntityType entityType) => entityType.AsEntityType().GetDerivedSkipNavigations(); /// @@ -357,49 +339,8 @@ public static IEnumerable GetPropertiesAndNavigations( /// 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 GetNotificationProperties( - [NotNull] this IEntityType entityType, - [CanBeNull] string? propertyName) - { - if (string.IsNullOrEmpty(propertyName)) - { - foreach (var property in entityType.GetProperties() - .Where(p => p.GetAfterSaveBehavior() == PropertySaveBehavior.Save)) - { - yield return property; - } - - foreach (var navigation in entityType.GetNavigations()) - { - yield return navigation; - } - - foreach (var navigation in entityType.GetSkipNavigations()) - { - yield return navigation; - } - } - else - { - // ReSharper disable once AssignNullToNotNullAttribute - var property = entityType.FindProperty(propertyName) - ?? entityType.FindNavigation(propertyName) - ?? (IPropertyBase?)entityType.FindSkipNavigation(propertyName); - - if (property != null) - { - yield return property; - } - } - } - - /// - /// 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 IProperty CheckPropertyBelongsToType([NotNull] this IEntityType entityType, [NotNull] IProperty property) + public static IProperty CheckPropertyBelongsToType( + [NotNull] this IEntityType entityType, [NotNull] IProperty property) { Check.NotNull(property, nameof(property)); @@ -418,33 +359,7 @@ public static IProperty CheckPropertyBelongsToType([NotNull] this IEntityType en /// 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 EntityType AsEntityType([NotNull] this IEntityType entityType, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(entityType, methodName); - - - - /// - /// Try to find the name of the type that has a different namespace from your entity types - /// - /// - /// model Of your context to find entities - /// - /// - /// A Type that you think have a different namespace and exists in your entity types - /// - /// - /// the name with different namespace found - /// - public static string? FindSameTypeNameWithDifferentNamespace([NotNull] this IModel model, [NotNull] Type type) - { - //try to find the same name of the entity with type to throw a specific exception - return model.GetEntityTypes() - //check the short names are equals because the namespaces are not equaled we need to check just names are equals - .Where(x => x.ClrType.DisplayName(false) == type.DisplayName(false)) - //select the full name of type because we need to show the developer a type with the full name - .Select(x => x.ClrType.DisplayName()) - //select one of them to show the developer - .FirstOrDefault(); - } + public static EntityType AsEntityType([NotNull] this IReadOnlyEntityType entityType, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(entityType, methodName); } } diff --git a/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs index b0a5e464035..10d65369dbb 100644 --- a/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs +++ b/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs @@ -43,7 +43,8 @@ public virtual bool CanBind( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual ParameterBinding Bind(IMutableEntityType entityType, Type parameterType, string parameterName) - => new EntityTypeParameterBinding(entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); + => new EntityTypeParameterBinding( + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -55,6 +56,7 @@ public virtual ParameterBinding Bind( IConventionEntityType entityType, Type parameterType, string parameterName) - => new EntityTypeParameterBinding(entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); + => new EntityTypeParameterBinding( + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType)); } } diff --git a/src/EFCore/Metadata/Internal/ForeignKey.cs b/src/EFCore/Metadata/Internal/ForeignKey.cs index 54d9f35b66f..06bfbdc0045 100644 --- a/src/EFCore/Metadata/Internal/ForeignKey.cs +++ b/src/EFCore/Metadata/Internal/ForeignKey.cs @@ -12,7 +12,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Utilities; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -24,7 +23,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 ForeignKey : ConventionAnnotatable, IMutableForeignKey, IConventionForeignKey + public class ForeignKey : ConventionAnnotatable, IMutableForeignKey, IConventionForeignKey, IForeignKey { private DeleteBehavior? _deleteBehavior; private bool? _isUnique; @@ -152,7 +151,7 @@ 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. /// - protected override bool IsReadOnly => DeclaringEntityType.Model.IsModelReadOnly; + public override bool IsReadOnly => DeclaringEntityType.Model.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -849,7 +848,7 @@ public virtual void UpdateIsOwnershipConfigurationSource(ConfigurationSource con /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindNavigationsFromInHierarchy([NotNull] EntityType entityType) - => ((IForeignKey)this).FindNavigationsFromInHierarchy(entityType).Cast(); + => ((IReadOnlyForeignKey)this).FindNavigationsFromInHierarchy(entityType).Cast(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -858,7 +857,7 @@ public virtual IEnumerable FindNavigationsFromInHierarchy([NotNull] /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IEnumerable FindNavigationsTo([NotNull] EntityType entityType) - => ((IForeignKey)this).FindNavigationsTo(entityType).Cast(); + => ((IReadOnlyForeignKey)this).FindNavigationsTo(entityType).Cast(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -867,7 +866,7 @@ public virtual IEnumerable FindNavigationsTo([NotNull] EntityType en /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual EntityType ResolveOtherEntityType([NotNull] EntityType entityType) - => (EntityType)((IForeignKey)this).GetRelatedEntityType(entityType); + => (EntityType)((IReadOnlyForeignKey)this).GetRelatedEntityType(entityType); // Note: This is set and used only by IdentityMapFactoryFactory, which ensures thread-safety /// @@ -931,7 +930,7 @@ public virtual Func DependentsMapFactory /// 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 IForeignKey.Properties + IReadOnlyList IReadOnlyForeignKey.Properties { [DebuggerStepThrough] get => Properties; @@ -943,7 +942,7 @@ IReadOnlyList IForeignKey.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. /// - IKey IForeignKey.PrincipalKey + IReadOnlyKey IReadOnlyForeignKey.PrincipalKey { [DebuggerStepThrough] get => PrincipalKey; @@ -955,7 +954,7 @@ IKey IForeignKey.PrincipalKey /// 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 IForeignKey.DeclaringEntityType + IReadOnlyEntityType IReadOnlyForeignKey.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; @@ -967,7 +966,7 @@ IEntityType IForeignKey.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 IForeignKey.PrincipalEntityType + IReadOnlyEntityType IReadOnlyForeignKey.PrincipalEntityType { [DebuggerStepThrough] get => PrincipalEntityType; @@ -979,7 +978,7 @@ IEntityType IForeignKey.PrincipalEntityType /// 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. /// - INavigation? IForeignKey.DependentToPrincipal + IReadOnlyNavigation? IReadOnlyForeignKey.DependentToPrincipal { [DebuggerStepThrough] get => DependentToPrincipal; @@ -991,7 +990,7 @@ IEntityType IForeignKey.PrincipalEntityType /// 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. /// - INavigation? IForeignKey.PrincipalToDependent + IReadOnlyNavigation? IReadOnlyForeignKey.PrincipalToDependent { [DebuggerStepThrough] get => PrincipalToDependent; @@ -1133,6 +1132,18 @@ IConventionEntityType IConventionForeignKey.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. + /// + IEntityType IForeignKey.DeclaringEntityType + { + [DebuggerStepThrough] + 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 @@ -1145,6 +1156,18 @@ IConventionEntityType IConventionForeignKey.PrincipalEntityType get => PrincipalEntityType; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IEntityType IForeignKey.PrincipalEntityType + { + [DebuggerStepThrough] + get => PrincipalEntityType; + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -1157,6 +1180,18 @@ IConventionKey IConventionForeignKey.PrincipalKey get => PrincipalKey; } + /// + /// 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. + /// + IKey IForeignKey.PrincipalKey + { + [DebuggerStepThrough] + get => PrincipalKey; + } + /// /// 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 @@ -1169,6 +1204,18 @@ IReadOnlyList IConventionForeignKey.Properties get => Properties; } + /// + /// 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. + /// + IReadOnlyList IForeignKey.Properties + { + [DebuggerStepThrough] + get => Properties; + } + /// /// 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 @@ -1181,6 +1228,18 @@ IReadOnlyList IConventionForeignKey.Properties get => DependentToPrincipal; } + /// + /// 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. + /// + INavigation? IForeignKey.DependentToPrincipal + { + [DebuggerStepThrough] + get => DependentToPrincipal; + } + /// /// 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 @@ -1193,6 +1252,18 @@ IReadOnlyList IConventionForeignKey.Properties get => PrincipalToDependent; } + /// + /// 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. + /// + INavigation? IForeignKey.PrincipalToDependent + { + [DebuggerStepThrough] + get => PrincipalToDependent; + } + /// /// 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 @@ -1249,7 +1320,7 @@ IReadOnlyList IConventionForeignKey.SetProperties( /// [DebuggerStepThrough] - IEnumerable IForeignKey.GetReferencingSkipNavigations() + IEnumerable IReadOnlyForeignKey.GetReferencingSkipNavigations() => GetReferencingSkipNavigations(); /// @@ -1366,8 +1437,8 @@ public static bool AreCompatible( [NotNull] EntityType dependentEntityType, [CanBeNull] MemberInfo? navigationToPrincipal, [CanBeNull] MemberInfo? navigationToDependent, - [CanBeNull] IReadOnlyList? dependentProperties, - [CanBeNull] IReadOnlyList? principalProperties, + [CanBeNull] IReadOnlyList? dependentProperties, + [CanBeNull] IReadOnlyList? principalProperties, bool? unique, bool shouldThrow) { @@ -1415,10 +1486,10 @@ public static bool AreCompatible( /// 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] IEntityType principalEntityType, - [NotNull] IEntityType dependentEntityType, + [NotNull] IReadOnlyList principalProperties, + [NotNull] IReadOnlyList dependentProperties, + [NotNull] IReadOnlyEntityType principalEntityType, + [NotNull] IReadOnlyEntityType dependentEntityType, bool shouldThrow) { Check.NotNull(principalProperties, nameof(principalProperties)); @@ -1460,13 +1531,13 @@ public static bool AreCompatible( } private static bool ArePropertyCountsEqual( - IReadOnlyList principalProperties, - IReadOnlyList dependentProperties) + IReadOnlyList principalProperties, + IReadOnlyList dependentProperties) => principalProperties.Count == dependentProperties.Count; private static bool ArePropertyTypesCompatible( - IReadOnlyList principalProperties, - IReadOnlyList dependentProperties) + IReadOnlyList principalProperties, + IReadOnlyList dependentProperties) => principalProperties.Select(p => p.ClrType.UnwrapNullableType()).SequenceEqual( dependentProperties.Select(p => p.ClrType.UnwrapNullableType())); } diff --git a/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs b/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs index 0cce91d9e28..e797b11735c 100644 --- a/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs +++ b/src/EFCore/Metadata/Internal/ForeignKeyExtensions.cs @@ -26,7 +26,7 @@ public static class ForeignKeyExtensions /// 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 IsSelfReferencing([NotNull] this IForeignKey foreignKey) + public static bool IsSelfReferencing([NotNull] this IReadOnlyForeignKey foreignKey) => foreignKey.DeclaringEntityType == foreignKey.PrincipalEntityType; /// @@ -35,7 +35,7 @@ public static bool IsSelfReferencing([NotNull] this IForeignKey foreignKey) /// 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 GetNavigations([NotNull] this IForeignKey foreignKey) + public static IEnumerable GetNavigations([NotNull] this IReadOnlyForeignKey foreignKey) { if (foreignKey.PrincipalToDependent != null) { @@ -54,9 +54,9 @@ public static IEnumerable GetNavigations([NotNull] this IForeignKey /// 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 FindNavigationsFrom( - [NotNull] this IForeignKey foreignKey, - [NotNull] IEntityType entityType) + public static IEnumerable FindNavigationsFrom( + [NotNull] this IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyEntityType entityType) { if (foreignKey.DeclaringEntityType != entityType && foreignKey.PrincipalEntityType != entityType) @@ -79,9 +79,9 @@ public static IEnumerable FindNavigationsFrom( /// 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 FindNavigationsFromInHierarchy( - [NotNull] this IForeignKey foreignKey, - [NotNull] IEntityType entityType) + public static IEnumerable FindNavigationsFromInHierarchy( + [NotNull] this IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyEntityType entityType) { if (!foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) && !foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)) @@ -105,7 +105,8 @@ public static IEnumerable FindNavigationsFromInHierarchy( /// 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 FindNavigationsTo([NotNull] this IForeignKey foreignKey, [NotNull] IEntityType entityType) + public static IEnumerable FindNavigationsTo( + [NotNull] this IReadOnlyForeignKey foreignKey, [NotNull] IReadOnlyEntityType entityType) { if (foreignKey.DeclaringEntityType != entityType && foreignKey.PrincipalEntityType != entityType) @@ -128,9 +129,9 @@ public static IEnumerable FindNavigationsTo([NotNull] this IForeign /// 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 FindNavigationsToInHierarchy( - [NotNull] this IForeignKey foreignKey, - [NotNull] IEntityType entityType) + public static IEnumerable FindNavigationsToInHierarchy( + [NotNull] this IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyEntityType entityType) { if (!foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) && !foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)) @@ -147,8 +148,8 @@ public static IEnumerable FindNavigationsToInHierarchy( : foreignKey.FindNavigations(foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)); } - private static IEnumerable FindNavigations( - this IForeignKey foreignKey, + private static IEnumerable FindNavigations( + this IReadOnlyForeignKey foreignKey, bool toPrincipal) { if (toPrincipal) @@ -174,9 +175,9 @@ private static IEnumerable FindNavigations( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [Obsolete] - public static IEntityType ResolveOtherEntityTypeInHierarchy( - [NotNull] this IForeignKey foreignKey, - [NotNull] IEntityType entityType) + public static IReadOnlyEntityType ResolveOtherEntityTypeInHierarchy( + [NotNull] this IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyEntityType entityType) { if (!foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) && !foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)) @@ -211,7 +212,7 @@ public static IEntityType ResolveOtherEntityTypeInHierarchy( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [Obsolete] - public static IEntityType ResolveEntityTypeInHierarchy([NotNull] this IForeignKey foreignKey, [NotNull] IEntityType entityType) + public static IReadOnlyEntityType ResolveEntityTypeInHierarchy([NotNull] this IReadOnlyForeignKey foreignKey, [NotNull] IReadOnlyEntityType entityType) { if (!foreignKey.DeclaringEntityType.IsAssignableFrom(entityType) && !foreignKey.PrincipalEntityType.IsAssignableFrom(entityType)) @@ -244,7 +245,7 @@ public static IEntityType ResolveEntityTypeInHierarchy([NotNull] this IForeignKe /// 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 IDependentsMap CreateDependentsMapFactory([NotNull] this IForeignKey foreignKey) + public static IDependentsMap CreateDependentsMapFactory([NotNull] this IReadOnlyForeignKey foreignKey) => foreignKey.AsForeignKey().DependentsMapFactory(); /// @@ -253,7 +254,7 @@ public static IDependentsMap CreateDependentsMapFactory([NotNull] this IForeignK /// 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 ForeignKey AsForeignKey([NotNull] this IForeignKey foreignKey, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(foreignKey, methodName); + public static ForeignKey AsForeignKey([NotNull] this IReadOnlyForeignKey foreignKey, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(foreignKey, methodName); } } diff --git a/src/EFCore/Metadata/Internal/Index.cs b/src/EFCore/Metadata/Internal/Index.cs index 24634905937..78fb778c0a9 100644 --- a/src/EFCore/Metadata/Internal/Index.cs +++ b/src/EFCore/Metadata/Internal/Index.cs @@ -23,7 +23,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 Index : ConventionAnnotatable, IMutableIndex, IConventionIndex + public class Index : ConventionAnnotatable, IMutableIndex, IConventionIndex, IIndex { private bool? _isUnique; private InternalIndexBuilder? _builder; @@ -133,7 +133,7 @@ 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. /// - protected override bool IsReadOnly => DeclaringEntityType.Model.IsModelReadOnly; + public override bool IsReadOnly => DeclaringEntityType.Model.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -261,7 +261,7 @@ public virtual DebugView DebugView /// 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 IIndex.Properties + IReadOnlyList IReadOnlyIndex.Properties { [DebuggerStepThrough] get => Properties; } @@ -272,7 +272,7 @@ IReadOnlyList IIndex.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. /// - IEntityType IIndex.DeclaringEntityType + IReadOnlyEntityType IReadOnlyIndex.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; } @@ -332,6 +332,18 @@ IReadOnlyList IConventionIndex.Properties [DebuggerStepThrough] get => Properties; } + /// + /// 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. + /// + IReadOnlyList IIndex.Properties + { + [DebuggerStepThrough] + get => Properties; + } + /// /// 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 @@ -343,6 +355,18 @@ IConventionEntityType IConventionIndex.DeclaringEntityType [DebuggerStepThrough] 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. + /// + IEntityType IIndex.DeclaringEntityType + { + [DebuggerStepThrough] + 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 diff --git a/src/EFCore/Metadata/Internal/IndexExtensions.cs b/src/EFCore/Metadata/Internal/IndexExtensions.cs index d459e9479e5..f99b66dc1e3 100644 --- a/src/EFCore/Metadata/Internal/IndexExtensions.cs +++ b/src/EFCore/Metadata/Internal/IndexExtensions.cs @@ -22,7 +22,7 @@ public static class IndexExtensions /// 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 Index AsIndex([NotNull] this IIndex index, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(index, methodName); + public static Index AsIndex([NotNull] this IReadOnlyIndex index, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(index, methodName); } } diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index d2fad8f4310..737f6a58531 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -1500,7 +1500,7 @@ public virtual bool CanSetDefiningQuery([CanBeNull] LambdaExpression? query, Con && navigationToDependent.GetConfigurationSource() == ConfigurationSource.Explicit) { var baseProperty = baseEntityType.FindMembersInHierarchy(navigationToDependent.Name).Single(); - if (!(baseProperty is INavigation)) + if (!(baseProperty is IReadOnlyNavigation)) { throw new InvalidOperationException( CoreStrings.DuplicatePropertiesOnBase( @@ -1881,25 +1881,25 @@ public virtual bool CanSetBaseType([CanBeNull] EntityType? baseEntityType, Confi if (derivedMember.GetConfigurationSource() == ConfigurationSource.Explicit && baseMembers.TryGetValue(derivedMember.Name, out var baseMember)) { - if (derivedMember is IProperty) + if (derivedMember is IReadOnlyProperty) { - return baseMember is IProperty; + return baseMember is IReadOnlyProperty; } - if (derivedMember is INavigation derivedNavigation) + if (derivedMember is IReadOnlyNavigation derivedNavigation) { - return baseMember is INavigation baseNavigation + return baseMember is IReadOnlyNavigation baseNavigation && derivedNavigation.TargetEntityType == baseNavigation.TargetEntityType; } - if (derivedMember is IServiceProperty) + if (derivedMember is IReadOnlyServiceProperty) { - return baseMember is IServiceProperty; + return baseMember is IReadOnlyServiceProperty; } - if (derivedMember is ISkipNavigation derivedSkipNavigation) + if (derivedMember is IReadOnlySkipNavigation derivedSkipNavigation) { - return baseMember is ISkipNavigation baseSkipNavigation + return baseMember is IReadOnlySkipNavigation baseSkipNavigation && derivedSkipNavigation.TargetEntityType == baseSkipNavigation.TargetEntityType; } } @@ -3289,7 +3289,7 @@ public virtual bool RemoveNonOwnershipRelationships([CanBeNull] ForeignKey? owne return true; } - private bool Contains(IForeignKey? inheritedFk, IForeignKey derivedFk) + private bool Contains(IReadOnlyForeignKey? inheritedFk, IReadOnlyForeignKey derivedFk) => inheritedFk != null && inheritedFk.PrincipalEntityType.IsAssignableFrom(derivedFk.PrincipalEntityType) && PropertyListComparer.Instance.Equals(inheritedFk.Properties, derivedFk.Properties); @@ -4314,7 +4314,7 @@ public virtual bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessM private InternalPropertyBuilder? GetOrCreateDiscriminatorProperty(Type? type, string? name, ConfigurationSource configurationSource) { - var discriminatorProperty = ((IEntityType)Metadata).GetDiscriminatorProperty(); + var discriminatorProperty = ((IReadOnlyEntityType)Metadata).GetDiscriminatorProperty(); if ((name != null && discriminatorProperty?.Name != name) || (type != null && discriminatorProperty?.ClrType != type)) { @@ -4388,7 +4388,7 @@ public virtual bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessM private void RemoveUnusedDiscriminatorProperty(Property? newDiscriminatorProperty, ConfigurationSource configurationSource) { - var oldDiscriminatorProperty = ((IEntityType)Metadata).GetDiscriminatorProperty() as Property; + var oldDiscriminatorProperty = ((IReadOnlyEntityType)Metadata).GetDiscriminatorProperty() as Property; if (oldDiscriminatorProperty?.IsInModel == true && oldDiscriminatorProperty != newDiscriminatorProperty) { @@ -4412,10 +4412,10 @@ private void RemoveUnusedDiscriminatorProperty(Property? newDiscriminatorPropert public virtual bool CanSetDiscriminator([CanBeNull] string? name, [CanBeNull] Type? type, ConfigurationSource configurationSource) => name == null && type == null ? CanRemoveDiscriminator(configurationSource) - : CanSetDiscriminator(((IEntityType)Metadata).GetDiscriminatorProperty(), name, type, configurationSource); + : CanSetDiscriminator(((IReadOnlyEntityType)Metadata).GetDiscriminatorProperty(), name, type, configurationSource); private bool CanSetDiscriminator( - IProperty? discriminatorProperty, + IReadOnlyProperty? discriminatorProperty, string? name, Type? discriminatorType, ConfigurationSource configurationSource) @@ -5114,7 +5114,7 @@ bool IConventionEntityTypeBuilder.CanHaveSkipNavigation(string skipNavigationNam /// [DebuggerStepThrough] IConventionEntityTypeBuilder? IConventionEntityTypeBuilder.HasNoSkipNavigation( - ISkipNavigation skipNavigation, + IReadOnlySkipNavigation skipNavigation, bool fromDataAnnotation) => HasNoSkipNavigation( (SkipNavigation)skipNavigation, @@ -5122,7 +5122,7 @@ bool IConventionEntityTypeBuilder.CanHaveSkipNavigation(string skipNavigationNam /// [DebuggerStepThrough] - bool IConventionEntityTypeBuilder.CanRemoveSkipNavigation(ISkipNavigation skipNavigation, bool fromDataAnnotation) + bool IConventionEntityTypeBuilder.CanRemoveSkipNavigation(IReadOnlySkipNavigation skipNavigation, bool fromDataAnnotation) => CanRemoveSkipNavigation( (SkipNavigation)skipNavigation, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); diff --git a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs index 024c8212f00..fce24ef1d33 100644 --- a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs @@ -1835,7 +1835,7 @@ public virtual bool CanInvert( public virtual bool CanSetForeignKey([CanBeNull] IReadOnlyList? propertyNames, ConfigurationSource? configurationSource) { if (propertyNames is not null - && Metadata.DeclaringEntityType.FindProperties(propertyNames) is IReadOnlyList properties) + && ((IReadOnlyEntityType)Metadata.DeclaringEntityType).FindProperties(propertyNames) is IReadOnlyList properties) { return CanSetForeignKey( properties, @@ -1861,7 +1861,7 @@ public virtual bool CanSetForeignKey([CanBeNull] IReadOnlyList? proper out _); private bool CanSetForeignKey( - IReadOnlyList? properties, + IReadOnlyList? properties, EntityType? dependentEntityType, ConfigurationSource? configurationSource, bool overrideSameSource = true) @@ -1873,7 +1873,7 @@ private bool CanSetForeignKey( overrideSameSource); private bool CanSetForeignKey( - IReadOnlyList? properties, + IReadOnlyList? properties, EntityType? dependentEntityType, ConfigurationSource? configurationSource, out bool resetPrincipalKey, @@ -1893,7 +1893,7 @@ private bool CanSetForeignKey( } private bool CanSetForeignKey( - IReadOnlyList? properties, + IReadOnlyList? properties, EntityType? dependentEntityType, IReadOnlyList principalKeyProperties, EntityType principalEntityType, @@ -2062,7 +2062,7 @@ private bool CanSetForeignKey( public virtual bool CanSetPrincipalKey([CanBeNull] IReadOnlyList? propertyNames, ConfigurationSource? configurationSource) { if (propertyNames is not null - && Metadata.PrincipalEntityType.FindProperties(propertyNames) is IReadOnlyList properties) + && ((IReadOnlyEntityType)Metadata.PrincipalEntityType).FindProperties(propertyNames) is IReadOnlyList properties) { return CanSetPrincipalKey( properties, @@ -2088,7 +2088,7 @@ public virtual bool CanSetPrincipalKey([CanBeNull] IReadOnlyList? prop out _); private bool CanSetPrincipalKey( - IReadOnlyList? properties, + IReadOnlyList? properties, ConfigurationSource? configurationSource, out bool resetDependent, out IReadOnlyList? oldNameDependentProperties) @@ -2842,9 +2842,9 @@ private InternalForeignKeyBuilder MergeFacetsFrom(Navigation newNavigation, Navi var oldIsEagerLoadedConfigurationSource = ((IConventionNavigation)oldNavigation).GetIsEagerLoadedConfigurationSource(); if (oldIsEagerLoadedConfigurationSource.HasValue - && builder.CanSetAutoInclude(((INavigation)oldNavigation).IsEagerLoaded, oldIsEagerLoadedConfigurationSource.Value)) + && builder.CanSetAutoInclude(((IReadOnlyNavigation)oldNavigation).IsEagerLoaded, oldIsEagerLoadedConfigurationSource.Value)) { - builder = builder.AutoInclude(((INavigation)oldNavigation).IsEagerLoaded, oldIsEagerLoadedConfigurationSource.Value)!; + builder = builder.AutoInclude(((IReadOnlyNavigation)oldNavigation).IsEagerLoaded, oldIsEagerLoadedConfigurationSource.Value)!; } return builder.Metadata.ForeignKey.Builder; @@ -3361,7 +3361,7 @@ private InternalForeignKeyBuilder MergeFacetsFrom(Navigation newNavigation, Navi /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static void ThrowForConflictingNavigation( - [NotNull] IForeignKey foreignKey, + [NotNull] IReadOnlyForeignKey foreignKey, [NotNull] string newInverseName, bool newToPrincipal) => ThrowForConflictingNavigation( @@ -3377,9 +3377,9 @@ public static void ThrowForConflictingNavigation( /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static void ThrowForConflictingNavigation( - [NotNull] IForeignKey foreignKey, - [NotNull] IEntityType principalEntityType, - [NotNull] IEntityType dependentEntityType, + [NotNull] IReadOnlyForeignKey foreignKey, + [NotNull] IReadOnlyEntityType principalEntityType, + [NotNull] IReadOnlyEntityType dependentEntityType, [CanBeNull] string? navigationToDependent, [CanBeNull] string? navigationToPrincipal) => throw new InvalidOperationException( diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBaseBuilder`.cs b/src/EFCore/Metadata/Internal/InternalPropertyBaseBuilder`.cs index aebc7360351..b231a55940a 100644 --- a/src/EFCore/Metadata/Internal/InternalPropertyBaseBuilder`.cs +++ b/src/EFCore/Metadata/Internal/InternalPropertyBaseBuilder`.cs @@ -141,6 +141,6 @@ public virtual bool CanSetPropertyAccessMode( PropertyAccessMode? propertyAccessMode, ConfigurationSource? configurationSource) => configurationSource.Overrides(Metadata.GetPropertyAccessModeConfigurationSource()) - || ((IPropertyBase)Metadata).GetPropertyAccessMode() == propertyAccessMode; + || ((IReadOnlyPropertyBase)Metadata).GetPropertyAccessMode() == propertyAccessMode; } } diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs index 39508db1351..a3ec56d2ddf 100644 --- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs @@ -550,7 +550,7 @@ public virtual bool CanSetValueComparer([CanBeNull] ValueComparer? comparer, Con return true; } - + return Metadata[CoreAnnotationNames.ValueComparer] == comparer; } @@ -663,7 +663,7 @@ public virtual bool CanSetKeyValueComparer([CanBeNull] ValueComparer? comparer, if (oldPropertyAccessModeConfigurationSource.HasValue) { newPropertyBuilder.UsePropertyAccessMode( - ((IProperty)Metadata).GetPropertyAccessMode(), oldPropertyAccessModeConfigurationSource.Value); + ((IReadOnlyProperty)Metadata).GetPropertyAccessMode(), oldPropertyAccessModeConfigurationSource.Value); } var oldFieldInfoConfigurationSource = Metadata.GetFieldInfoConfigurationSource(); diff --git a/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs b/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs index c4dc527d156..096999b35db 100644 --- a/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalSkipNavigationBuilder.cs @@ -298,7 +298,7 @@ public virtual bool CanSetInverse( if (propertyAccessModeConfigurationSource.HasValue) { newSkipNavigationBuilder.UsePropertyAccessMode( - ((ISkipNavigation)Metadata).GetPropertyAccessMode(), propertyAccessModeConfigurationSource.Value); + ((IReadOnlySkipNavigation)Metadata).GetPropertyAccessMode(), propertyAccessModeConfigurationSource.Value); } var oldFieldInfoConfigurationSource = Metadata.GetFieldInfoConfigurationSource(); diff --git a/src/EFCore/Metadata/Internal/Key.cs b/src/EFCore/Metadata/Internal/Key.cs index ef434bbd69c..54a6f5f2d79 100644 --- a/src/EFCore/Metadata/Internal/Key.cs +++ b/src/EFCore/Metadata/Internal/Key.cs @@ -24,7 +24,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 Key : ConventionAnnotatable, IMutableKey, IConventionKey + public class Key : ConventionAnnotatable, IMutableKey, IConventionKey, IKey { private InternalKeyBuilder? _builder; private ConfigurationSource _configurationSource; @@ -105,7 +105,7 @@ 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. /// - protected override bool IsReadOnly => DeclaringEntityType.Model.IsModelReadOnly; + public override bool IsReadOnly => DeclaringEntityType.Model.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -216,7 +216,7 @@ public virtual DebugView DebugView /// 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 + IReadOnlyList IReadOnlyKey.Properties { [DebuggerStepThrough] get => Properties; } @@ -227,7 +227,7 @@ 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. /// - IEntityType IKey.DeclaringEntityType + IReadOnlyEntityType IReadOnlyKey.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; } @@ -288,6 +288,18 @@ IReadOnlyList IConventionKey.Properties [DebuggerStepThrough] get => Properties; } + /// + /// 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. + /// + IReadOnlyList IKey.Properties + { + [DebuggerStepThrough] + get => Properties; + } + /// /// 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 @@ -298,5 +310,17 @@ IConventionEntityType IConventionKey.DeclaringEntityType { [DebuggerStepThrough] 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. + /// + IEntityType IKey.DeclaringEntityType + { + [DebuggerStepThrough] + get => DeclaringEntityType; + } } } diff --git a/src/EFCore/Metadata/Internal/KeyExtensions.cs b/src/EFCore/Metadata/Internal/KeyExtensions.cs index 8eb6f5d3b37..bf1116fe0a7 100644 --- a/src/EFCore/Metadata/Internal/KeyExtensions.cs +++ b/src/EFCore/Metadata/Internal/KeyExtensions.cs @@ -24,7 +24,7 @@ public static class KeyExtensions /// 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 Func GetIdentityMapFactory([NotNull] this IKey key) + public static Func GetIdentityMapFactory([NotNull] this IReadOnlyKey key) => key.AsKey().IdentityMapFactory; /// @@ -33,7 +33,7 @@ public static Func GetIdentityMapFactory([NotNull] this IKey /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static int IndexOf([NotNull] this IKey key, [NotNull] IProperty property) + public static int IndexOf([NotNull] this IReadOnlyKey key, [NotNull] IReadOnlyProperty property) { var index = 0; for (; index < key.Properties.Count && key.Properties[index] != property; index++) @@ -49,7 +49,7 @@ public static int IndexOf([NotNull] this IKey key, [NotNull] IProperty property) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static Key AsKey([NotNull] this IKey key, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(key, methodName); + public static Key AsKey([NotNull] this IReadOnlyKey key, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(key, methodName); } } diff --git a/src/EFCore/Metadata/Internal/Model.cs b/src/EFCore/Metadata/Internal/Model.cs index f5286230636..2a73a1cb1fd 100644 --- a/src/EFCore/Metadata/Internal/Model.cs +++ b/src/EFCore/Metadata/Internal/Model.cs @@ -37,7 +37,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal /// The implementation does not need to be thread-safe. /// /// - public class Model : ConventionAnnotatable, IMutableModel, IConventionModel + public class Model : ConventionAnnotatable, IMutableModel, IConventionModel, IModel { /// /// The CLR type that is used for property bag entity types when no other type is specified. @@ -118,12 +118,7 @@ public virtual ModelDependencies? ScopedModelDependencies /// /// Indicates whether the model is read-only. /// - protected override bool IsReadOnly => IsModelReadOnly; - - /// - /// Indicates whether the model is read-only. - /// - public virtual bool IsModelReadOnly => _conventionDispatcher == null; + public override bool IsReadOnly => _conventionDispatcher == null; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -474,59 +469,6 @@ public virtual IReadOnlyCollection GetEntityTypes([NotNull] string n : new[] { entityType }; } - /// - /// 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 IReadOnlyList FindLeastDerivedEntityTypes( - [NotNull] Type type, - [CanBeNull] Func? condition = null) - { - var derivedLevels = new Dictionary { [type] = 0 }; - - var leastDerivedTypesGroups = GetEntityTypes() - .GroupBy(t => GetDerivedLevel(t.ClrType, derivedLevels)) - .Where(g => g.Key != int.MaxValue) - .OrderBy(g => g.Key); - - foreach (var leastDerivedTypes in leastDerivedTypesGroups) - { - if (condition == null) - { - return leastDerivedTypes.ToList(); - } - - var filteredTypes = leastDerivedTypes.Where(condition).ToList(); - if (filteredTypes.Count > 0) - { - return filteredTypes; - } - } - - return new List(); - } - - private static int GetDerivedLevel(Type? derivedType, Dictionary derivedLevels) - { - if (derivedType?.BaseType == null) - { - return int.MaxValue; - } - - if (derivedLevels.TryGetValue(derivedType, out var level)) - { - return level; - } - - var baseType = derivedType.BaseType; - level = GetDerivedLevel(baseType, derivedLevels); - level += level == int.MaxValue ? 0 : 1; - derivedLevels.Add(derivedType, level); - return level; - } - /// /// 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 @@ -893,13 +835,16 @@ public virtual IModel FinalizeModel() EnsureMutable(); ConventionDispatcher.AssertNoScope(); - IModel? finalizedModel = ConventionDispatcher.OnModelFinalizing(Builder)?.Metadata; - if (finalizedModel != null) + var finalizedModel = (IModel)ConventionDispatcher.OnModelFinalizing(Builder).Metadata; + + finalizedModel = ConventionDispatcher.OnModelFinalized(finalizedModel); + + if (finalizedModel is Model model) { - finalizedModel = ConventionDispatcher.OnModelFinalized(finalizedModel); + finalizedModel = model.MakeReadonly(); } - return (finalizedModel as Model)?.MakeReadonly() ?? finalizedModel!; + return finalizedModel; } /// @@ -908,7 +853,7 @@ public virtual IModel FinalizeModel() /// 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. /// - private IModel MakeReadonly() + private Model MakeReadonly() { // ConventionDispatcher should never be accessed once the model is made read-only. _conventionDispatcher = null; @@ -961,17 +906,6 @@ public virtual bool SkipDetectChanges public virtual object? RelationalModel => FindRuntimeAnnotation("Relational:RelationalModel"); - /// - /// 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( - () => this.ToDebugString(MetadataDebugStringOptions.ShortDefault), - () => this.ToDebugString(MetadataDebugStringOptions.LongDefault)); - /// /// The runtime service dependencies. /// @@ -989,12 +923,15 @@ public virtual DebugView DebugView } /// - /// Set the runtime service dependencies. + /// 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. /// - /// The runtime service dependencies. - /// if the runtime service dependencies were set; otherwise. - bool IModel.SetModelDependencies(SingletonModelDependencies modelDependencies) - => Interlocked.CompareExchange(ref _modelDependencies, modelDependencies, null) == null; + public virtual DebugView DebugView + => new( + () => this.ToDebugString(MetadataDebugStringOptions.ShortDefault), + () => this.ToDebugString(MetadataDebugStringOptions.LongDefault)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1002,8 +939,11 @@ bool IModel.SetModelDependencies(SingletonModelDependencies modelDependencies) /// 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? IModel.FindEntityType(string name) - => FindEntityType(name); + IConventionModelBuilder IConventionModel.Builder + { + [DebuggerStepThrough] + get => Builder; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1011,8 +951,11 @@ bool IModel.SetModelDependencies(SingletonModelDependencies modelDependencies) /// 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. /// - IEnumerable IModel.GetEntityTypes() - => GetEntityTypes(); + IConventionAnnotatableBuilder IConventionAnnotatable.Builder + { + [DebuggerStepThrough] + get => Builder; + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1020,7 +963,8 @@ IEnumerable IModel.GetEntityTypes() /// 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? IMutableModel.FindEntityType(string name) + [DebuggerStepThrough] + IReadOnlyEntityType? IReadOnlyModel.FindEntityType(string name) => FindEntityType(name); /// @@ -1029,8 +973,9 @@ IEnumerable IModel.GetEntityTypes() /// 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 IMutableModel.AddEntityType(string name) - => AddEntityType(name, ConfigurationSource.Explicit)!; + [DebuggerStepThrough] + IMutableEntityType? IMutableModel.FindEntityType(string name) + => FindEntityType(name); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1038,8 +983,9 @@ IMutableEntityType IMutableModel.AddEntityType(string name) /// 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 IMutableModel.AddEntityType(Type type) - => AddEntityType(type, ConfigurationSource.Explicit)!; + [DebuggerStepThrough] + IConventionEntityType? IConventionModel.FindEntityType(string name) + => FindEntityType(name); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1047,8 +993,9 @@ IMutableEntityType IMutableModel.AddEntityType(Type type) /// 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 IMutableModel.AddEntityType(string name, Type type) - => AddEntityType(name, type, ConfigurationSource.Explicit)!; + [DebuggerStepThrough] + IEntityType? IModel.FindEntityType(string name) + => FindEntityType(name); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1056,8 +1003,9 @@ IMutableEntityType IMutableModel.AddEntityType(string name, Type type) /// 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? IMutableModel.RemoveEntityType(IMutableEntityType entityType) - => RemoveEntityType((EntityType)entityType); + [DebuggerStepThrough] + IEntityType? IModel.FindEntityType(Type type) + => FindEntityType(type); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1065,7 +1013,8 @@ IMutableEntityType IMutableModel.AddEntityType(string name, Type type) /// 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? IModel.FindEntityType(string name, string definingNavigationName, IEntityType definingEntityType) + [DebuggerStepThrough] + IReadOnlyEntityType? IReadOnlyModel.FindEntityType(string name, string definingNavigationName, IReadOnlyEntityType definingEntityType) => FindEntityType(name, definingNavigationName, (EntityType)definingEntityType); /// @@ -1074,6 +1023,7 @@ IMutableEntityType IMutableModel.AddEntityType(string name, Type type) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// + [DebuggerStepThrough] IMutableEntityType? IMutableModel.FindEntityType( string name, string definingNavigationName, @@ -1086,11 +1036,12 @@ IMutableEntityType IMutableModel.AddEntityType(string name, Type type) /// 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 IMutableModel.AddEntityType( + [DebuggerStepThrough] + IConventionEntityType? IConventionModel.FindEntityType( string name, string definingNavigationName, - IMutableEntityType definingEntityType) - => AddEntityType(name, definingNavigationName, (EntityType)definingEntityType, ConfigurationSource.Explicit)!; + IConventionEntityType definingEntityType) + => FindEntityType(name, definingNavigationName, (EntityType)definingEntityType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1098,11 +1049,12 @@ IMutableEntityType IMutableModel.AddEntityType( /// 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 IMutableModel.AddEntityType( - Type type, + [DebuggerStepThrough] + IEntityType? IModel.FindEntityType( + string name, string definingNavigationName, - IMutableEntityType definingEntityType) - => AddEntityType(type, definingNavigationName, (EntityType)definingEntityType, ConfigurationSource.Explicit)!; + IEntityType definingEntityType) + => FindEntityType(name, definingNavigationName, (EntityType)definingEntityType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1110,7 +1062,8 @@ IMutableEntityType IMutableModel.AddEntityType( /// 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. /// - IEnumerable IMutableModel.GetEntityTypes() + [DebuggerStepThrough] + IEnumerable IReadOnlyModel.GetEntityTypes() => GetEntityTypes(); /// @@ -1119,8 +1072,9 @@ IEnumerable IMutableModel.GetEntityTypes() /// 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 IMutableModel.AddIgnored(string name) - => AddIgnored(name, ConfigurationSource.Explicit)!; + [DebuggerStepThrough] + IEnumerable IMutableModel.GetEntityTypes() + => GetEntityTypes(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1128,11 +1082,9 @@ string IMutableModel.AddIgnored(string name) /// 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. /// - IConventionModelBuilder IConventionModel.Builder - { - [DebuggerStepThrough] - get => Builder; - } + [DebuggerStepThrough] + IEnumerable IConventionModel.GetEntityTypes() + => GetEntityTypes(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1140,11 +1092,9 @@ IConventionModelBuilder IConventionModel.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 - { - [DebuggerStepThrough] - get => Builder; - } + [DebuggerStepThrough] + IEnumerable IModel.GetEntityTypes() + => GetEntityTypes(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1152,8 +1102,9 @@ 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. /// - IConventionEntityType? IConventionModel.FindEntityType(string name) - => FindEntityType(name); + [DebuggerStepThrough] + IEnumerable IModel.GetEntityTypes(Type type) + => GetEntityTypes(type); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1161,11 +1112,9 @@ 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. /// - IConventionEntityType? IConventionModel.FindEntityType( - string name, - string definingNavigationName, - IConventionEntityType definingEntityType) - => FindEntityType(name, definingNavigationName, (EntityType)definingEntityType); + [DebuggerStepThrough] + IMutableEntityType IMutableModel.AddEntityType(string name) + => AddEntityType(name, ConfigurationSource.Explicit)!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1173,6 +1122,7 @@ 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. /// + [DebuggerStepThrough] IConventionEntityType? IConventionModel.AddEntityType(string name, bool fromDataAnnotation) => AddEntityType(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -1182,6 +1132,17 @@ 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. /// + [DebuggerStepThrough] + IMutableEntityType IMutableModel.AddEntityType(Type type) + => AddEntityType(type, ConfigurationSource.Explicit)!; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] IConventionEntityType? IConventionModel.AddEntityType(Type type, bool fromDataAnnotation) => AddEntityType(type, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -1191,9 +1152,33 @@ 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. /// + [DebuggerStepThrough] + IMutableEntityType IMutableModel.AddEntityType(string name, Type type) + => AddEntityType(name, type, ConfigurationSource.Explicit)!; + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] IConventionEntityType? IConventionModel.AddEntityType(string name, Type type, bool fromDataAnnotation) => AddEntityType(name, type, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IMutableEntityType IMutableModel.AddEntityType( + string name, + string definingNavigationName, + IMutableEntityType definingEntityType) + => AddEntityType(name, definingNavigationName, (EntityType)definingEntityType, ConfigurationSource.Explicit)!; + /// /// 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 @@ -1201,6 +1186,7 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [Obsolete] + [DebuggerStepThrough] IConventionEntityType? IConventionModel.AddEntityType( string name, string definingNavigationName, @@ -1210,6 +1196,19 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder name, definingNavigationName, (EntityType)definingEntityType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IMutableEntityType IMutableModel.AddEntityType( + Type type, + string definingNavigationName, + IMutableEntityType definingEntityType) + => AddEntityType(type, definingNavigationName, (EntityType)definingEntityType, ConfigurationSource.Explicit)!; + /// /// 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 @@ -1217,6 +1216,7 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder /// doing so can result in application failures when updating to a new Entity Framework Core release. /// [Obsolete] + [DebuggerStepThrough] IConventionEntityType? IConventionModel.AddEntityType( Type type, string definingNavigationName, @@ -1232,6 +1232,17 @@ 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. /// + [DebuggerStepThrough] + IMutableEntityType? IMutableModel.RemoveEntityType(IMutableEntityType entityType) + => RemoveEntityType((EntityType)entityType); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] IConventionEntityType? IConventionModel.RemoveEntityType(IConventionEntityType entityType) => RemoveEntityType((EntityType)entityType); @@ -1241,8 +1252,9 @@ 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. /// - IEnumerable IConventionModel.GetEntityTypes() - => GetEntityTypes(); + [DebuggerStepThrough] + string IMutableModel.AddIgnored(string name) + => AddIgnored(name, ConfigurationSource.Explicit)!; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1250,6 +1262,7 @@ IEnumerable IConventionModel.GetEntityTypes() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// + [DebuggerStepThrough] string? IConventionModel.AddIgnored(string name, bool fromDataAnnotation) => AddIgnored(name, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -1259,7 +1272,17 @@ IEnumerable IConventionModel.GetEntityTypes() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// + [DebuggerStepThrough] bool IConventionModel.IsShared(Type type) => IsShared(type); + + /// + /// Set the runtime service dependencies. + /// + /// The runtime service dependencies. + /// if the runtime service dependencies were set; otherwise. + [DebuggerStepThrough] + bool IModel.SetModelDependencies(SingletonModelDependencies modelDependencies) + => Interlocked.CompareExchange(ref _modelDependencies, modelDependencies, null) == null; } } diff --git a/src/EFCore/Metadata/Internal/ModelExtensions.cs b/src/EFCore/Metadata/Internal/ModelExtensions.cs index 7f83658c3b9..8378617da59 100644 --- a/src/EFCore/Metadata/Internal/ModelExtensions.cs +++ b/src/EFCore/Metadata/Internal/ModelExtensions.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -42,7 +43,19 @@ public static IEnumerable GetRootEntityTypes([NotNull] this IModel /// 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 Model AsModel([NotNull] this IModel model, [CallerMemberName] [NotNull] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(model, methodName); + public static string? FindSameTypeNameWithDifferentNamespace([NotNull] this IModel model, [NotNull] Type type) + => model.GetEntityTypes() + .Where(x => x.ClrType.DisplayName(false) == type.DisplayName(false)) + .Select(x => x.ClrType.DisplayName()) + .FirstOrDefault(); + + /// + /// 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 Model AsModel([NotNull] this IReadOnlyModel model, [CallerMemberName] [NotNull] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(model, methodName); } } diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs index ed7a65cb05d..d69aa927c24 100644 --- a/src/EFCore/Metadata/Internal/Navigation.cs +++ b/src/EFCore/Metadata/Internal/Navigation.cs @@ -21,7 +21,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 Navigation : PropertyBase, IMutableNavigation, IConventionNavigation + public class Navigation : PropertyBase, IMutableNavigation, IConventionNavigation, INavigation { private InternalNavigationBuilder? _builder; @@ -104,7 +104,7 @@ public virtual void SetRemovedFromModel() public virtual EntityType DeclaringEntityType { [DebuggerStepThrough] - get => (EntityType)((INavigation)this).DeclaringEntityType; + get => (EntityType)((IReadOnlyNavigation)this).DeclaringEntityType; } /// @@ -116,7 +116,7 @@ public virtual EntityType DeclaringEntityType public override TypeBase DeclaringType { [DebuggerStepThrough] - get => (EntityType)((INavigation)this).DeclaringEntityType; + get => (EntityType)((IReadOnlyNavigation)this).DeclaringEntityType; } /// @@ -128,7 +128,7 @@ public override TypeBase DeclaringType public virtual EntityType TargetEntityType { [DebuggerStepThrough] - get => (EntityType)((INavigationBase)this).TargetEntityType; + get => (EntityType)((IReadOnlyNavigationBase)this).TargetEntityType; } /// @@ -253,7 +253,7 @@ public static bool IsCompatible( public virtual Navigation? Inverse { [DebuggerStepThrough] - get => (Navigation?)((INavigationBase)this).Inverse; + get => (Navigation?)((IReadOnlyNavigationBase)this).Inverse; } /// @@ -346,7 +346,7 @@ public virtual DebugView DebugView /// 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. /// - IForeignKey INavigation.ForeignKey + IReadOnlyForeignKey IReadOnlyNavigation.ForeignKey { [DebuggerStepThrough] get => ForeignKey; } diff --git a/src/EFCore/Metadata/Internal/NavigationExtensions.cs b/src/EFCore/Metadata/Internal/NavigationExtensions.cs index 3752d30a5e4..99681905a3e 100644 --- a/src/EFCore/Metadata/Internal/NavigationExtensions.cs +++ b/src/EFCore/Metadata/Internal/NavigationExtensions.cs @@ -23,7 +23,7 @@ public static class NavigationExtensions /// 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 MemberIdentity CreateMemberIdentity([CanBeNull] this INavigation? navigation) + public static MemberIdentity CreateMemberIdentity([CanBeNull] this IReadOnlyNavigation? navigation) => navigation?.GetIdentifyingMemberInfo() == null ? MemberIdentity.Create(navigation?.Name) : MemberIdentity.Create(navigation.GetIdentifyingMemberInfo()); @@ -34,8 +34,8 @@ public static MemberIdentity CreateMemberIdentity([CanBeNull] this INavigation? /// 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 Navigation AsNavigation([NotNull] this INavigation navigation, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(navigation, methodName); + public static Navigation AsNavigation([NotNull] this IReadOnlyNavigation navigation, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(navigation, methodName); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -43,7 +43,7 @@ public static Navigation AsNavigation([NotNull] this INavigation navigation, [No /// 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 ICollectionLoader GetManyToManyLoader([NotNull] this ISkipNavigation navigation) + public static ICollectionLoader GetManyToManyLoader([NotNull] this IReadOnlySkipNavigation navigation) => ((SkipNavigation)navigation).ManyToManyLoader; } } diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 8f68951bafb..6e08ff36648 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -27,7 +27,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 Property : PropertyBase, IMutableProperty, IConventionProperty + public class Property : PropertyBase, IMutableProperty, IConventionProperty, IProperty { private bool? _isConcurrencyToken; private bool? _isNullable; @@ -581,7 +581,7 @@ public virtual CoreTypeMapping? TypeMapping /// 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 IKey? PrimaryKey { get; [param: CanBeNull] set; } + public virtual IReadOnlyKey? PrimaryKey { get; [param: CanBeNull] set; } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -729,7 +729,7 @@ 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. /// - IEntityType IProperty.DeclaringEntityType + IReadOnlyEntityType IReadOnlyProperty.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; } @@ -762,6 +762,49 @@ IConventionEntityType IConventionProperty.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 IProperty.DeclaringEntityType + { + [DebuggerStepThrough] + 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. + /// + [DebuggerStepThrough] + IEnumerable IProperty.GetContainingForeignKeys() + => GetContainingForeignKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IProperty.GetContainingIndexes() + => GetContainingIndexes(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IEnumerable IProperty.GetContainingKeys() + => GetContainingKeys(); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] bool? IConventionProperty.SetIsNullable(bool? nullable, bool fromDataAnnotation) => SetIsNullable( nullable, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -772,6 +815,7 @@ IConventionEntityType IConventionProperty.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. /// + [DebuggerStepThrough] ValueGenerated? IConventionProperty.SetValueGenerated(ValueGenerated? valueGenerated, bool fromDataAnnotation) => SetValueGenerated( valueGenerated, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); @@ -782,6 +826,7 @@ IConventionEntityType IConventionProperty.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. /// + [DebuggerStepThrough] bool? IConventionProperty.SetIsConcurrencyToken(bool? concurrencyToken, bool fromDataAnnotation) => SetIsConcurrencyToken( concurrencyToken, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index a16c83af913..6058cbc13c9 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.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 abstract class PropertyBase : ConventionAnnotatable, IMutablePropertyBase, IConventionPropertyBase + public abstract class PropertyBase : ConventionAnnotatable, IMutablePropertyBase, IConventionPropertyBase, IPropertyBase { private FieldInfo? _fieldInfo; private ConfigurationSource _configurationSource; @@ -70,7 +70,7 @@ protected PropertyBase( /// /// Indicates whether the model is read-only. /// - protected override bool IsReadOnly => DeclaringType.Model.IsModelReadOnly; + public override bool IsReadOnly => DeclaringType.Model.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -433,7 +433,7 @@ public virtual IComparer CurrentValueComparer /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static Expression CreateMemberAccess( - [CanBeNull] IPropertyBase? property, + [CanBeNull] IReadOnlyPropertyBase? property, [NotNull] Expression instanceExpression, [NotNull] MemberInfo memberInfo) { @@ -463,7 +463,7 @@ public static Expression CreateMemberAccess( /// 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. /// - ITypeBase IPropertyBase.DeclaringType + IReadOnlyTypeBase IReadOnlyPropertyBase.DeclaringType { [DebuggerStepThrough] get => DeclaringType; } @@ -499,5 +499,15 @@ IConventionTypeBase IConventionPropertyBase.DeclaringType [DebuggerStepThrough] FieldInfo? IConventionPropertyBase.SetFieldInfo(FieldInfo? fieldInfo, bool fromDataAnnotation) => SetFieldInfo(fieldInfo, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [DebuggerStepThrough] + IClrPropertyGetter IPropertyBase.GetGetter() + => Getter; } } diff --git a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs index 7e4867a04a1..9d4ab1db4b8 100644 --- a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs +++ b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs @@ -83,7 +83,7 @@ public static PropertyAccessors GetPropertyAccessors([NotNull] this IPropertyBas // Note: only use this to find the property/field that defines the property in the model. Use // GetMemberInfo to get the property/field to use, which may be different. public static MemberInfo? GetIdentifyingMemberInfo( - [NotNull] this IPropertyBase propertyBase) + [NotNull] this IReadOnlyPropertyBase propertyBase) => propertyBase.PropertyInfo ?? (MemberInfo?)propertyBase.FieldInfo; /// @@ -107,7 +107,7 @@ public static bool TryGetMemberInfo( var setterProperty = propertyInfo?.FindSetterProperty(); var getterProperty = propertyInfo?.FindGetterProperty(); - var isCollectionNav = (propertyBase as INavigation)?.IsCollection == true; + var isCollectionNav = (propertyBase as IReadOnlyNavigation)?.IsCollection == true; var hasField = fieldInfo != null; var hasSetter = setterProperty != null; var hasGetter = getterProperty != null; @@ -359,8 +359,8 @@ private static string GetNoFieldErrorMessage(IPropertyBase propertyBase) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static PropertyBase AsPropertyBase( - [NotNull] this IPropertyBase propertyBase, + [NotNull] this IReadOnlyPropertyBase propertyBase, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(propertyBase, methodName); + => MetadataExtensions.AsConcreteMetadataType(propertyBase, methodName); } } diff --git a/src/EFCore/Metadata/Internal/PropertyExtensions.cs b/src/EFCore/Metadata/Internal/PropertyExtensions.cs index d5f2ff28cf5..79fbb627d6c 100644 --- a/src/EFCore/Metadata/Internal/PropertyExtensions.cs +++ b/src/EFCore/Metadata/Internal/PropertyExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Utilities; @@ -37,24 +36,6 @@ public static bool ForAdd(this ValueGenerated valueGenerated) public static bool ForUpdate(this ValueGenerated valueGenerated) => (valueGenerated & ValueGenerated.OnUpdate) != 0; - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public static IEnumerable GetContainingEntityTypes([NotNull] this IProperty property) - => property.DeclaringEntityType.GetDerivedTypesInclusive(); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public static IEnumerable GetReferencingForeignKeys([NotNull] this IProperty property) - => property.GetContainingKeys().SelectMany(k => k.GetReferencingForeignKeys()); - /// /// 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 @@ -102,7 +83,7 @@ public static IEnumerable GetReferencingForeignKeys([NotNull] this /// 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 RequiresValueGenerator([NotNull] this IProperty property) + public static bool RequiresValueGenerator([NotNull] this IReadOnlyProperty property) => (property.ValueGenerated.ForAdd() && property.IsKey() && (!property.IsForeignKey() || property.IsForeignKeyToSelf())) @@ -114,7 +95,7 @@ public static bool RequiresValueGenerator([NotNull] this IProperty property) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static bool IsForeignKeyToSelf([NotNull] this IProperty property) + public static bool IsForeignKeyToSelf([NotNull] this IReadOnlyProperty property) { Check.DebugAssert(property.IsKey(), "Only call this method for properties known to be part of a key."); @@ -160,7 +141,7 @@ public static bool MayBeStoreGenerated([NotNull] this IProperty property) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static bool RequiresOriginalValue([NotNull] this IProperty property) + public static bool RequiresOriginalValue([NotNull] this IReadOnlyProperty property) => property.DeclaringEntityType.GetChangeTrackingStrategy() != ChangeTrackingStrategy.ChangingAndChangedNotifications || property.IsConcurrencyToken || property.IsKey() @@ -173,7 +154,7 @@ public static bool RequiresOriginalValue([NotNull] this IProperty property) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public static Property AsProperty([NotNull] this IProperty property, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(property, methodName); + public static Property AsProperty([NotNull] this IReadOnlyProperty property, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(property, methodName); } } diff --git a/src/EFCore/Metadata/Internal/PropertyListComparer.cs b/src/EFCore/Metadata/Internal/PropertyListComparer.cs index c0e92cd33a9..8c9915b16c7 100644 --- a/src/EFCore/Metadata/Internal/PropertyListComparer.cs +++ b/src/EFCore/Metadata/Internal/PropertyListComparer.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal /// doing so can result in application failures when updating to a new Entity Framework Core release. /// // Sealed for perf - public sealed class PropertyListComparer : IComparer>, IEqualityComparer> + public sealed class PropertyListComparer : IComparer>, IEqualityComparer> { /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -35,7 +35,7 @@ private PropertyListComparer() /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public int Compare(IReadOnlyList? x, IReadOnlyList? y) + public int Compare(IReadOnlyList? x, IReadOnlyList? y) { if (ReferenceEquals(x, y)) { @@ -76,7 +76,7 @@ public int Compare(IReadOnlyList? x, IReadOnlyList? y) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public bool Equals(IReadOnlyList? x, IReadOnlyList? y) + public bool Equals(IReadOnlyList? x, IReadOnlyList? y) => Compare(x, y) == 0; /// @@ -85,7 +85,7 @@ public bool Equals(IReadOnlyList? x, IReadOnlyList? y) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public int GetHashCode(IReadOnlyList obj) + public int GetHashCode(IReadOnlyList obj) { var hash = new HashCode(); for (var i = 0; i < obj.Count; i++) diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs index 2f95c13a4ed..5bf94f53f91 100644 --- a/src/EFCore/Metadata/Internal/ServiceProperty.cs +++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs @@ -20,7 +20,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 ServiceProperty : PropertyBase, IMutableServiceProperty, IConventionServiceProperty + public class ServiceProperty : PropertyBase, IMutableServiceProperty, IConventionServiceProperty, IServiceProperty { private ServiceParameterBinding? _parameterBinding; private InternalServicePropertyBuilder? _builder; @@ -167,7 +167,7 @@ private void UpdateParameterBindingConfigurationSource(ConfigurationSource confi /// 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 IServiceProperty.DeclaringEntityType + IReadOnlyEntityType IReadOnlyServiceProperty.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; } @@ -202,7 +202,8 @@ IConventionServicePropertyBuilder IConventionServiceProperty.Builder /// IConventionAnnotatableBuilder IConventionAnnotatable.Builder { - [DebuggerStepThrough] get => Builder; + [DebuggerStepThrough] + get => Builder; } /// @@ -213,7 +214,14 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder /// IConventionEntityType IConventionServiceProperty.DeclaringEntityType { - [DebuggerStepThrough] get => DeclaringEntityType; + [DebuggerStepThrough] + get => DeclaringEntityType; + } + + IEntityType IServiceProperty.DeclaringEntityType + { + [DebuggerStepThrough] + get => DeclaringEntityType; } /// diff --git a/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs b/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs index f0d2b4889a5..214a77ea521 100644 --- a/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs +++ b/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs @@ -32,8 +32,8 @@ public static class ServicePropertyExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static ServiceProperty AsServiceProperty( - [NotNull] this IServiceProperty serviceProperty, + [NotNull] this IReadOnlyServiceProperty serviceProperty, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(serviceProperty, methodName); + => MetadataExtensions.AsConcreteMetadataType(serviceProperty, methodName); } } diff --git a/src/EFCore/Metadata/Internal/SkipNavigation.cs b/src/EFCore/Metadata/Internal/SkipNavigation.cs index d30e17b325f..681b5fa7c30 100644 --- a/src/EFCore/Metadata/Internal/SkipNavigation.cs +++ b/src/EFCore/Metadata/Internal/SkipNavigation.cs @@ -22,7 +22,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 SkipNavigation : PropertyBase, IMutableSkipNavigation, IConventionSkipNavigation + public class SkipNavigation : PropertyBase, IMutableSkipNavigation, IConventionSkipNavigation, ISkipNavigation { private ConfigurationSource? _foreignKeyConfigurationSource; private ConfigurationSource? _inverseConfigurationSource; @@ -412,21 +412,21 @@ IConventionAnnotatableBuilder IConventionAnnotatable.Builder } /// - IEntityType INavigationBase.DeclaringEntityType + IReadOnlyEntityType IReadOnlyNavigationBase.DeclaringEntityType { [DebuggerStepThrough] get => DeclaringEntityType; } /// - IEntityType INavigationBase.TargetEntityType + IReadOnlyEntityType IReadOnlyNavigationBase.TargetEntityType { [DebuggerStepThrough] get => TargetEntityType; } /// - IForeignKey ISkipNavigation.ForeignKey + IReadOnlyForeignKey IReadOnlySkipNavigation.ForeignKey { // ModelValidator makes sure ForeignKey isn't null, so we expose it as non-nullable. [DebuggerStepThrough] @@ -445,7 +445,7 @@ void IMutableSkipNavigation.SetForeignKey(IMutableForeignKey? foreignKey) (ForeignKey?)foreignKey, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); /// - ISkipNavigation ISkipNavigation.Inverse + IReadOnlySkipNavigation IReadOnlySkipNavigation.Inverse { // ModelValidator makes sure ForeignKey isn't null, so we expose it as non-nullable. [DebuggerStepThrough] diff --git a/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs b/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs index 6095477522c..600b66716b8 100644 --- a/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs +++ b/src/EFCore/Metadata/Internal/SkipNavigationExtensions.cs @@ -21,7 +21,7 @@ public static class SkipNavigationExtensions /// 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 MemberIdentity CreateMemberIdentity([CanBeNull] this ISkipNavigation? navigation) + public static MemberIdentity CreateMemberIdentity([CanBeNull] this IReadOnlySkipNavigation? navigation) => navigation?.GetIdentifyingMemberInfo() == null ? MemberIdentity.Create(navigation?.Name) : MemberIdentity.Create(navigation.GetIdentifyingMemberInfo()); diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index fb7f731e3ad..14a90ad0f09 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -10,7 +10,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; -using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -22,7 +21,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 abstract class TypeBase : ConventionAnnotatable, IMutableTypeBase, IConventionTypeBase + public abstract class TypeBase : ConventionAnnotatable, IMutableTypeBase, IConventionTypeBase, ITypeBase { private ConfigurationSource _configurationSource; @@ -94,7 +93,7 @@ protected TypeBase([NotNull] string name, [NotNull] Type type, [NotNull] Model m /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - protected override bool IsReadOnly => Model.IsModelReadOnly; + public override bool IsReadOnly => Model.IsReadOnly; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -328,7 +327,7 @@ public virtual bool IsIgnored(string name) /// 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. /// - IModel ITypeBase.Model + IReadOnlyModel IReadOnlyTypeBase.Model { [DebuggerStepThrough] get => Model; } @@ -361,7 +360,19 @@ IConventionModel IConventionTypeBase.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. /// - Type ITypeBase.ClrType + IModel ITypeBase.Model + { + [DebuggerStepThrough] + get => 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. + /// + Type IReadOnlyTypeBase.ClrType { [DebuggerStepThrough] get => ClrType; } diff --git a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs index 0ea1ed8b815..d488645b939 100644 --- a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs +++ b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs @@ -24,7 +24,7 @@ public static class TypeBaseExtensions /// 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 IReadOnlyDictionary GetRuntimeProperties([NotNull] this ITypeBase type) + public static IReadOnlyDictionary GetRuntimeProperties([NotNull] this IReadOnlyTypeBase type) => ((TypeBase)type).GetRuntimeProperties(); /// @@ -33,7 +33,7 @@ public static IReadOnlyDictionary GetRuntimeProperties([No /// 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 IReadOnlyDictionary GetRuntimeFields([NotNull] this ITypeBase type) + public static IReadOnlyDictionary GetRuntimeFields([NotNull] this IReadOnlyTypeBase type) => ((TypeBase)type).GetRuntimeFields(); /// @@ -42,7 +42,7 @@ public static IReadOnlyDictionary GetRuntimeFields([NotNull] /// 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 PropertyInfo? FindIndexerPropertyInfo([NotNull] this ITypeBase type) + public static PropertyInfo? FindIndexerPropertyInfo([NotNull] this IReadOnlyTypeBase type) => ((TypeBase)type).FindIndexerPropertyInfo(); /// @@ -51,7 +51,7 @@ public static IReadOnlyDictionary GetRuntimeFields([NotNull] /// 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 TypeBase AsTypeBase([NotNull] this ITypeBase entityType, [NotNull] [CallerMemberName] string methodName = "") - => MetadataExtensions.AsConcreteMetadataType(entityType, methodName); + public static TypeBase AsTypeBase([NotNull] this IReadOnlyTypeBase entityType, [NotNull] [CallerMemberName] string methodName = "") + => MetadataExtensions.AsConcreteMetadataType(entityType, methodName); } } diff --git a/src/EFCore/Metadata/KeyComparer.cs b/src/EFCore/Metadata/KeyComparer.cs index 64c19618055..95a7e07ffcb 100644 --- a/src/EFCore/Metadata/KeyComparer.cs +++ b/src/EFCore/Metadata/KeyComparer.cs @@ -12,14 +12,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata /// /// /// An implementation of and to compare - /// instances. + /// instances. /// /// /// This type is typically used by database providers (and other extensions). It is generally /// not used in application code. /// /// - public sealed class KeyComparer : IEqualityComparer, IComparer + public sealed class KeyComparer : IEqualityComparer, IComparer { private KeyComparer() { @@ -36,7 +36,7 @@ private KeyComparer() /// The first object to compare. /// The second object to compare. /// A negative number if 'x' is less than 'y'; a positive number if 'x' is greater than 'y'; zero otherwise. - public int Compare(IKey? x, IKey? y) + public int Compare(IReadOnlyKey? x, IReadOnlyKey? y) { var result = PropertyListComparer.Instance.Compare(x?.Properties, y?.Properties); return result != 0 ? result : EntityTypeFullNameComparer.Instance.Compare(x?.DeclaringEntityType, y?.DeclaringEntityType); @@ -48,7 +48,7 @@ public int Compare(IKey? x, IKey? y) /// The first object to compare. /// The second object to compare. /// if the specified objects are equal; otherwise, . - public bool Equals(IKey? x, IKey? y) + public bool Equals(IReadOnlyKey? x, IReadOnlyKey? y) => Compare(x, y) == 0; /// @@ -56,7 +56,7 @@ public bool Equals(IKey? x, IKey? y) /// /// The for which a hash code is to be returned. /// A hash code for the specified object. - public int GetHashCode(IKey obj) + public int GetHashCode(IReadOnlyKey obj) { var hashCode = new HashCode(); hashCode.Add(obj.Properties, PropertyListComparer.Instance); diff --git a/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs index b30165b0213..4f4034c5651 100644 --- a/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs +++ b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs @@ -83,7 +83,7 @@ public override ParameterBinding Bind( } while (baseType != null); - return Bind(entityType, parameterType); + return Bind((IEntityType)entityType, parameterType); } /// @@ -110,7 +110,7 @@ public override ParameterBinding Bind( } while (baseType != null); - return Bind(entityType, parameterType); + return Bind((IEntityType)entityType, parameterType); } private static ParameterBinding Bind(IEntityType entityType, Type parameterType) diff --git a/src/EFCore/Metadata/ServiceParameterBindingFactory.cs b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs index 4decec7aa4e..f3957eb7dc5 100644 --- a/src/EFCore/Metadata/ServiceParameterBindingFactory.cs +++ b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs @@ -64,7 +64,7 @@ public virtual ParameterBinding Bind(IMutableEntityType entityType, Type paramet return new DependencyInjectionParameterBinding( _serviceType, _serviceType, - entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); } /// @@ -86,7 +86,7 @@ public virtual ParameterBinding Bind( return new DependencyInjectionParameterBinding( _serviceType, _serviceType, - entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); + (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType)); } } } diff --git a/src/EFCore/ModelBuilder.cs b/src/EFCore/ModelBuilder.cs index 5261de4a36a..8a2c9aed00f 100644 --- a/src/EFCore/ModelBuilder.cs +++ b/src/EFCore/ModelBuilder.cs @@ -544,7 +544,7 @@ public virtual ModelBuilder UsePropertyAccessMode(PropertyAccessMode propertyAcc /// processing happens automatically when using ; this method allows it to be run /// explicitly in cases where the automatic execution is not possible. /// - /// The finalized . + /// The finalized model. public virtual IModel FinalizeModel() => Builder.Metadata.FinalizeModel(); diff --git a/src/EFCore/Update/Internal/UpdateAdapter.cs b/src/EFCore/Update/Internal/UpdateAdapter.cs index 963e5c90907..8a53b510f92 100644 --- a/src/EFCore/Update/Internal/UpdateAdapter.cs +++ b/src/EFCore/Update/Internal/UpdateAdapter.cs @@ -166,7 +166,6 @@ public virtual IUpdateEntry CreateEntry( /// 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 IModel Model - => _stateManager.Model; + public virtual IModel Model => _stateManager.Model; } } diff --git a/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs b/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs index 540b2bca304..9fa5f21ffc2 100644 --- a/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/CosmosApiConsistencyTest.cs @@ -37,14 +37,30 @@ public class CosmosApiConsistencyFixture : ApiConsistencyFixtureBase }; public override - List<(Type Type, Type ReadonlyExtensions, Type MutableExtensions, Type ConventionExtensions, Type - ConventionBuilderExtensions)> MetadataExtensionTypes { get; } + List<(Type Type, + Type ReadonlyExtensions, + Type MutableExtensions, + Type ConventionExtensions, + Type ConventionBuilderExtensions, + Type RuntimeExtensions)> MetadataExtensionTypes { get; } = new() { - (typeof(IModel), typeof(CosmosModelExtensions), typeof(CosmosModelExtensions), typeof(CosmosModelExtensions), - typeof(CosmosModelBuilderExtensions)), - (typeof(IProperty), typeof(CosmosPropertyExtensions), typeof(CosmosPropertyExtensions), - typeof(CosmosPropertyExtensions), typeof(CosmosPropertyBuilderExtensions)) + ( + typeof(IReadOnlyModel), + typeof(CosmosModelExtensions), + typeof(CosmosModelExtensions), + typeof(CosmosModelExtensions), + typeof(CosmosModelBuilderExtensions), + null + ), + ( + typeof(IReadOnlyProperty), + typeof(CosmosPropertyExtensions), + typeof(CosmosPropertyExtensions), + typeof(CosmosPropertyExtensions), + typeof(CosmosPropertyBuilderExtensions), + null + ) }; } } diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs index 9ace098eac6..9d5fad851e6 100644 --- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs +++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs @@ -333,18 +333,46 @@ public bool HasSharedClrType public bool IsPropertyBag => throw new NotImplementedException(); + IReadOnlyEntityType IReadOnlyEntityType.BaseType + => throw new NotImplementedException(); + + IReadOnlyModel IReadOnlyTypeBase.Model + => throw new NotImplementedException(); + + public IEnumerable FindDeclaredForeignKeys(IReadOnlyList properties) + => throw new NotImplementedException(); + + public INavigation FindDeclaredNavigation(string name) + => throw new NotImplementedException(); + + public IProperty FindDeclaredProperty(string name) + => throw new NotImplementedException(); + public IForeignKey FindForeignKey(IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType) => throw new NotImplementedException(); + public IForeignKey FindForeignKey( + IReadOnlyList properties, IReadOnlyKey principalKey, IReadOnlyEntityType principalEntityType) + => throw new NotImplementedException(); + + public IEnumerable FindForeignKeys(IReadOnlyList properties) + => throw new NotImplementedException(); + public IIndex FindIndex(IReadOnlyList properties) => throw new NotImplementedException(); public IIndex FindIndex(string name) => throw new NotImplementedException(); + public IIndex FindIndex(IReadOnlyList properties) + => throw new NotImplementedException(); + public IKey FindKey(IReadOnlyList properties) => throw new NotImplementedException(); + public IKey FindKey(IReadOnlyList properties) + => throw new NotImplementedException(); + public IKey FindPrimaryKey() => throw new NotImplementedException(); @@ -357,6 +385,39 @@ public IServiceProperty FindServiceProperty(string name) public ISkipNavigation FindSkipNavigation(string name) => throw new NotImplementedException(); + public IEnumerable GetDeclaredForeignKeys() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredIndexes() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredKeys() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredNavigations() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredProperties() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredReferencingForeignKeys() + => throw new NotImplementedException(); + + public IEnumerable GetDeclaredServiceProperties() + => throw new NotImplementedException(); + + public IEnumerable GetDerivedForeignKeys() + => throw new NotImplementedException(); + + public IEnumerable GetDerivedIndexes() + => throw new NotImplementedException(); + + public IEnumerable GetDirectlyDerivedTypes() + => throw new NotImplementedException(); + + public IEnumerable GetForeignKeyProperties() + => throw new NotImplementedException(); + public IEnumerable GetForeignKeys() => throw new NotImplementedException(); @@ -369,11 +430,60 @@ public IEnumerable GetKeys() public IEnumerable GetProperties() => throw new NotImplementedException(); + public IEnumerable GetReferencingForeignKeys() + => throw new NotImplementedException(); + public IEnumerable GetServiceProperties() => throw new NotImplementedException(); public IEnumerable GetSkipNavigations() => throw new NotImplementedException(); + + public IEnumerable GetValueGeneratingProperties() + => throw new NotImplementedException(); + + IReadOnlyForeignKey IReadOnlyEntityType.FindForeignKey( + IReadOnlyList properties, IReadOnlyKey principalKey, IReadOnlyEntityType principalEntityType) + => throw new NotImplementedException(); + + IReadOnlyIndex IReadOnlyEntityType.FindIndex(IReadOnlyList properties) + => throw new NotImplementedException(); + + IReadOnlyIndex IReadOnlyEntityType.FindIndex(string name) + => throw new NotImplementedException(); + + IReadOnlyKey IReadOnlyEntityType.FindKey(IReadOnlyList properties) + => throw new NotImplementedException(); + + IReadOnlyKey IReadOnlyEntityType.FindPrimaryKey() + => throw new NotImplementedException(); + + IReadOnlyProperty IReadOnlyEntityType.FindProperty(string name) + => throw new NotImplementedException(); + + IReadOnlyServiceProperty IReadOnlyEntityType.FindServiceProperty(string name) + => throw new NotImplementedException(); + + IReadOnlySkipNavigation IReadOnlyEntityType.FindSkipNavigation(string name) + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetForeignKeys() + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetIndexes() + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetKeys() + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetProperties() + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetServiceProperties() + => throw new NotImplementedException(); + + IEnumerable IReadOnlyEntityType.GetSkipNavigations() + => throw new NotImplementedException(); } } } diff --git a/test/EFCore.Cosmos.Tests/Metadata/Conventions/CosmosConventionSetBuilderTests.cs b/test/EFCore.Cosmos.Tests/Metadata/Conventions/CosmosConventionSetBuilderTests.cs index 5a5b420afb1..0bf01572741 100644 --- a/test/EFCore.Cosmos.Tests/Metadata/Conventions/CosmosConventionSetBuilderTests.cs +++ b/test/EFCore.Cosmos.Tests/Metadata/Conventions/CosmosConventionSetBuilderTests.cs @@ -9,12 +9,12 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { public class CosmosConventionSetBuilderTests : ConventionSetBuilderTests { - public override IModel Can_build_a_model_with_default_conventions_without_DI() + public override IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI() { return null; } - public override IModel Can_build_a_model_with_default_conventions_without_DI_new() + public override IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI_new() { var model = base.Can_build_a_model_with_default_conventions_without_DI_new(); diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index 46838bd9e7f..301bb31e568 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -389,13 +389,13 @@ public void Snapshot_with_enum_discriminator_uses_converted_values() eb.Property("EnumDiscriminator").HasConversion(); }); - modelBuilder.FinalizeModel(); + var finalizedModel = modelBuilder.FinalizeModel(); var modelSnapshotCode = generator.GenerateSnapshot( "MyNamespace", typeof(MyContext), "MySnapshot", - modelBuilder.Model); + finalizedModel); var snapshotModel = CompileModelSnapshot(modelSnapshotCode, "MyNamespace.MySnapshot").Model; @@ -427,7 +427,7 @@ private static void AssertConverter(ValueConverter valueConverter, string expect var sb = new IndentedStringBuilder(); - generator.TestGeneratePropertyAnnotations(property, sb); + generator.TestGeneratePropertyAnnotations((IProperty)property, sb); Assert.Equal(expected + _nl + ".HasMaxLength(1000)", sb.ToString()); } @@ -519,12 +519,14 @@ protected override void Down(MigrationBuilder migrationBuilder) eb.HasKey("Id"); }); + var finalizedModel = modelBuilder.FinalizeModel(); + var migrationMetadataCode = generator.GenerateMetadata( "MyNamespace", typeof(MyContext), "MyMigration", "20150511161616_MyMigration", - modelBuilder.Model); + finalizedModel); Assert.Equal( @"// using System.Text.RegularExpressions; @@ -649,13 +651,13 @@ public void Snapshots_compile() entityType.SetPrimaryKey(property2); - modelBuilder.FinalizeModel(); + var finalizedModel = modelBuilder.FinalizeModel(); var modelSnapshotCode = generator.GenerateSnapshot( "MyNamespace", typeof(MyContext), "MySnapshot", - model); + finalizedModel); Assert.Equal( @"// using System; @@ -765,13 +767,13 @@ public void Snapshot_with_default_values_are_round_tripped() eb.HasKey(e => e.Boolean); }); - modelBuilder.FinalizeModel(); + var finalizedModel = modelBuilder.FinalizeModel(); var modelSnapshotCode = generator.GenerateSnapshot( "MyNamespace", typeof(MyContext), "MySnapshot", - modelBuilder.Model); + finalizedModel); var snapshot = CompileModelSnapshot(modelSnapshotCode, "MyNamespace.MySnapshot"); var entityType = snapshot.Model.GetEntityTypes().Single(); diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 6e99aa8c2a2..bcf68177e72 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -689,7 +689,7 @@ public virtual void ProductVersion_is_stored_in_snapshot() { var modelBuilder = CreateConventionalModelBuilder(); var generator = CreateMigrationsGenerator(); - var code = generator.GenerateSnapshot("RootNamespace", typeof(DbContext), "Snapshot", modelBuilder.Model); + var code = generator.GenerateSnapshot("RootNamespace", typeof(DbContext), "Snapshot", (IModel)modelBuilder.Model); Assert.Contains(@".HasAnnotation(""ProductVersion"",", code); var modelFromSnapshot = BuildModelFromSnapshotSource(code); @@ -1509,7 +1509,7 @@ public virtual void Can_override_table_name_for_many_to_many_join_table_stored_i [ConditionalFact] public virtual void TableName_preserved_when_generic() { - IModel originalModel = null; + IReadOnlyModel originalModel = null; Test( builder => @@ -1621,7 +1621,7 @@ public virtual void Shared_columns_are_stored_in_the_snapshot() [ConditionalFact] public virtual void PrimaryKey_name_preserved_when_generic() { - IModel originalModel = null; + IReadOnlyModel originalModel = null; Test( builder => @@ -1659,7 +1659,7 @@ public virtual void PrimaryKey_name_preserved_when_generic() [ConditionalFact] public virtual void AlternateKey_name_preserved_when_generic() { - IModel originalModel = null; + IReadOnlyModel originalModel = null; Test( builder => @@ -4265,7 +4265,7 @@ public virtual void ForeignKey_deleteBehavior_is_stored_in_snapshot_for_one_to_o [ConditionalFact] public virtual void ForeignKey_name_preserved_when_generic() { - IModel originalModel = null; + IReadOnlyModel originalModel = null; Test( builder => diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpModelGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpModelGeneratorTest.cs index 77fa2699b2d..7fcbfa1b67b 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpModelGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpModelGeneratorTest.cs @@ -30,11 +30,8 @@ public void WriteCode_works() var modelBuilder = RelationalTestHelpers.Instance.CreateConventionBuilder(); modelBuilder.Entity("TestEntity").Property("Id").HasAnnotation(ScaffoldingAnnotationNames.ColumnOrdinal, 0); - var model = (Model)modelBuilder.Model; - var finalizedModel = model.ConventionDispatcher.OnModelFinalizing(model.Builder)?.Metadata; - var result = generator.GenerateModel( - finalizedModel, + modelBuilder.FinalizeModel(), new ModelCodeGenerationOptions { ModelNamespace = "TestNamespace", diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ModelCodeGeneratorTestBase.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ModelCodeGeneratorTestBase.cs index 31c30ae5966..c78f7503f45 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/ModelCodeGeneratorTestBase.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ModelCodeGeneratorTestBase.cs @@ -25,7 +25,6 @@ protected void Test( var modelBuilder = SqlServerTestHelpers.Instance.CreateConventionBuilder(); modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion); buildModel(modelBuilder); - var _ = modelBuilder.Model.GetEntityTypeErrors(); var model = modelBuilder.FinalizeModel(); diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingMetadataExtensionsTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingMetadataExtensionsTest.cs index 70cdd81341c..ff534684213 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingMetadataExtensionsTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ScaffoldingMetadataExtensionsTest.cs @@ -16,14 +16,16 @@ public void It_sets_gets_entity_type_errors() { IMutableModel model = new Model(); - model.GetEntityTypeErrors().Add("ET", "FAIL!"); + Assert.Empty(model.GetEntityTypeErrors().Values); + + model.GetOrCreateEntityTypeErrors().Add("ET", "FAIL!"); Assert.Equal("FAIL!", model.GetEntityTypeErrors()["ET"]); model.SetEntityTypeErrors(new Dictionary()); Assert.Empty(model.GetEntityTypeErrors().Values); - model.GetEntityTypeErrors()["ET"] = "FAIL 2!"; - model.GetEntityTypeErrors().Clear(); + model.GetOrCreateEntityTypeErrors()["ET"] = "FAIL 2!"; + model.GetOrCreateEntityTypeErrors().Clear(); Assert.Empty(model.GetEntityTypeErrors().Values); } diff --git a/test/EFCore.InMemory.FunctionalTests/ShadowStateUpdateTest.cs b/test/EFCore.InMemory.FunctionalTests/ShadowStateUpdateTest.cs index a44d36e60c6..65cb012a96c 100644 --- a/test/EFCore.InMemory.FunctionalTests/ShadowStateUpdateTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/ShadowStateUpdateTest.cs @@ -15,13 +15,14 @@ public class ShadowStateUpdateTest : IClassFixture [ConditionalFact] public async Task Can_add_update_delete_end_to_end_using_partial_shadow_state() { - IMutableModel model = InMemoryTestHelpers.Instance.CreateConventionBuilder().Model; + var modelBuilder = InMemoryTestHelpers.Instance.CreateConventionBuilder(); + var entityTypeBuilder = modelBuilder.Entity(); + entityTypeBuilder.Property("Name"); - var customerType = model.AddEntityType(typeof(Customer)); - customerType.AddProperty("Name", typeof(string)); + var customerType = (IEntityType)entityTypeBuilder.Metadata; var optionsBuilder = new DbContextOptionsBuilder() - .UseModel(model.FinalizeModel()) + .UseModel(modelBuilder.FinalizeModel()) .UseInMemoryDatabase(nameof(ShadowStateUpdateTest)) .UseInternalServiceProvider(_fixture.ServiceProvider); diff --git a/test/EFCore.InMemory.Tests/InMemoryDatabaseCreatorTest.cs b/test/EFCore.InMemory.Tests/InMemoryDatabaseCreatorTest.cs index cf7ea9199b2..bd905a2a743 100644 --- a/test/EFCore.InMemory.Tests/InMemoryDatabaseCreatorTest.cs +++ b/test/EFCore.InMemory.Tests/InMemoryDatabaseCreatorTest.cs @@ -140,7 +140,7 @@ private static IModel CreateModel() b.HasData(new Test { Id = 1 }); }); - return modelBuilder.Model; + return modelBuilder.FinalizeModel(); } private class Test diff --git a/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalDatabaseCleaner.cs b/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalDatabaseCleaner.cs index 0809f5a3242..647c30a7f0c 100644 --- a/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalDatabaseCleaner.cs +++ b/test/EFCore.Relational.Specification.Tests/TestUtilities/RelationalDatabaseCleaner.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Migrations.Operations; using Microsoft.EntityFrameworkCore.Scaffolding; @@ -96,7 +97,7 @@ public virtual void Clean(DatabaseFacade facade) if (operations.Count > 0) { - var commands = sqlGenerator.Generate(operations); + var commands = sqlGenerator.Generate(operations, null); executor.ExecuteNonQuery(commands, connection); } diff --git a/test/EFCore.Relational.Specification.Tests/Update/UpdateSqlGeneratorTestBase.cs b/test/EFCore.Relational.Specification.Tests/Update/UpdateSqlGeneratorTestBase.cs index b6361a1b32b..b42b552b867 100644 --- a/test/EFCore.Relational.Specification.Tests/Update/UpdateSqlGeneratorTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Update/UpdateSqlGeneratorTestBase.cs @@ -667,11 +667,12 @@ protected virtual string GetIdentityWhereCondition(string columnName) protected ModificationCommand CreateInsertCommand(bool identityKey = true, bool isComputed = true, bool defaultsOnly = false) { - var duckType = GetDuckType(); - var stateManager = TestHelpers.CreateContextServices(duckType.Model.FinalizeModel()).GetRequiredService(); + var model = GetDuckType().Model.FinalizeModel(); + var stateManager = TestHelpers.CreateContextServices(model).GetRequiredService(); var entry = stateManager.GetOrCreateEntry(new Duck()); var generator = new ParameterNameGenerator(); + var duckType = model.FindEntityType(typeof(Duck)); var idProperty = duckType.FindProperty(nameof(Duck.Id)); var nameProperty = duckType.FindProperty(nameof(Duck.Name)); var quacksProperty = duckType.FindProperty(nameof(Duck.Quacks)); @@ -706,11 +707,12 @@ protected ModificationCommand CreateInsertCommand(bool identityKey = true, bool protected ModificationCommand CreateUpdateCommand(bool isComputed = true, bool concurrencyToken = true) { - var duckType = GetDuckType(); - var stateManager = TestHelpers.CreateContextServices(duckType.Model.FinalizeModel()).GetRequiredService(); + var model = GetDuckType().Model.FinalizeModel(); + var stateManager = TestHelpers.CreateContextServices(model).GetRequiredService(); var entry = stateManager.GetOrCreateEntry(new Duck()); var generator = new ParameterNameGenerator(); + var duckType = model.FindEntityType(typeof(Duck)); var idProperty = duckType.FindProperty(nameof(Duck.Id)); var nameProperty = duckType.FindProperty(nameof(Duck.Name)); var quacksProperty = duckType.FindProperty(nameof(Duck.Quacks)); @@ -740,11 +742,12 @@ protected ModificationCommand CreateUpdateCommand(bool isComputed = true, bool c protected ModificationCommand CreateDeleteCommand(bool concurrencyToken = true) { - var duckType = GetDuckType(); - var stateManager = TestHelpers.CreateContextServices(duckType.Model.FinalizeModel()).GetRequiredService(); + var model = GetDuckType().Model.FinalizeModel(); + var stateManager = TestHelpers.CreateContextServices(model).GetRequiredService(); var entry = stateManager.GetOrCreateEntry(new Duck()); var generator = new ParameterNameGenerator(); + var duckType = model.FindEntityType(typeof(Duck)); var idProperty = duckType.FindProperty(nameof(Duck.Id)); var concurrencyProperty = duckType.FindProperty(nameof(Duck.ConcurrencyToken)); var columnModifications = new[] diff --git a/test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs b/test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs index 3dd18625a57..5829436d7de 100644 --- a/test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs +++ b/test/EFCore.Relational.Tests/Design/AnnotationCodeGeneratorTest.cs @@ -19,7 +19,7 @@ public void IsTableExcludedFromMigrations_false_is_handled_by_convention() var entityType = modelBuilder.Model.GetEntityTypes().Single(); var annotations = entityType.GetAnnotations().ToDictionary(a => a.Name, a => a); - CreateGenerator().RemoveAnnotationsHandledByConventions(entityType, annotations); + CreateGenerator().RemoveAnnotationsHandledByConventions((IEntityType)entityType, annotations); Assert.DoesNotContain(RelationalAnnotationNames.IsTableExcludedFromMigrations, annotations.Keys); } diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index d5b82803a50..ca3941c24b0 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -949,8 +949,8 @@ public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy_when_on public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hierarchy() { var modelBuilder = CreateConventionalModelBuilder(); - IForeignKey fk1 = null; - IForeignKey fk2 = null; + IReadOnlyForeignKey fk1 = null; + IReadOnlyForeignKey fk2 = null; modelBuilder.Entity(); modelBuilder.Entity( @@ -995,8 +995,8 @@ public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hier public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hierarchy_name_configured_explicitly() { var modelBuilder = CreateConventionalModelBuilder(); - IForeignKey fk1 = null; - IForeignKey fk2 = null; + IReadOnlyForeignKey fk1 = null; + IReadOnlyForeignKey fk2 = null; modelBuilder.Entity(); modelBuilder.Entity( diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs index 39d4b0f0a37..66cd0a0f9ff 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalBuilderExtensionsTest.cs @@ -841,7 +841,7 @@ public void Can_create_named_sequence() ValidateNamedSequence(sequence); } - private static void ValidateNamedSequence(ISequence sequence) + private static void ValidateNamedSequence(IReadOnlySequence sequence) { Assert.Equal("Snook", sequence.Name); Assert.Null(sequence.Schema); @@ -864,7 +864,7 @@ public void Can_create_schema_named_sequence() ValidateSchemaNamedSequence(sequence); } - private static void ValidateSchemaNamedSequence(ISequence sequence) + private static void ValidateSchemaNamedSequence(IReadOnlySequence sequence) { Assert.Equal("Snook", sequence.Name); Assert.Equal("Tasty", sequence.Schema); @@ -949,7 +949,7 @@ public void Can_create_named_sequence_with_specific_facets_using_nested_closure_ ValidateNamedSpecificSequence(sequence); } - private static void ValidateNamedSpecificSequence(ISequence sequence) + private static void ValidateNamedSpecificSequence(IReadOnlySequence sequence) { Assert.Equal("Snook", sequence.Name); Assert.Null(sequence.Schema); @@ -1224,7 +1224,7 @@ private void AssertIsGeneric(ReferenceReferenceBuilder _) protected virtual ModelBuilder CreateConventionModelBuilder() => RelationalTestHelpers.Instance.CreateConventionBuilder(); - private static void ValidateSchemaNamedSpecificSequence(ISequence sequence) + private static void ValidateSchemaNamedSpecificSequence(IReadOnlySequence sequence) { Assert.Equal("Snook", sequence.Name); Assert.Equal("Tasty", sequence.Schema); diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalMetadataBuilderExtensionsTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalMetadataBuilderExtensionsTest.cs index 72abc764e76..5e22dfd01ea 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalMetadataBuilderExtensionsTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalMetadataBuilderExtensionsTest.cs @@ -202,7 +202,7 @@ public void Can_access_relationship() public void Can_access_check_constraint() { var typeBuilder = CreateBuilder().Entity(typeof(Splot), ConfigurationSource.Convention); - IEntityType entityType = typeBuilder.Metadata; + IReadOnlyEntityType entityType = typeBuilder.Metadata; Assert.NotNull(typeBuilder.HasCheckConstraint("Splew", "s > p")); Assert.Equal("Splew", entityType.GetCheckConstraints().Single().Name); diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs index 1952eaa2c28..82a9f0e3712 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalMetadataExtensionsTest.cs @@ -359,17 +359,15 @@ public void Can_get_and_set_index_name() .HasIndex(e => e.Id) .Metadata; -#pragma warning disable CS0618 // Type or member is obsolete - Assert.Equal("IX_Customer_Id", index.GetName()); + Assert.Equal("IX_Customer_Id", index.GetDatabaseName()); - index.SetName("MyIndex"); + index.SetDatabaseName("MyIndex"); - Assert.Equal("MyIndex", index.GetName()); + Assert.Equal("MyIndex", index.GetDatabaseName()); - index.SetName(null); + index.SetDatabaseName(null); - Assert.Equal("IX_Customer_Id", index.GetName()); -#pragma warning restore CS0618 // Type or member is obsolete + Assert.Equal("IX_Customer_Id", index.GetDatabaseName()); } [ConditionalFact] diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs index c3371f9ad2e..651ee07fbbc 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.TestUtilities; @@ -24,7 +25,7 @@ public void GetRelationalModel_throws_if_convention_has_not_run() Assert.Equal( CoreStrings.ModelNotFinalized("GetRelationalModel"), Assert.Throws( - () => modelBuilder.Model.GetRelationalModel()).Message); + () => ((IModel)modelBuilder.Model).GetRelationalModel()).Message); } [ConditionalTheory] @@ -36,6 +37,9 @@ 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); @@ -628,7 +632,7 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie { cb.ToTable("ExtraSpecialCustomer", "ExtraSpecialSchema"); } - }); + }); modelBuilder.Entity( ob => diff --git a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs index 04b39120e91..edc94392cc6 100644 --- a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs +++ b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs @@ -47,20 +47,37 @@ public void Readonly_relational_metadata_methods_have_expected_name() public class RelationalApiConsistencyFixture : ApiConsistencyFixtureBase { - private static Dictionary _metadataTypes + private static Dictionary _metadataTypes => new() { { - typeof(IDbFunction), - (typeof(IMutableDbFunction), typeof(IConventionDbFunction), typeof(IConventionDbFunctionBuilder)) + typeof(IReadOnlyDbFunction), + (typeof(IMutableDbFunction), + typeof(IConventionDbFunction), + typeof(IConventionDbFunctionBuilder), + typeof(IDbFunction)) }, { - typeof(IDbFunctionParameter), - (typeof(IMutableDbFunctionParameter), typeof(IConventionDbFunctionParameter), - typeof(IConventionDbFunctionParameterBuilder)) + typeof(IReadOnlyDbFunctionParameter), + (typeof(IMutableDbFunctionParameter), + typeof(IConventionDbFunctionParameter), + typeof(IConventionDbFunctionParameterBuilder), + typeof(IDbFunctionParameter)) }, - { typeof(ISequence), (typeof(IMutableSequence), typeof(IConventionSequence), typeof(IConventionSequenceBuilder)) }, - { typeof(ICheckConstraint), (typeof(IMutableCheckConstraint), typeof(IConventionCheckConstraint), null) } + { + typeof(IReadOnlySequence), + (typeof(IMutableSequence), + typeof(IConventionSequence), + typeof(IConventionSequenceBuilder), + typeof(ISequence)) + }, + { + typeof(IReadOnlyCheckConstraint), + (typeof(IMutableCheckConstraint), + typeof(IConventionCheckConstraint), + null, + typeof(ICheckConstraint)) + } }; public virtual HashSet RelationalMetadataTypes { get; } = new() @@ -104,22 +121,62 @@ public class RelationalApiConsistencyFixture : ApiConsistencyFixtureBase }; public override - List<(Type Type, Type ReadonlyExtensions, Type MutableExtensions, Type ConventionExtensions, Type - ConventionBuilderExtensions)> MetadataExtensionTypes { get; } + List<(Type Type, + Type ReadonlyExtensions, + Type MutableExtensions, + Type ConventionExtensions, + Type ConventionBuilderExtensions, + Type RuntimeExtensions)> MetadataExtensionTypes { get; } = new() { - (typeof(IModel), typeof(RelationalModelExtensions), typeof(RelationalModelExtensions), - typeof(RelationalModelExtensions), typeof(RelationalModelBuilderExtensions)), - (typeof(IEntityType), typeof(RelationalEntityTypeExtensions), typeof(RelationalEntityTypeExtensions), - typeof(RelationalEntityTypeExtensions), typeof(RelationalEntityTypeBuilderExtensions)), - (typeof(IKey), typeof(RelationalKeyExtensions), typeof(RelationalKeyExtensions), typeof(RelationalKeyExtensions), - typeof(RelationalKeyBuilderExtensions)), - (typeof(IForeignKey), typeof(RelationalForeignKeyExtensions), typeof(RelationalForeignKeyExtensions), - typeof(RelationalForeignKeyExtensions), typeof(RelationalForeignKeyBuilderExtensions)), - (typeof(IProperty), typeof(RelationalPropertyExtensions), typeof(RelationalPropertyExtensions), - typeof(RelationalPropertyExtensions), typeof(RelationalPropertyBuilderExtensions)), - (typeof(IIndex), typeof(RelationalIndexExtensions), typeof(RelationalIndexExtensions), - typeof(RelationalIndexExtensions), typeof(RelationalIndexBuilderExtensions)) + ( + typeof(IReadOnlyModel), + typeof(RelationalModelExtensions), + typeof(RelationalModelExtensions), + typeof(RelationalModelExtensions), + typeof(RelationalModelBuilderExtensions), + null + ), + ( + typeof(IReadOnlyEntityType), + typeof(RelationalEntityTypeExtensions), + typeof(RelationalEntityTypeExtensions), + typeof(RelationalEntityTypeExtensions), + typeof(RelationalEntityTypeBuilderExtensions), + null + ), + ( + typeof(IReadOnlyKey), + typeof(RelationalKeyExtensions), + typeof(RelationalKeyExtensions), + typeof(RelationalKeyExtensions), + typeof(RelationalKeyBuilderExtensions), + null + ), + ( + typeof(IReadOnlyForeignKey), + typeof(RelationalForeignKeyExtensions), + typeof(RelationalForeignKeyExtensions), + typeof(RelationalForeignKeyExtensions), + typeof(RelationalForeignKeyBuilderExtensions), + null + ), + ( + typeof(IReadOnlyProperty), + typeof(RelationalPropertyExtensions), + typeof(RelationalPropertyExtensions), + typeof(RelationalPropertyExtensions), + typeof(RelationalPropertyBuilderExtensions), + null + ), + ( + typeof(IReadOnlyIndex), + typeof(RelationalIndexExtensions), + typeof(RelationalIndexExtensions), + typeof(RelationalIndexExtensions), + typeof(RelationalIndexBuilderExtensions), + null + ) }; public override HashSet NonVirtualMethods { get; } @@ -134,9 +191,9 @@ public override public override HashSet UnmatchedMetadataMethods { get; } = new() { - typeof(IDbFunction).GetMethod("get_ReturnEntityType"), typeof(IMutableSequence).GetMethod("set_ClrType"), - typeof(RelationalPropertyExtensions).GetMethod(nameof(RelationalPropertyExtensions.FindOverrides)), + typeof(RelationalPropertyExtensions).GetRuntimeMethods().Single(m => m.Name == nameof(RelationalPropertyExtensions.FindOverrides) + && m.ReturnType == typeof(IReadOnlyAnnotatable)), typeof(RelationalEntityTypeBuilderExtensions).GetMethod( nameof(RelationalEntityTypeBuilderExtensions.ExcludeTableFromMigrations)) }; diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs index 89b09da618f..467338fedd5 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs @@ -76,13 +76,13 @@ public void Does_type_mapping_from_btye_array_greater_than_unbounded_max() private RelationalTypeMapping GetTypeMapping(Type propertyType, int? maxLength = null) { - var property = CreateEntityType().AddProperty("MyProp", propertyType); + var property = CreateEntityType().AddProperty("MyProp", propertyType); if (maxLength.HasValue) { property.SetMaxLength(maxLength); } - return GetMapping(property); + return GetMapping((IProperty)property); } [ConditionalFact] @@ -124,10 +124,10 @@ public void Does_type_mapping_from_named_binary_with_no_MaxLength() private RelationalTypeMapping GetNamedMapping(Type propertyType, string typeName) { - var property = CreateEntityType().AddProperty("MyProp", propertyType); + var property = CreateEntityType().AddProperty("MyProp", propertyType); property.SetColumnType(typeName); - return GetMapping(property); + return GetMapping((IProperty)property); } [ConditionalFact] diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs index d8f8813fa7e..7872c241e9a 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTestBase.cs @@ -7,10 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Storage { public abstract class RelationalTypeMapperTestBase { - protected IMutableEntityType CreateEntityType() - => CreateModel().FindEntityType(typeof(MyType)); - - protected IMutableModel CreateModel() + protected IMutableEntityType CreateEntityType() { var builder = CreateModelBuilder(); @@ -26,9 +23,11 @@ protected IMutableModel CreateModel() builder.Entity().Property(e => e.PrecisionAndScale).HasPrecision(18, 7); builder.Entity(); - return builder.Model; + return builder.Model.FindEntityType(typeof(TEntity)); } + protected IModel CreateModel() => CreateEntityType().Model.FinalizeModel(); + protected abstract ModelBuilder CreateModelBuilder(); protected class MyType diff --git a/test/EFCore.Relational.Tests/Update/ModificationCommandComparerTest.cs b/test/EFCore.Relational.Tests/Update/ModificationCommandComparerTest.cs index 1e602b27f09..30110d7db22 100644 --- a/test/EFCore.Relational.Tests/Update/ModificationCommandComparerTest.cs +++ b/test/EFCore.Relational.Tests/Update/ModificationCommandComparerTest.cs @@ -32,19 +32,19 @@ public void Compare_returns_0_only_for_commands_that_are_equal() var stateManager = new DbContext(optionsBuilder.Options).GetService(); var entry1 = stateManager.GetOrCreateEntry(new object()); - entry1[key] = 1; + entry1[(IProperty)key] = 1; entry1.SetEntityState(EntityState.Added); var modificationCommandAdded = new ModificationCommand("A", null, new ParameterNameGenerator().GenerateNext, false, null); modificationCommandAdded.AddEntry(entry1, true); var entry2 = stateManager.GetOrCreateEntry(new object()); - entry2[key] = 2; + entry2[(IProperty)key] = 2; entry2.SetEntityState(EntityState.Modified); var modificationCommandModified = new ModificationCommand("A", null, new ParameterNameGenerator().GenerateNext, false, null); modificationCommandModified.AddEntry(entry2, true); var entry3 = stateManager.GetOrCreateEntry(new object()); - entry3[key] = 3; + entry3[(IProperty)key] = 3; entry3.SetEntityState(EntityState.Deleted); var modificationCommandDeleted = new ModificationCommand("A", null, new ParameterNameGenerator().GenerateNext, false, null); modificationCommandDeleted.AddEntry(entry3, true); @@ -174,13 +174,13 @@ private void Compare_returns_0_only_for_entries_that_have_same_key_values_generi var stateManager = new DbContext(optionsBuilder.Options).GetService(); var entry1 = stateManager.GetOrCreateEntry(new object()); - entry1[keyProperty] = value1; + entry1[(IProperty)keyProperty] = value1; entry1.SetEntityState(EntityState.Modified); var modificationCommand1 = new ModificationCommand("A", null, new ParameterNameGenerator().GenerateNext, false, null); modificationCommand1.AddEntry(entry1, true); var entry2 = stateManager.GetOrCreateEntry(new object()); - entry2[keyProperty] = value2; + entry2[(IProperty)keyProperty] = value2; entry2.SetEntityState(EntityState.Modified); var modificationCommand2 = new ModificationCommand("A", null, new ParameterNameGenerator().GenerateNext, false, null); modificationCommand2.AddEntry(entry2, true); diff --git a/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs b/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs index 3448d95e745..040b4f1a2f0 100644 --- a/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs +++ b/test/EFCore.Specification.Tests/ApiConsistencyTestBase.cs @@ -160,13 +160,13 @@ public void Metadata_types_have_expected_structure() "\r\n-- Errors: --\r\n" + string.Join(Environment.NewLine, errors)); } - private static readonly string MetadataNamespace = typeof(IModel).Namespace; + private static readonly string MetadataNamespace = typeof(IReadOnlyModel).Namespace; private static readonly string MetadataBuilderNamespace = typeof(IConventionModelBuilder).Namespace; - private string ValidateMetadata(KeyValuePair types) + private string ValidateMetadata(KeyValuePair types) { var readonlyType = types.Key; - var (mutableType, conventionType, conventionBuilderType) = types.Value; + var (mutableType, conventionType, conventionBuilderType, runtimeType) = types.Value; if (!readonlyType.IsAssignableFrom(mutableType)) { @@ -179,9 +179,9 @@ private string ValidateMetadata(KeyValuePair types) } if (typeof(IAnnotation) != readonlyType - && typeof(IAnnotatable) != readonlyType) + && typeof(IReadOnlyAnnotatable) != readonlyType) { - if (!typeof(IAnnotatable).IsAssignableFrom(readonlyType)) + if (!typeof(IReadOnlyAnnotatable).IsAssignableFrom(readonlyType)) { return $"{readonlyType.Name} should derive from IAnnotatable"; } @@ -269,8 +269,7 @@ private string MatchMutable((MethodInfo Readonly, MethodInfo Mutable) methodTupl { var (readonlyMethod, mutableMethod) = methodTuple; - (Type Mutable, Type Convention, Type _) expectedReturnTypes; - if (Fixture.MetadataTypes.TryGetValue(readonlyMethod.ReturnType, out expectedReturnTypes)) + if (Fixture.MetadataTypes.TryGetValue(readonlyMethod.ReturnType, out var expectedReturnTypes)) { if (mutableMethod == null) { @@ -502,6 +501,77 @@ private string ValidateConventionBuilderMethods(IReadOnlyList method return null; } + [ConditionalFact] + public void Runtime_metadata_types_have_matching_methods() + { + var errors = + Fixture.MetadataMethods.Select( + typeTuple => + from readOnlyMethod in typeTuple.ReadOnly + where !Fixture.UnmatchedMetadataMethods.Contains(readOnlyMethod) + join runtimeMethod in typeTuple.Runtime + on readOnlyMethod.Name equals runtimeMethod?.Name into runtimeGroup + from runtimeMethod in runtimeGroup.DefaultIfEmpty() + select (readOnlyMethod, runtimeMethod)) + .SelectMany(m => m.Select(MatchRuntime)) + .Where(e => e != null) + .ToList(); + + Assert.False( + errors.Count > 0, + "\r\n-- Mismatches: --\r\n" + string.Join(Environment.NewLine, errors)); + } + + private string MatchRuntime((MethodInfo ReadOnly, MethodInfo Runtime) methodTuple) + { + var (readOnlyMethod, runtimeMethod) = methodTuple; + + Type expectedReturnType; + if (readOnlyMethod.ReturnType == typeof(void)) + { + if (runtimeMethod == null) + { + return "No IRuntime equivalent of " + + $"{readOnlyMethod.DeclaringType.Name}.{readOnlyMethod.Name}({Format(readOnlyMethod.GetParameters())})"; + } + } + else if (Fixture.MutableMetadataTypes.TryGetValue(readOnlyMethod.ReturnType, out expectedReturnType)) + { + if (runtimeMethod == null) + { + return "No IRuntime equivalent of " + + $"{readOnlyMethod.DeclaringType.Name}.{readOnlyMethod.Name}({Format(readOnlyMethod.GetParameters())})"; + } + + if (runtimeMethod.ReturnType != expectedReturnType) + { + return $"{runtimeMethod.DeclaringType.Name}.{runtimeMethod.Name}({Format(runtimeMethod.GetParameters())})" + + $" expected to have {expectedReturnType.ShortDisplayName()} return type"; + } + } + else + { + var sequenceType = readOnlyMethod.ReturnType.TryGetSequenceType(); + if (sequenceType != null + && Fixture.MutableMetadataTypes.TryGetValue(sequenceType, out expectedReturnType)) + { + if (runtimeMethod == null) + { + return "No IRuntime equivalent of " + + $"{readOnlyMethod.DeclaringType.Name}.{readOnlyMethod.Name}({Format(readOnlyMethod.GetParameters())})"; + } + + if (runtimeMethod.ReturnType.TryGetSequenceType() != expectedReturnType) + { + return $"{runtimeMethod.DeclaringType.Name}.{runtimeMethod.Name}({Format(runtimeMethod.GetParameters())})" + + $" expected to have a return type that derives from IEnumerable<{expectedReturnType.Name}>."; + } + } + } + + return null; + } + [ConditionalFact] public void Readonly_metadata_methods_have_expected_name() { @@ -948,55 +1018,127 @@ protected ApiConsistencyFixtureBase() #pragma warning restore CS0618 // Type or member is obsolete }; - public Dictionary MetadataTypes { get; } + public Dictionary MetadataTypes { get; } = new() { - { typeof(IModel), (typeof(IMutableModel), typeof(IConventionModel), typeof(IConventionModelBuilder)) }, { - typeof(IAnnotatable), - (typeof(IMutableAnnotatable), typeof(IConventionAnnotatable), typeof(IConventionAnnotatableBuilder)) + typeof(IReadOnlyModel), + (typeof(IMutableModel), + typeof(IConventionModel), + typeof(IConventionModelBuilder), + typeof(IModel)) + }, + { + typeof(IReadOnlyAnnotatable), + (typeof(IMutableAnnotatable), + typeof(IConventionAnnotatable), + typeof(IConventionAnnotatableBuilder), + typeof(IAnnotatable)) }, - { typeof(IAnnotation), (typeof(IAnnotation), typeof(IConventionAnnotation), null) }, { - typeof(IEntityType), - (typeof(IMutableEntityType), typeof(IConventionEntityType), typeof(IConventionEntityTypeBuilder)) + typeof(IAnnotation), + (typeof(IAnnotation), + typeof(IConventionAnnotation), + null, + null) }, - { typeof(ITypeBase), (typeof(IMutableTypeBase), typeof(IConventionTypeBase), null) }, - { typeof(IKey), (typeof(IMutableKey), typeof(IConventionKey), typeof(IConventionKeyBuilder)) }, { - typeof(IForeignKey), - (typeof(IMutableForeignKey), typeof(IConventionForeignKey), typeof(IConventionForeignKeyBuilder)) + typeof(IReadOnlyEntityType), + (typeof(IMutableEntityType), + typeof(IConventionEntityType), + typeof(IConventionEntityTypeBuilder), + typeof(IEntityType)) }, - { typeof(IIndex), (typeof(IMutableIndex), typeof(IConventionIndex), typeof(IConventionIndexBuilder)) }, - { typeof(IProperty), (typeof(IMutableProperty), typeof(IConventionProperty), typeof(IConventionPropertyBuilder)) }, { - typeof(INavigation), - (typeof(IMutableNavigation), typeof(IConventionNavigation), typeof(IConventionNavigationBuilder)) + typeof(IReadOnlyTypeBase), + (typeof(IMutableTypeBase), + typeof(IConventionTypeBase), + null, + typeof(ITypeBase)) }, { - typeof(ISkipNavigation), - (typeof(IMutableSkipNavigation), typeof(IConventionSkipNavigation), typeof(IConventionSkipNavigationBuilder)) + typeof(IReadOnlyKey), + (typeof(IMutableKey), + typeof(IConventionKey), + typeof(IConventionKeyBuilder), + typeof(IKey)) }, { - typeof(IServiceProperty), - (typeof(IMutableServiceProperty), typeof(IConventionServiceProperty), typeof(IConventionServicePropertyBuilder)) + typeof(IReadOnlyForeignKey), + (typeof(IMutableForeignKey), + typeof(IConventionForeignKey), + typeof(IConventionForeignKeyBuilder), + typeof(IForeignKey)) }, - { typeof(INavigationBase), (typeof(IMutableNavigationBase), typeof(IConventionNavigationBase), null) }, - { typeof(IPropertyBase), (typeof(IMutablePropertyBase), typeof(IConventionPropertyBase), null) } + { + typeof(IReadOnlyIndex), + (typeof(IMutableIndex), + typeof(IConventionIndex), + typeof(IConventionIndexBuilder), + typeof(IIndex)) + }, + { + typeof(IReadOnlyProperty), + (typeof(IMutableProperty), + typeof(IConventionProperty), + typeof(IConventionPropertyBuilder), + typeof(IProperty)) + }, + { + typeof(IReadOnlyNavigation), + (typeof(IMutableNavigation), + typeof(IConventionNavigation), + typeof(IConventionNavigationBuilder), + typeof(INavigation)) + }, + { + typeof(IReadOnlySkipNavigation), + (typeof(IMutableSkipNavigation), + typeof(IConventionSkipNavigation), + typeof(IConventionSkipNavigationBuilder), + typeof(ISkipNavigation)) + }, + { + typeof(IReadOnlyServiceProperty), + (typeof(IMutableServiceProperty), + typeof(IConventionServiceProperty), + typeof(IConventionServicePropertyBuilder), + typeof(IServiceProperty)) + }, + { + typeof(IReadOnlyNavigationBase), + (typeof(IMutableNavigationBase), + typeof(IConventionNavigationBase), + null, + typeof(INavigationBase)) + }, + { + typeof(IReadOnlyPropertyBase), + (typeof(IMutablePropertyBase), + typeof(IConventionPropertyBase), + null, + typeof(IPropertyBase)) + } }; public Dictionary MutableMetadataTypes { get; } = new(); public Dictionary ConventionMetadataTypes { get; } = new(); public virtual - List<(Type Type, Type ReadonlyExtensions, Type MutableExtensions, Type ConventionExtensions, Type - ConventionBuilderExtensions)> MetadataExtensionTypes { get; } + List<(Type Type, + Type ReadonlyExtensions, + Type MutableExtensions, + Type ConventionExtensions, + Type ConventionBuilderExtensions, + Type RuntimeExtensions)> MetadataExtensionTypes { get; } = new(); - public List<(IReadOnlyList ReadOnly, IReadOnlyList Mutable, IReadOnlyList Convention, - IReadOnlyList ConventionBuilder)> - MetadataMethods { get; } - = new(); + public List<(IReadOnlyList ReadOnly, + IReadOnlyList Mutable, + IReadOnlyList Convention, + IReadOnlyList ConventionBuilder, + IReadOnlyList Runtime)> + MetadataMethods { get; } = new(); protected virtual void Initialize() { @@ -1009,33 +1151,42 @@ protected virtual void Initialize() foreach (var extensionTypeTuple in MetadataExtensionTypes) { var type = extensionTypeTuple.Type; - var (mutableType, conventionType, conventionBuilderType) = MetadataTypes[type]; - var readOnlyMethods = extensionTypeTuple.ReadonlyExtensions.GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == type).ToArray(); - var mutableMethods = extensionTypeTuple.MutableExtensions.GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == mutableType).ToArray(); - var conventionMethods = extensionTypeTuple.ConventionExtensions.GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == conventionType).ToArray(); + var (mutableType, conventionType, conventionBuilderType, runtimeType) = MetadataTypes[type]; + var readOnlyMethods = extensionTypeTuple.ReadonlyExtensions?.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == type).ToArray() + ?? new MethodInfo[0]; + var mutableMethods = extensionTypeTuple.MutableExtensions?.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == mutableType).ToArray() + ?? new MethodInfo[0]; + var conventionMethods = extensionTypeTuple.ConventionExtensions?.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == conventionType).ToArray() + ?? new MethodInfo[0]; var conventionBuilderMethods = extensionTypeTuple.ConventionBuilderExtensions ?.GetMethods(BindingFlags.Public | BindingFlags.Static) - .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == conventionBuilderType).ToArray(); - MetadataMethods.Add((readOnlyMethods, mutableMethods, conventionMethods, conventionBuilderMethods)); + .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == conventionBuilderType).ToArray() + ?? new MethodInfo[0]; + var runtimeMethods = extensionTypeTuple.RuntimeExtensions?.GetMethods(BindingFlags.Public | BindingFlags.Static) + .Where(m => !IsObsolete(m) && m.GetParameters().First().ParameterType == runtimeType).ToArray() + ?? new MethodInfo[0]; + MetadataMethods.Add((readOnlyMethods, mutableMethods, conventionMethods, conventionBuilderMethods, runtimeMethods)); } } - protected void AddInstanceMethods(Dictionary types) + protected void AddInstanceMethods(Dictionary types) { foreach (var typeTuple in types) { var readOnlyMethods = typeTuple.Key.GetMethods(PublicInstance) - .Where(m => !IsObsolete(m)).ToArray(); + .Where(m => !IsObsolete(m)).ToArray() ?? new MethodInfo[0]; var mutableMethods = typeTuple.Value.Mutable.GetMethods(PublicInstance) - .Where(m => !IsObsolete(m)).ToArray(); + .Where(m => !IsObsolete(m)).ToArray() ?? new MethodInfo[0]; var conventionMethods = typeTuple.Value.Convention.GetMethods(PublicInstance) - .Where(m => !IsObsolete(m)).ToArray(); + .Where(m => !IsObsolete(m)).ToArray() ?? new MethodInfo[0]; var conventionBuilderMethods = typeTuple.Value.ConventionBuilder?.GetMethods(PublicInstance) - .Where(m => !IsObsolete(m)).ToArray(); - MetadataMethods.Add((readOnlyMethods, mutableMethods, conventionMethods, conventionBuilderMethods)); + .Where(m => !IsObsolete(m)).ToArray() ?? new MethodInfo[0]; + var runtimeMethods = typeTuple.Value.Runtime?.GetMethods(PublicInstance) + .Where(m => !IsObsolete(m)).ToArray() ?? new MethodInfo[0]; + MetadataMethods.Add((readOnlyMethods, mutableMethods, conventionMethods, conventionBuilderMethods, runtimeMethods)); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/ForeignKeyStrictComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/ForeignKeyStrictComparer.cs index 56abfcbdf80..c285d32fbf4 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/ForeignKeyStrictComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/ForeignKeyStrictComparer.cs @@ -7,7 +7,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class ForeignKeyStrictComparer : IEqualityComparer, IComparer + public class ForeignKeyStrictComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; private readonly bool _compareNavigations; @@ -18,10 +18,10 @@ public ForeignKeyStrictComparer(bool compareAnnotations = true, bool compareNavi _compareNavigations = compareNavigations; } - public int Compare(IForeignKey x, IForeignKey y) + public int Compare(IReadOnlyForeignKey x, IReadOnlyForeignKey y) => ForeignKeyComparer.Instance.Compare(x, y); - public bool Equals(IForeignKey x, IForeignKey y) + public bool Equals(IReadOnlyForeignKey x, IReadOnlyForeignKey y) { if (x == null) { @@ -39,7 +39,7 @@ public bool Equals(IForeignKey x, IForeignKey y) && (!_compareAnnotations || x.GetAnnotations().SequenceEqual(y.GetAnnotations(), AnnotationComparer.Instance)); } - public int GetHashCode(IForeignKey obj) + public int GetHashCode(IReadOnlyForeignKey obj) => ForeignKeyComparer.Instance.GetHashCode(obj); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/MetadataExtensions.cs b/test/EFCore.Specification.Tests/TestUtilities/MetadataExtensions.cs index 5829109468c..dc695eb4a00 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/MetadataExtensions.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/MetadataExtensions.cs @@ -28,10 +28,10 @@ public static void ForEach(this IEnumerable @this, Action action) } } - public static IModel Clone(this IModel model) + public static IReadOnlyModel Clone(this IReadOnlyModel model) { IMutableModel modelClone = new Model(); - var clonedEntityTypes = new Dictionary(); + var clonedEntityTypes = new Dictionary(); foreach (var entityType in model.GetEntityTypes()) { var clrType = entityType.ClrType; @@ -78,7 +78,7 @@ public static IModel Clone(this IModel model) return modelClone; } - private static void CloneProperties(IEntityType sourceEntityType, IMutableEntityType targetEntityType) + private static void CloneProperties(IReadOnlyEntityType sourceEntityType, IMutableEntityType targetEntityType) { foreach (var property in sourceEntityType.GetDeclaredProperties()) { @@ -92,7 +92,7 @@ private static void CloneProperties(IEntityType sourceEntityType, IMutableEntity } } - private static void CloneKeys(IEntityType sourceEntityType, IMutableEntityType targetEntityType) + private static void CloneKeys(IReadOnlyEntityType sourceEntityType, IMutableEntityType targetEntityType) { foreach (var key in sourceEntityType.GetDeclaredKeys()) { @@ -107,7 +107,7 @@ private static void CloneKeys(IEntityType sourceEntityType, IMutableEntityType t } } - private static void CloneIndexes(IEntityType sourceEntityType, IMutableEntityType targetEntityType) + private static void CloneIndexes(IReadOnlyEntityType sourceEntityType, IMutableEntityType targetEntityType) { foreach (var index in sourceEntityType.GetDeclaredIndexes()) { @@ -118,7 +118,7 @@ private static void CloneIndexes(IEntityType sourceEntityType, IMutableEntityTyp } } - private static void CloneForeignKeys(IEntityType sourceEntityType, IMutableEntityType targetEntityType) + private static void CloneForeignKeys(IReadOnlyEntityType sourceEntityType, IMutableEntityType targetEntityType) { foreach (var foreignKey in sourceEntityType.GetDeclaredForeignKeys()) { @@ -134,7 +134,7 @@ private static void CloneForeignKeys(IEntityType sourceEntityType, IMutableEntit } } - private static void CloneNavigations(IEntityType sourceEntityType, IMutableEntityType targetEntityType) + private static void CloneNavigations(IReadOnlyEntityType sourceEntityType, IMutableEntityType targetEntityType) { foreach (var navigation in sourceEntityType.GetDeclaredNavigations()) { diff --git a/test/EFCore.Specification.Tests/TestUtilities/NavigationComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/NavigationComparer.cs index 9cff0448aba..b1c5898389e 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/NavigationComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/NavigationComparer.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class NavigationComparer : IEqualityComparer, IComparer + public class NavigationComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; @@ -17,10 +17,10 @@ public NavigationComparer(bool compareAnnotations = true) _compareAnnotations = compareAnnotations; } - public int Compare(INavigation x, INavigation y) + public int Compare(IReadOnlyNavigation x, IReadOnlyNavigation y) => StringComparer.Ordinal.Compare(x.Name, y.Name); - public bool Equals(INavigation x, INavigation y) + public bool Equals(IReadOnlyNavigation x, IReadOnlyNavigation y) { if (x == null) { @@ -33,7 +33,7 @@ public bool Equals(INavigation x, INavigation y) && (!_compareAnnotations || x.GetAnnotations().SequenceEqual(y.GetAnnotations(), AnnotationComparer.Instance)); } - public int GetHashCode(INavigation obj) + public int GetHashCode(IReadOnlyNavigation obj) => obj.Name.GetHashCode(); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/PropertyComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/PropertyComparer.cs index 64036354a76..16862e2b9f2 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/PropertyComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/PropertyComparer.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class PropertyComparer : IEqualityComparer, IComparer + public class PropertyComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; @@ -17,10 +17,10 @@ public PropertyComparer(bool compareAnnotations = true) _compareAnnotations = compareAnnotations; } - public int Compare(IProperty x, IProperty y) + public int Compare(IReadOnlyProperty x, IReadOnlyProperty y) => StringComparer.Ordinal.Compare(x.Name, y.Name); - public bool Equals(IProperty x, IProperty y) + public bool Equals(IReadOnlyProperty x, IReadOnlyProperty y) { if (x == null) { @@ -40,7 +40,7 @@ public bool Equals(IProperty x, IProperty y) && (!_compareAnnotations || x.GetAnnotations().SequenceEqual(y.GetAnnotations(), AnnotationComparer.Instance)); } - public int GetHashCode(IProperty obj) + public int GetHashCode(IReadOnlyProperty obj) => obj.Name.GetHashCode(); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs index 7659450c63f..56c7d494bfc 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs @@ -127,14 +127,6 @@ public IServiceProvider CreateContextServices(IServiceCollection customServices, public IServiceProvider CreateContextServices(IServiceCollection customServices) => ((IInfrastructure)CreateContext(customServices)).Instance; - public IMutableModel BuildModelFor() - where TEntity : class - { - var builder = CreateConventionBuilder(); - builder.Entity(); - return builder.Model; - } - public IModel Finalize(ModelBuilder modelBuilder, bool skipValidation = false) { var contextServices = CreateContextServices(); diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs index 2c51e9b41e3..88c32467152 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestIndexComparer.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class TestIndexComparer : IEqualityComparer, IComparer + public class TestIndexComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; @@ -17,10 +17,10 @@ public TestIndexComparer(bool compareAnnotations = true) _compareAnnotations = compareAnnotations; } - public int Compare(IIndex x, IIndex y) + public int Compare(IReadOnlyIndex x, IReadOnlyIndex y) => PropertyListComparer.Instance.Compare(x.Properties, y.Properties); - public bool Equals(IIndex x, IIndex y) + public bool Equals(IReadOnlyIndex x, IReadOnlyIndex y) { if (x == null) { @@ -34,7 +34,7 @@ public bool Equals(IIndex x, IIndex y) && (!_compareAnnotations || x.GetAnnotations().SequenceEqual(y.GetAnnotations(), AnnotationComparer.Instance)); } - public int GetHashCode(IIndex obj) + public int GetHashCode(IReadOnlyIndex obj) => PropertyListComparer.Instance.GetHashCode(obj.Properties); } } diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs b/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs index df3219d7f14..cd7e7fbb48e 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestKeyComparer.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.TestUtilities { - public class TestKeyComparer : IEqualityComparer, IComparer + public class TestKeyComparer : IEqualityComparer, IComparer { private readonly bool _compareAnnotations; @@ -17,10 +17,10 @@ public TestKeyComparer(bool compareAnnotations = true) _compareAnnotations = compareAnnotations; } - public int Compare(IKey x, IKey y) + public int Compare(IReadOnlyKey x, IReadOnlyKey y) => PropertyListComparer.Instance.Compare(x.Properties, y.Properties); - public bool Equals(IKey x, IKey y) + public bool Equals(IReadOnlyKey x, IReadOnlyKey y) { if (x == null) { @@ -33,7 +33,7 @@ public bool Equals(IKey x, IKey y) && (!_compareAnnotations || x.GetAnnotations().SequenceEqual(y.GetAnnotations(), AnnotationComparer.Instance)); } - public int GetHashCode(IKey obj) + public int GetHashCode(IReadOnlyKey obj) => PropertyListComparer.Instance.GetHashCode(obj.Properties); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerApiConsistencyTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerApiConsistencyTest.cs index cac3768eae8..fa0e09f2684 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerApiConsistencyTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerApiConsistencyTest.cs @@ -42,19 +42,54 @@ public class SqlServerApiConsistencyFixture : ApiConsistencyFixtureBase }; public override - List<(Type Type, Type ReadonlyExtensions, Type MutableExtensions, Type ConventionExtensions, Type - ConventionBuilderExtensions)> MetadataExtensionTypes { get; } = new() + List<(Type Type, + Type ReadonlyExtensions, + Type MutableExtensions, + Type ConventionExtensions, + Type ConventionBuilderExtensions, + Type RuntimeExtensions)> MetadataExtensionTypes { get; } + = new() { - (typeof(IModel), typeof(SqlServerModelExtensions), typeof(SqlServerModelExtensions), - typeof(SqlServerModelExtensions), typeof(SqlServerModelBuilderExtensions)), - (typeof(IEntityType), typeof(SqlServerEntityTypeExtensions), typeof(SqlServerEntityTypeExtensions), - typeof(SqlServerEntityTypeExtensions), typeof(SqlServerEntityTypeBuilderExtensions)), - (typeof(IKey), typeof(SqlServerKeyExtensions), typeof(SqlServerKeyExtensions), typeof(SqlServerKeyExtensions), - typeof(SqlServerKeyBuilderExtensions)), - (typeof(IProperty), typeof(SqlServerPropertyExtensions), typeof(SqlServerPropertyExtensions), - typeof(SqlServerPropertyExtensions), typeof(SqlServerPropertyBuilderExtensions)), - (typeof(IIndex), typeof(SqlServerIndexExtensions), typeof(SqlServerIndexExtensions), - typeof(SqlServerIndexExtensions), typeof(SqlServerIndexBuilderExtensions)) + ( + typeof(IReadOnlyModel), + typeof(SqlServerModelExtensions), + typeof(SqlServerModelExtensions), + typeof(SqlServerModelExtensions), + typeof(SqlServerModelBuilderExtensions), + null + ), + ( + typeof(IReadOnlyEntityType), + typeof(SqlServerEntityTypeExtensions), + typeof(SqlServerEntityTypeExtensions), + typeof(SqlServerEntityTypeExtensions), + typeof(SqlServerEntityTypeBuilderExtensions), + null + ), + ( + typeof(IReadOnlyKey), + typeof(SqlServerKeyExtensions), + typeof(SqlServerKeyExtensions), + typeof(SqlServerKeyExtensions), + typeof(SqlServerKeyBuilderExtensions), + null + ), + ( + typeof(IReadOnlyProperty), + typeof(SqlServerPropertyExtensions), + typeof(SqlServerPropertyExtensions), + typeof(SqlServerPropertyExtensions), + typeof(SqlServerPropertyBuilderExtensions), + null + ), + ( + typeof(IReadOnlyIndex), + typeof(SqlServerIndexExtensions), + typeof(SqlServerIndexExtensions), + typeof(SqlServerIndexExtensions), + typeof(SqlServerIndexBuilderExtensions), + null + ) }; } } diff --git a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs index c399d0adcb2..ff286a59a48 100644 --- a/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/Design/Internal/SqlServerAnnotationCodeGeneratorTest.cs @@ -29,7 +29,7 @@ public void GenerateFluentApi_IKey_works_when_clustered() x.Property("Id"); x.HasKey("Id").IsClustered(); }); - var key = modelBuilder.Model.FindEntityType("Post").GetKeys().Single(); + var key = (IKey)modelBuilder.Model.FindEntityType("Post").GetKeys().Single(); var result = generator.GenerateFluentApiCalls(key, key.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -51,7 +51,7 @@ public void GenerateFluentApi_IKey_works_when_nonclustered() x.Property("Id"); x.HasKey("Id").IsClustered(false); }); - var key = modelBuilder.Model.FindEntityType("Post").GetKeys().Single(); + var key = (IKey)modelBuilder.Model.FindEntityType("Post").GetKeys().Single(); var result = generator.GenerateFluentApiCalls(key, key.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -75,7 +75,7 @@ public void GenerateFluentApi_IIndex_works_when_clustered() x.Property("Name"); x.HasIndex("Name").IsClustered(); }); - var index = modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); + var index = (IIndex)modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); var result = generator.GenerateFluentApiCalls(index, index.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -98,7 +98,7 @@ public void GenerateFluentApi_IIndex_works_when_nonclustered() x.Property("Name"); x.HasIndex("Name").IsClustered(false); }); - var index = modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); + var index = (IIndex)modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); var result = generator.GenerateFluentApiCalls(index, index.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -123,7 +123,7 @@ public void GenerateFluentApi_IIndex_works_with_fillfactor() x.HasIndex("Name").HasFillFactor(90); }); - var index = modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); + var index = (IIndex)modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); var result = generator.GenerateFluentApiCalls(index, index.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -146,8 +146,8 @@ public void GenerateFluentApi_IIndex_works_with_includes() x.Property("LastName"); x.HasIndex("LastName").IncludeProperties("FirstName"); }); - var index = modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); + var index = (IIndex)modelBuilder.Model.FindEntityType("Post").GetIndexes().Single(); var result = generator.GenerateFluentApiCalls(index, index.GetAnnotations().ToDictionary(a => a.Name, a => a)) .Single(); @@ -166,7 +166,7 @@ public void GenerateFluentApi_IModel_works_with_identity() modelBuilder.UseIdentityColumns(seed: 5, increment: 10); var annotations = modelBuilder.Model.GetAnnotations().ToDictionary(a => a.Name, a => a); - var result = generator.GenerateFluentApiCalls(modelBuilder.Model, annotations).Single(); + var result = generator.GenerateFluentApiCalls((IModel)modelBuilder.Model, annotations).Single(); Assert.Equal("UseIdentityColumns", result.Method); @@ -185,7 +185,7 @@ public void GenerateFluentApi_IProperty_works_with_identity() var property = modelBuilder.Model.FindEntityType("Post").FindProperty("Id"); var annotations = property.GetAnnotations().ToDictionary(a => a.Name, a => a); - var result = generator.GenerateFluentApiCalls(property, annotations).Single(); + var result = generator.GenerateFluentApiCalls((IProperty)property, annotations).Single(); Assert.Equal("UseIdentityColumn", result.Method); @@ -203,7 +203,7 @@ public void GenerateFluentApi_IModel_works_with_HiLo() modelBuilder.UseHiLo("HiLoIndexName", "HiLoIndexSchema"); var annotations = modelBuilder.Model.GetAnnotations().ToDictionary(a => a.Name, a => a); - var result = generator.GenerateFluentApiCalls(modelBuilder.Model, annotations).Single(); + var result = generator.GenerateFluentApiCalls((IModel)modelBuilder.Model, annotations).Single(); Assert.Equal("UseHiLo", result.Method); @@ -222,7 +222,7 @@ public void GenerateFluentApi_IProperty_works_with_HiLo() var property = modelBuilder.Model.FindEntityType("Post").FindProperty("Id"); var annotations = property.GetAnnotations().ToDictionary(a => a.Name, a => a); - var result = generator.GenerateFluentApiCalls(property, annotations).Single(); + var result = generator.GenerateFluentApiCalls((IProperty)property, annotations).Single(); Assert.Equal("UseHiLo", result.Method); @@ -258,7 +258,7 @@ MethodCallCodeFragment GenerateFluentApiCall(string entityTypeName, string prope { var property = modelBuilder.Model.FindEntityType(entityTypeName).FindProperty(propertyName); var annotations = property.GetAnnotations().ToDictionary(a => a.Name, a => a); - return generator.GenerateFluentApiCalls(property, annotations).SingleOrDefault(); + return generator.GenerateFluentApiCalls((IProperty)property, annotations).SingleOrDefault(); } } diff --git a/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerConventionSetBuilderTests.cs b/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerConventionSetBuilderTests.cs index 6e4a93da86d..35b8929bddb 100644 --- a/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerConventionSetBuilderTests.cs +++ b/test/EFCore.SqlServer.Tests/Metadata/Conventions/SqlServerConventionSetBuilderTests.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { public class SqlServerConventionSetBuilderTests : ConventionSetBuilderTests { - public override IModel Can_build_a_model_with_default_conventions_without_DI() + public override IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI() { var model = base.Can_build_a_model_with_default_conventions_without_DI(); diff --git a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs index 415650d0a4b..50b58d95571 100644 --- a/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs +++ b/test/EFCore.SqlServer.Tests/Metadata/SqlServerBuilderExtensionsTest.cs @@ -364,7 +364,7 @@ public void Can_set_use_of_existing_SQL_sequence_for_model() ValidateSchemaNamedSpecificSequence(sqlServerExtensions.FindSequence("Snook", "Tasty")); } - private static void ValidateSchemaNamedSpecificSequence(ISequence sequence) + private static void ValidateSchemaNamedSpecificSequence(IReadOnlySequence sequence) { Assert.Equal("Snook", sequence.Name); Assert.Equal("Tasty", sequence.Schema); diff --git a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationsAnnotationProviderTest.cs b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationsAnnotationProviderTest.cs index 93f9f9c58d5..ef327a14f0c 100644 --- a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationsAnnotationProviderTest.cs +++ b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationsAnnotationProviderTest.cs @@ -22,11 +22,10 @@ public SqlServerMigrationsAnnotationProviderTest() public void For_property_handles_identity_annotations() { var modelBuilder = SqlServerTestHelpers.Instance.CreateConventionBuilder(); - var property = modelBuilder.Entity() - .Property("Id").UseIdentityColumn(2, 3) - .Metadata; + modelBuilder.Entity().Property("Id").UseIdentityColumn(2, 3); - SqlServerTestHelpers.Instance.Finalize(modelBuilder); + var model = SqlServerTestHelpers.Instance.Finalize(modelBuilder); + var property = model.FindEntityType(typeof(Entity)).FindProperty("Id"); var migrationAnnotations = _annotations.For(property.GetTableColumnMappings().Single().Column).ToList(); @@ -39,11 +38,11 @@ public void Resolves_column_names_for_Index_with_included_properties() { var modelBuilder = SqlServerTestHelpers.Instance.CreateConventionBuilder(); modelBuilder.Entity().Property(e => e.IncludedProp).HasColumnName("IncludedColumn"); - var index = modelBuilder.Entity().HasIndex(e => e.IndexedProp).IncludeProperties(e => e.IncludedProp).Metadata; - SqlServerTestHelpers.Instance.Finalize(modelBuilder); + modelBuilder.Entity().HasIndex(e => e.IndexedProp).IncludeProperties(e => e.IncludedProp); + var model = SqlServerTestHelpers.Instance.Finalize(modelBuilder); Assert.Contains( - _annotations.For(index.GetMappedTableIndexes().Single()), + _annotations.For(model.FindEntityType(typeof(Entity)).GetIndexes().Single().GetMappedTableIndexes().Single()), a => a.Name == SqlServerAnnotationNames.Include && ((string[])a.Value).Contains("IncludedColumn")); } diff --git a/test/EFCore.SqlServer.Tests/SqlServerEventIdTest.cs b/test/EFCore.SqlServer.Tests/SqlServerEventIdTest.cs index b834e88f32c..64cc9b9caa2 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerEventIdTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerEventIdTest.cs @@ -30,6 +30,7 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() { { typeof(IList), () => new List { "Fake1", "Fake2" } }, { typeof(IProperty), () => property }, + { typeof(IReadOnlyProperty), () => property }, { typeof(string), () => "Fake" } }; diff --git a/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs b/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs index 8bd63dc49dd..cc07be84632 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerSequenceValueGeneratorTest.cs @@ -79,7 +79,7 @@ private async Task Generates_sequential_values(bool async) var sequence = ((IMutableModel)new Model()).AddSequence("Foo"); sequence.IncrementBy = blockSize; - var state = new SqlServerSequenceValueGeneratorState(sequence); + var state = new SqlServerSequenceValueGeneratorState((ISequence)sequence); var generator = new SqlServerSequenceHiLoValueGenerator( new FakeRawSqlCommandBuilder(blockSize), @@ -134,7 +134,7 @@ private async Task>> GenerateValuesInMultipleThreads(int var sequence = ((IMutableModel)new Model()).AddSequence("Foo"); sequence.IncrementBy = blockSize; - var state = new SqlServerSequenceValueGeneratorState(sequence); + var state = new SqlServerSequenceValueGeneratorState((ISequence)sequence); var executor = new FakeRawSqlCommandBuilder(blockSize); var sqlGenerator = new SqlServerUpdateSqlGenerator( @@ -184,7 +184,7 @@ public void Does_not_generate_temp_values() { var sequence = ((IMutableModel)new Model()).AddSequence("Foo"); sequence.IncrementBy = 4; - var state = new SqlServerSequenceValueGeneratorState(sequence); + var state = new SqlServerSequenceValueGeneratorState((ISequence)sequence); var generator = new SqlServerSequenceHiLoValueGenerator( new FakeRawSqlCommandBuilder(4), diff --git a/test/EFCore.SqlServer.Tests/SqlServerTypeMapperTest.cs b/test/EFCore.SqlServer.Tests/SqlServerTypeMapperTest.cs index dba574ac350..31838ee8729 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerTypeMapperTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerTypeMapperTest.cs @@ -288,13 +288,13 @@ public void Does_non_key_SQL_Server_required_string_mapping(bool? unicode, bool? [InlineData(null, null)] public void Does_key_SQL_Server_string_mapping(bool? unicode, bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.IsNullable = false; property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); property.DeclaringEntityType.SetPrimaryKey(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Null(typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -316,7 +316,7 @@ private static IRelationalTypeMappingSource CreateTypeMapper() [InlineData(null, null)] public void Does_foreign_key_SQL_Server_string_mapping(bool? unicode, bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.IsNullable = false; property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); @@ -324,7 +324,7 @@ public void Does_foreign_key_SQL_Server_string_mapping(bool? unicode, bool? fixe var pk = property.DeclaringEntityType.SetPrimaryKey(property); property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.Null(typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -341,7 +341,7 @@ public void Does_foreign_key_SQL_Server_string_mapping(bool? unicode, bool? fixe [InlineData(null, null)] public void Does_required_foreign_key_SQL_Server_string_mapping(bool? unicode, bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.IsNullable = false; property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); @@ -350,7 +350,7 @@ public void Does_required_foreign_key_SQL_Server_string_mapping(bool? unicode, b property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); fkProperty.IsNullable = false; - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.Null(typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -367,13 +367,13 @@ public void Does_required_foreign_key_SQL_Server_string_mapping(bool? unicode, b [InlineData(null, null)] public void Does_indexed_column_SQL_Server_string_mapping(bool? unicode, bool? fixedLength) { - var entityType = CreateEntityType(); + var entityType = CreateEntityType(); var property = entityType.AddProperty("MyProp", typeof(string)); property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Null(typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -390,14 +390,13 @@ public void Does_indexed_column_SQL_Server_string_mapping(bool? unicode, bool? f [InlineData(null, null)] public void Does_IndexAttribute_column_SQL_Server_string_mapping(bool? unicode, bool? fixedLength) { - var model = CreateModel(); - var entityType = model.FindEntityType(typeof(MyTypeWithIndexAttribute)); + var entityType = CreateEntityType(); var property = entityType.FindProperty("Name"); property.SetIsUnicode(unicode); property.SetIsFixedLength(fixedLength); - model.FinalizeModel(); + entityType.Model.FinalizeModel(); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Null(typeMapping.DbType); Assert.Equal("nvarchar(450)", typeMapping.StoreType); @@ -550,13 +549,13 @@ public void Does_non_key_SQL_Server_required_string_mapping_ansi(bool? fixedLeng [InlineData(null)] public void Does_key_SQL_Server_string_mapping_ansi(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.IsNullable = false; property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); property.DeclaringEntityType.SetPrimaryKey(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -571,7 +570,7 @@ public void Does_key_SQL_Server_string_mapping_ansi(bool? fixedLength) [InlineData(null)] public void Does_foreign_key_SQL_Server_string_mapping_ansi(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); property.IsNullable = false; @@ -579,7 +578,7 @@ public void Does_foreign_key_SQL_Server_string_mapping_ansi(bool? fixedLength) var pk = property.DeclaringEntityType.SetPrimaryKey(property); property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -594,7 +593,7 @@ public void Does_foreign_key_SQL_Server_string_mapping_ansi(bool? fixedLength) [InlineData(null)] public void Does_required_foreign_key_SQL_Server_string_mapping_ansi(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(string)); + var property = CreateEntityType().AddProperty("MyProp", typeof(string)); property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); property.IsNullable = false; @@ -603,7 +602,7 @@ public void Does_required_foreign_key_SQL_Server_string_mapping_ansi(bool? fixed property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); fkProperty.IsNullable = false; - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -618,13 +617,13 @@ public void Does_required_foreign_key_SQL_Server_string_mapping_ansi(bool? fixed [InlineData(null)] public void Does_indexed_column_SQL_Server_string_mapping_ansi(bool? fixedLength) { - var entityType = CreateEntityType(); + var entityType = CreateEntityType(); var property = entityType.AddProperty("MyProp", typeof(string)); property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -639,14 +638,13 @@ public void Does_indexed_column_SQL_Server_string_mapping_ansi(bool? fixedLength [InlineData(null)] public void Does_IndexAttribute_column_SQL_Server_string_mapping_ansi(bool? fixedLength) { - var model = CreateModel(); - var entityType = model.FindEntityType(typeof(MyTypeWithIndexAttribute)); + var entityType = CreateEntityType(); var property = entityType.FindProperty("Name"); property.SetIsUnicode(false); property.SetIsFixedLength(fixedLength); - model.FinalizeModel(); + entityType.Model.FinalizeModel(); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.AnsiString, typeMapping.DbType); Assert.Equal("varchar(900)", typeMapping.StoreType); @@ -800,7 +798,7 @@ public void Does_non_key_SQL_Server_fixed_length_binary_mapping_with_extreme_val private RelationalTypeMapping CreateBinaryMapping(string typeName, int? maxLength) { - var property = CreateEntityType().AddProperty("MyBinaryProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyBinaryProp", typeof(byte[])); if (typeName != null) { @@ -816,7 +814,7 @@ private RelationalTypeMapping CreateBinaryMapping(string typeName, int? maxLengt property.SetMaxLength(maxLength); } - return CreateTypeMapper().GetMapping(property); + return CreateTypeMapper().GetMapping((IProperty)property); } [ConditionalTheory] @@ -824,12 +822,12 @@ private RelationalTypeMapping CreateBinaryMapping(string typeName, int? maxLengt [InlineData(null)] public void Does_key_SQL_Server_binary_mapping(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsNullable = false; property.SetIsFixedLength(fixedLength); property.DeclaringEntityType.SetPrimaryKey(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("varbinary(900)", typeMapping.StoreType); @@ -842,14 +840,14 @@ public void Does_key_SQL_Server_binary_mapping(bool? fixedLength) [InlineData(null)] public void Does_foreign_key_SQL_Server_binary_mapping(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsNullable = false; property.SetIsFixedLength(fixedLength); var fkProperty = property.DeclaringEntityType.AddProperty("FK", typeof(byte[])); var pk = property.DeclaringEntityType.SetPrimaryKey(property); property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -862,7 +860,7 @@ public void Does_foreign_key_SQL_Server_binary_mapping(bool? fixedLength) [InlineData(null)] public void Does_required_foreign_key_SQL_Server_binary_mapping(bool? fixedLength) { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsNullable = false; property.SetIsFixedLength(fixedLength); var fkProperty = property.DeclaringEntityType.AddProperty("FK", typeof(byte[])); @@ -870,7 +868,7 @@ public void Does_required_foreign_key_SQL_Server_binary_mapping(bool? fixedLengt property.DeclaringEntityType.AddForeignKey(fkProperty, pk, property.DeclaringEntityType); fkProperty.IsNullable = false; - var typeMapping = CreateTypeMapper().GetMapping(fkProperty); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)fkProperty); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -883,12 +881,12 @@ public void Does_required_foreign_key_SQL_Server_binary_mapping(bool? fixedLengt [InlineData(null)] public void Does_indexed_column_SQL_Server_binary_mapping(bool? fixedLength) { - var entityType = CreateEntityType(); + var entityType = CreateEntityType(); var property = entityType.AddProperty("MyProp", typeof(byte[])); property.SetIsFixedLength(fixedLength); entityType.AddIndex(property); - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.False(typeMapping.IsFixedLength); Assert.Equal(DbType.Binary, typeMapping.DbType); @@ -899,11 +897,11 @@ public void Does_indexed_column_SQL_Server_binary_mapping(bool? fixedLength) [ConditionalFact] public void Does_non_key_SQL_Server_rowversion_mapping() { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsConcurrencyToken = true; property.ValueGenerated = ValueGenerated.OnAddOrUpdate; - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("rowversion", typeMapping.StoreType); @@ -915,12 +913,12 @@ public void Does_non_key_SQL_Server_rowversion_mapping() [ConditionalFact] public void Does_non_key_SQL_Server_required_rowversion_mapping() { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsConcurrencyToken = true; property.ValueGenerated = ValueGenerated.OnAddOrUpdate; property.IsNullable = false; - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.Equal("rowversion", typeMapping.StoreType); @@ -932,10 +930,10 @@ public void Does_non_key_SQL_Server_required_rowversion_mapping() [ConditionalFact] public void Does_not_do_rowversion_mapping_for_non_computed_concurrency_tokens() { - var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); + var property = CreateEntityType().AddProperty("MyProp", typeof(byte[])); property.IsConcurrencyToken = true; - var typeMapping = CreateTypeMapper().GetMapping(property); + var typeMapping = CreateTypeMapper().GetMapping((IProperty)property); Assert.Equal(DbType.Binary, typeMapping.DbType); Assert.False(typeMapping.IsFixedLength); @@ -949,7 +947,7 @@ private RelationalTypeMapping GetTypeMapping( bool? unicode = null, bool? fixedLength = null) { - var property = CreateEntityType().AddProperty("MyProp", propertyType); + var property = CreateEntityType().AddProperty("MyProp", propertyType); if (nullable.HasValue) { @@ -971,7 +969,7 @@ private RelationalTypeMapping GetTypeMapping( property.SetIsFixedLength(fixedLength); } - return CreateTypeMapper().GetMapping(property); + return CreateTypeMapper().GetMapping((IProperty)property); } [ConditionalFact] @@ -1010,7 +1008,7 @@ public void Throws_for_unrecognized_property_types() { var property = ((IMutableModel)new Model()).AddEntityType("Entity1") .AddProperty("Strange", typeof(object)); - var ex = Assert.Throws(() => CreateTypeMapper().GetMapping(property)); + var ex = Assert.Throws(() => CreateTypeMapper().GetMapping((IProperty)property)); Assert.Equal(RelationalStrings.UnsupportedPropertyType("Entity1 (Dictionary)", "Strange", "object"), ex.Message); Assert.Equal(RelationalStrings.UnsupportedType("object"), @@ -1103,7 +1101,7 @@ public void Can_map_string_base_type_name_and_size(string typeName) .HasMaxLength(2018) .Metadata; - var mapping = CreateTypeMapper().FindMapping(property); + var mapping = CreateTypeMapper().FindMapping((IProperty)property); Assert.Same(typeof(string), mapping.ClrType); Assert.Equal(2018, mapping.Size); @@ -1126,7 +1124,7 @@ public void Can_map_binary_base_type_name_and_size(string typeName) .HasMaxLength(2018) .Metadata; - var mapping = CreateTypeMapper().FindMapping(property); + var mapping = CreateTypeMapper().FindMapping((IProperty)property); Assert.Same(typeof(byte[]), mapping.ClrType); Assert.Equal(2018, mapping.Size); diff --git a/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorCacheTest.cs b/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorCacheTest.cs index 464980fceab..a9bee4a5182 100644 --- a/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorCacheTest.cs +++ b/test/EFCore.SqlServer.Tests/SqlServerValueGeneratorCacheTest.cs @@ -127,7 +127,7 @@ public void Block_size_is_obtained_from_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(10, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(10, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -145,7 +145,7 @@ public void Block_size_is_obtained_from_named_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(10, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(10, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -163,7 +163,7 @@ public void Block_size_is_obtained_from_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(10, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(10, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -181,7 +181,7 @@ public void Block_size_is_obtained_from_named_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(10, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(10, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -200,7 +200,7 @@ public void Block_size_is_obtained_from_specified_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(11, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(11, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -222,7 +222,7 @@ public void Non_positive_block_sizes_are_not_allowed() Assert.StartsWith( CoreStrings.HiLoBadBlockSize, Assert.Throws( - () => cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy).Message); + () => cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy).Message); } [ConditionalFact] @@ -241,7 +241,7 @@ public void Block_size_is_obtained_from_specified_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal(11, cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.IncrementBy); + Assert.Equal(11, cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.IncrementBy); } [ConditionalFact] @@ -259,7 +259,7 @@ public void Sequence_name_is_obtained_from_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("EntityFrameworkHiLoSequence", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("EntityFrameworkHiLoSequence", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -277,7 +277,7 @@ public void Sequence_name_is_obtained_from_named_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -295,7 +295,7 @@ public void Sequence_name_is_obtained_from_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("EntityFrameworkHiLoSequence", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("EntityFrameworkHiLoSequence", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -313,7 +313,7 @@ public void Sequence_name_is_obtained_from_named_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -332,7 +332,7 @@ public void Sequence_name_is_obtained_from_specified_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -351,7 +351,7 @@ public void Sequence_name_is_obtained_from_specified_model_default_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); } [ConditionalFact] @@ -369,8 +369,8 @@ public void Schema_qualified_sequence_name_is_obtained_from_named_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); - Assert.Equal("R", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Schema); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); + Assert.Equal("R", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Schema); } [ConditionalFact] @@ -388,8 +388,8 @@ public void Schema_qualified_sequence_name_is_obtained_from_named_model_default_ var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); - Assert.Equal("R", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Schema); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); + Assert.Equal("R", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Schema); } [ConditionalFact] @@ -408,8 +408,8 @@ public void Schema_qualified_sequence_name_is_obtained_from_specified_sequence() var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); - Assert.Equal("R", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Schema); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); + Assert.Equal("R", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Schema); } [ConditionalFact] @@ -428,8 +428,8 @@ public void Schema_qualified_sequence_name_is_obtained_from_specified_model_defa var cache = new SqlServerValueGeneratorCache(new ValueGeneratorCacheDependencies()); - Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Name); - Assert.Equal("R", cache.GetOrAddSequenceState(property, CreateConnection()).Sequence.Schema); + Assert.Equal("DaneelOlivaw", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Name); + Assert.Equal("R", cache.GetOrAddSequenceState((IProperty)property, CreateConnection()).Sequence.Schema); } protected virtual ModelBuilder CreateConventionModelBuilder() diff --git a/test/EFCore.Sqlite.Tests/Metadata/Conventions/SqliteConventionSetBuilderTests.cs b/test/EFCore.Sqlite.Tests/Metadata/Conventions/SqliteConventionSetBuilderTests.cs index d5e38c3bb56..28bfc0d8ba4 100644 --- a/test/EFCore.Sqlite.Tests/Metadata/Conventions/SqliteConventionSetBuilderTests.cs +++ b/test/EFCore.Sqlite.Tests/Metadata/Conventions/SqliteConventionSetBuilderTests.cs @@ -8,7 +8,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions { public class SqliteConventionSetBuilderTests : ConventionSetBuilderTests { - public override IModel Can_build_a_model_with_default_conventions_without_DI() + public override IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI() { var model = base.Can_build_a_model_with_default_conventions_without_DI(); diff --git a/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationAnnotationProviderTest.cs b/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationAnnotationProviderTest.cs index ba708970627..ada2276997d 100644 --- a/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationAnnotationProviderTest.cs +++ b/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationAnnotationProviderTest.cs @@ -19,7 +19,7 @@ public class SqliteMigrationAnnotationProviderTest [ConditionalFact] public void Does_not_add_Autoincrement_for_OnAdd_integer_property_non_key() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAdd().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAdd().Metadata; FinalizeModel(); Assert.DoesNotContain( @@ -30,7 +30,7 @@ public void Does_not_add_Autoincrement_for_OnAdd_integer_property_non_key() [ConditionalFact] public void Adds_Autoincrement_for_OnAdd_integer_property_primary_key() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAdd().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAdd().Metadata; _modelBuilder.Entity().HasKey(e => e.IntProp); FinalizeModel(); @@ -42,7 +42,7 @@ public void Adds_Autoincrement_for_OnAdd_integer_property_primary_key() [ConditionalFact] public void Does_not_add_Autoincrement_for_OnAddOrUpdate_integer_property() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAddOrUpdate().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnAddOrUpdate().Metadata; FinalizeModel(); Assert.DoesNotContain( @@ -53,7 +53,7 @@ public void Does_not_add_Autoincrement_for_OnAddOrUpdate_integer_property() [ConditionalFact] public void Does_not_add_Autoincrement_for_OnUpdate_integer_property() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnUpdate().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedOnUpdate().Metadata; FinalizeModel(); Assert.DoesNotContain( @@ -64,7 +64,7 @@ public void Does_not_add_Autoincrement_for_OnUpdate_integer_property() [ConditionalFact] public void Does_not_add_Autoincrement_for_Never_value_generated_integer_property() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedNever().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).ValueGeneratedNever().Metadata; FinalizeModel(); Assert.DoesNotContain( @@ -75,7 +75,7 @@ public void Does_not_add_Autoincrement_for_Never_value_generated_integer_propert [ConditionalFact] public void Does_not_add_Autoincrement_for_default_integer_property() { - var property = _modelBuilder.Entity().Property(e => e.IntProp).Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.IntProp).Metadata; FinalizeModel(); Assert.DoesNotContain( @@ -86,7 +86,7 @@ public void Does_not_add_Autoincrement_for_default_integer_property() [ConditionalFact] public void Does_not_add_Autoincrement_for_non_integer_OnAdd_property() { - var property = _modelBuilder.Entity().Property(e => e.StringProp).ValueGeneratedOnAdd().Metadata; + var property = (IProperty)_modelBuilder.Entity().Property(e => e.StringProp).ValueGeneratedOnAdd().Metadata; FinalizeModel(); Assert.DoesNotContain( diff --git a/test/EFCore.Sqlite.Tests/SqliteEventIdTest.cs b/test/EFCore.Sqlite.Tests/SqliteEventIdTest.cs index b56abc88844..1e8925d8f67 100644 --- a/test/EFCore.Sqlite.Tests/SqliteEventIdTest.cs +++ b/test/EFCore.Sqlite.Tests/SqliteEventIdTest.cs @@ -27,7 +27,7 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() { { typeof(string), () => "Fake" }, { typeof(IEntityType), () => entityType }, - { typeof(ISequence), () => new FakeSequence() }, + { typeof(IReadOnlySequence), () => new FakeSequence() }, { typeof(Type), () => typeof(object) } }; @@ -38,7 +38,7 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() fakeFactories); } - private class FakeSequence : Annotatable, ISequence + private class FakeSequence : Annotatable, IReadOnlySequence { public string Name => "SequenceName"; @@ -64,7 +64,7 @@ public Type ClrType public Type Type => throw new NotImplementedException(); - public IModel Model + public IReadOnlyModel Model => throw new NotImplementedException(); public bool IsCyclic diff --git a/test/EFCore.Tests/ApiConsistencyTest.cs b/test/EFCore.Tests/ApiConsistencyTest.cs index 99493e25134..60321ce7247 100644 --- a/test/EFCore.Tests/ApiConsistencyTest.cs +++ b/test/EFCore.Tests/ApiConsistencyTest.cs @@ -78,26 +78,85 @@ protected override void Initialize() }; public override - List<(Type Type, Type ReadonlyExtensions, Type MutableExtensions, Type ConventionExtensions, Type - ConventionBuilderExtensions)> MetadataExtensionTypes { get; } - = new() + List<(Type Type, + Type ReadonlyExtensions, + Type MutableExtensions, + Type ConventionExtensions, + Type ConventionBuilderExtensions, + Type RuntimeExtensions)> MetadataExtensionTypes { get; } = new() { - (typeof(IModel), typeof(ModelExtensions), typeof(MutableModelExtensions), typeof(ConventionModelExtensions), null), - (typeof(IAnnotatable), typeof(AnnotatableExtensions), typeof(MutableAnnotatableExtensions), - typeof(ConventionAnnotatableExtensions), null), - (typeof(IEntityType), typeof(EntityTypeExtensions), typeof(MutableEntityTypeExtensions), - typeof(ConventionEntityTypeExtensions), null), - (typeof(ITypeBase), typeof(TypeBaseExtensions), typeof(MutableTypeBaseExtensions), - typeof(ConventionTypeBaseExtensions), null), - (typeof(IKey), typeof(KeyExtensions), typeof(MutableKeyExtensions), typeof(ConventionKeyExtensions), null), - (typeof(IForeignKey), typeof(ForeignKeyExtensions), typeof(MutableForeignKeyExtensions), - typeof(ConventionForeignKeyExtensions), null), - (typeof(IProperty), typeof(PropertyExtensions), typeof(MutablePropertyExtensions), - typeof(ConventionPropertyExtensions), null), - (typeof(INavigation), typeof(NavigationExtensions), typeof(MutableNavigationExtensions), - typeof(ConventionNavigationExtensions), null), - (typeof(IPropertyBase), typeof(PropertyBaseExtensions), typeof(MutablePropertyBaseExtensions), - typeof(ConventionPropertyBaseExtensions), null) + ( + typeof(IReadOnlyModel), + typeof(ModelExtensions), + typeof(MutableModelExtensions), + typeof(ConventionModelExtensions), + null, + null + ), + ( + typeof(IReadOnlyAnnotatable), + typeof(AnnotatableExtensions), + typeof(MutableAnnotatableExtensions), + typeof(ConventionAnnotatableExtensions), + null, + null + ), + ( + typeof(IReadOnlyEntityType), + typeof(EntityTypeExtensions), + typeof(MutableEntityTypeExtensions), + typeof(ConventionEntityTypeExtensions), + null, + null + ), + ( + typeof(IReadOnlyTypeBase), + typeof(TypeBaseExtensions), + typeof(MutableTypeBaseExtensions), + typeof(ConventionTypeBaseExtensions), + null, + null + ), + ( + typeof(IReadOnlyKey), + typeof(KeyExtensions), + typeof(MutableKeyExtensions), + typeof(ConventionKeyExtensions), + null, + null + ), + ( + typeof(IReadOnlyForeignKey), + typeof(ForeignKeyExtensions), + typeof(MutableForeignKeyExtensions), + typeof(ConventionForeignKeyExtensions), + null, + null + ), + ( + typeof(IReadOnlyProperty), + typeof(PropertyExtensions), + typeof(MutablePropertyExtensions), + typeof(ConventionPropertyExtensions), + null, + null + ), + ( + typeof(IReadOnlyNavigation), + typeof(NavigationExtensions), + typeof(MutableNavigationExtensions), + typeof(ConventionNavigationExtensions), + null, + null + ), + ( + typeof(IReadOnlyPropertyBase), + typeof(PropertyBaseExtensions), + typeof(MutablePropertyBaseExtensions), + typeof(ConventionPropertyBaseExtensions), + null, + null + ) }; public override HashSet NonVirtualMethods { get; } = new() @@ -130,17 +189,12 @@ public override typeof(OwnedNavigationBuilder<,>).GetMethod( nameof(OwnedNavigationBuilder.OwnsMany), 0, new[] { typeof(Type), typeof(string) }), typeof(IConventionPropertyBase).GetMethod(nameof(IConventionPropertyBase.SetField), new[] { typeof(string), typeof(bool) }), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.FindAnnotation)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.GetAnnotations)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.FindRuntimeAnnotation)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.GetRuntimeAnnotations)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.AddRuntimeAnnotation)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.SetRuntimeAnnotation)), - typeof(IAnnotatable).GetMethod(nameof(IAnnotatable.RemoveRuntimeAnnotation)), + typeof(IReadOnlyAnnotatable).GetMethod(nameof(IReadOnlyAnnotatable.FindAnnotation)), + typeof(IReadOnlyAnnotatable).GetMethod(nameof(IReadOnlyAnnotatable.GetAnnotations)), + typeof(AnnotatableExtensions).GetMethod(nameof(AnnotatableExtensions.GetAnnotation)), typeof(IMutableAnnotatable).GetMethod("set_Item"), typeof(IConventionAnnotatable).GetMethod(nameof(IConventionAnnotatable.SetAnnotation)), typeof(ConventionAnnotatableExtensions).GetMethod(nameof(ConventionAnnotatableExtensions.SetOrRemoveAnnotation)), - typeof(ModelExtensions).GetMethod(nameof(ModelExtensions.FindRuntimeEntityType)), typeof(EntityTypeExtensions).GetMethod(nameof(EntityTypeExtensions.GetAllBaseTypesInclusive)), typeof(EntityTypeExtensions).GetMethod(nameof(EntityTypeExtensions.GetAllBaseTypesInclusiveAscending)), typeof(EntityTypeExtensions).GetMethod(nameof(EntityTypeExtensions.GetConcreteDerivedTypesInclusive)), @@ -148,9 +202,9 @@ public override typeof(EntityTypeExtensions).GetMethod(nameof(EntityTypeExtensions.GetProperty)), typeof(EntityTypeExtensions).GetMethod(nameof(EntityTypeExtensions.LeastDerivedType)), typeof(IConventionModelBuilder).GetMethod(nameof(IConventionModelBuilder.HasNoEntityType)), - typeof(INavigationBase).GetMethod("get_DeclaringEntityType"), - typeof(INavigationBase).GetMethod("get_TargetEntityType"), - typeof(INavigationBase).GetMethod("get_Inverse"), + typeof(IReadOnlyNavigationBase).GetMethod("get_DeclaringEntityType"), + typeof(IReadOnlyNavigationBase).GetMethod("get_TargetEntityType"), + typeof(IReadOnlyNavigationBase).GetMethod("get_Inverse"), typeof(IConventionAnnotatableBuilder).GetMethod(nameof(IConventionAnnotatableBuilder.HasNonNullAnnotation)), typeof(IConventionEntityTypeBuilder).GetMethod(nameof(IConventionEntityTypeBuilder.RemoveUnusedImplicitProperties)), typeof(IConventionEntityTypeBuilder).GetMethod(nameof(IConventionEntityTypeBuilder.Ignore)), diff --git a/test/EFCore.Tests/ChangeTracking/Internal/InternalEntityEntryFactoryTest.cs b/test/EFCore.Tests/ChangeTracking/Internal/InternalEntityEntryFactoryTest.cs index f9b7b320472..c374554f3d3 100644 --- a/test/EFCore.Tests/ChangeTracking/Internal/InternalEntityEntryFactoryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/Internal/InternalEntityEntryFactoryTest.cs @@ -29,7 +29,7 @@ public void Creates_CLR_only_entry_when_entity_has_no_shadow_properties() var factory = contextServices.GetRequiredService(); var entity = new RedHook(); - var entry = factory.Create(stateManager, entityTypeBuilder.Metadata, entity); + var entry = factory.Create(stateManager, (IEntityType)entityTypeBuilder.Metadata, entity); Assert.IsType(entry); @@ -54,7 +54,7 @@ public void Creates_mixed_entry_when_entity_CLR_entity_type_and_shadow_propertie var factory = contextServices.GetRequiredService(); var entity = new RedHook(); - var entry = factory.Create(stateManager, entityTypeBuilder.Metadata, entity); + var entry = factory.Create(stateManager, (IEntityType)entityTypeBuilder.Metadata, entity); Assert.IsType(entry); diff --git a/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs b/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs index 90379b6340a..38637c02908 100644 --- a/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs @@ -1165,7 +1165,7 @@ public static IModel BuildModel( b.Property(e => e.ConcurrentPrimate).IsConcurrencyToken(); }); - return finalize ? builder.Model.FinalizeModel() : builder.Model; + return finalize ? builder.Model.FinalizeModel() : (IModel)builder.Model; } private class PrimateContext : DbContext diff --git a/test/EFCore.Tests/Extensions/PropertyExtensionsTest.cs b/test/EFCore.Tests/Extensions/PropertyExtensionsTest.cs index 76fe1457398..ea4a1aa3fc7 100644 --- a/test/EFCore.Tests/Extensions/PropertyExtensionsTest.cs +++ b/test/EFCore.Tests/Extensions/PropertyExtensionsTest.cs @@ -91,7 +91,7 @@ public void Get_generation_property_returns_null_for_property_without_generator( var model = CreateModel(); var entityType = model.AddEntityType("Entity"); - var property = entityType.AddProperty("Property", typeof(int)); + var property = (IProperty)entityType.AddProperty("Property", typeof(int)); Assert.Null(property.FindGenerationProperty()); } @@ -107,7 +107,7 @@ public void Get_generation_property_returns_same_property_on_property_with_gener property.ValueGenerated = ValueGenerated.OnAdd; - Assert.Equal(property, property.FindGenerationProperty()); + Assert.Equal((IProperty)property, ((IProperty)property).FindGenerationProperty()); } [ConditionalFact] @@ -130,7 +130,7 @@ public void Get_generation_property_returns_generation_property_from_foreign_key firstProperty.ValueGenerated = ValueGenerated.OnAdd; - Assert.Equal(firstProperty, thirdProperty.FindGenerationProperty()); + Assert.Equal((IProperty)firstProperty, ((IProperty)thirdProperty).FindGenerationProperty()); } [ConditionalFact] @@ -161,7 +161,7 @@ public void Get_generation_property_returns_generation_property_from_foreign_key rightId2.ValueGenerated = ValueGenerated.OnAdd; - Assert.Equal(rightId2, endProperty.FindGenerationProperty()); + Assert.Equal((IProperty)rightId2, ((IProperty)endProperty).FindGenerationProperty()); } [ConditionalFact] @@ -189,7 +189,7 @@ public void Get_generation_property_returns_generation_property_from_foreign_key leafId1.ValueGenerated = ValueGenerated.OnAdd; - Assert.Equal(leafId1, secondId1.FindGenerationProperty()); + Assert.Equal((IProperty)leafId1, ((IProperty)secondId1).FindGenerationProperty()); } [ConditionalFact] @@ -198,20 +198,20 @@ public void Get_generation_property_for_one_to_one_FKs() var model = BuildModel(); Assert.Equal( - model.FindEntityType(typeof(Product)).FindProperty("Id"), - model.FindEntityType(typeof(ProductDetails)).GetForeignKeys().Single().Properties[0].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(Product)).FindProperty("Id"), + ((IProperty)model.FindEntityType(typeof(ProductDetails)).GetForeignKeys().Single().Properties[0]).FindGenerationProperty()); Assert.Equal( - model.FindEntityType(typeof(Product)).FindProperty("Id"), - model.FindEntityType(typeof(ProductDetailsTag)).GetForeignKeys().Single().Properties[0].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(Product)).FindProperty("Id"), + ((IProperty)model.FindEntityType(typeof(ProductDetailsTag)).GetForeignKeys().Single().Properties[0]).FindGenerationProperty()); Assert.Equal( - model.FindEntityType(typeof(ProductDetails)).FindProperty("Id2"), - model.FindEntityType(typeof(ProductDetailsTag)).GetForeignKeys().Single().Properties[1].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(ProductDetails)).FindProperty("Id2"), + ((IProperty)model.FindEntityType(typeof(ProductDetailsTag)).GetForeignKeys().Single().Properties[1]).FindGenerationProperty()); Assert.Equal( - model.FindEntityType(typeof(ProductDetails)).FindProperty("Id2"), - model.FindEntityType(typeof(ProductDetailsTagDetails)).GetForeignKeys().Single().Properties[0].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(ProductDetails)).FindProperty("Id2"), + ((IProperty)model.FindEntityType(typeof(ProductDetailsTagDetails)).GetForeignKeys().Single().Properties[0]).FindGenerationProperty()); } [ConditionalFact] @@ -220,14 +220,14 @@ public void Get_generation_property_for_one_to_many_identifying_FKs() var model = BuildModel(); Assert.Equal( - model.FindEntityType(typeof(Order)).FindProperty("Id"), - model.FindEntityType(typeof(OrderDetails)).GetForeignKeys().Single(k => k.Properties.First().Name == "OrderId") - .Properties[0].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(Order)).FindProperty("Id"), + ((IProperty)model.FindEntityType(typeof(OrderDetails)).GetForeignKeys().Single(k => k.Properties.First().Name == "OrderId") + .Properties[0]).FindGenerationProperty()); Assert.Equal( - model.FindEntityType(typeof(Product)).FindProperty("Id"), - model.FindEntityType(typeof(OrderDetails)).GetForeignKeys().Single(k => k.Properties.First().Name == "ProductId") - .Properties[0].FindGenerationProperty()); + (IProperty)model.FindEntityType(typeof(Product)).FindProperty("Id"), + ((IProperty)model.FindEntityType(typeof(OrderDetails)).GetForeignKeys().Single(k => k.Properties.First().Name == "ProductId") + .Properties[0]).FindGenerationProperty()); } private class Category diff --git a/test/EFCore.Tests/Infrastructure/CoreEventIdTest.cs b/test/EFCore.Tests/Infrastructure/CoreEventIdTest.cs index 951975d4a57..5c92855f1a4 100644 --- a/test/EFCore.Tests/Infrastructure/CoreEventIdTest.cs +++ b/test/EFCore.Tests/Infrastructure/CoreEventIdTest.cs @@ -48,12 +48,15 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() { typeof(ExpressionPrinter), () => new ExpressionPrinter() }, { typeof(Expression), () => Expression.Constant("A") }, { typeof(IEntityType), () => entityType }, + { typeof(IReadOnlyEntityType), () => entityType }, { typeof(IConventionEntityType), () => entityType }, { typeof(IKey), () => new Key(new[] { property }, ConfigurationSource.Convention) }, { typeof(IPropertyBase), () => property }, + { typeof(IReadOnlyProperty), () => property }, { typeof(IServiceProvider), () => new FakeServiceProvider() }, { typeof(ICollection), () => new List() }, { typeof(IReadOnlyList), () => new[] { property } }, + { typeof(IReadOnlyList), () => new[] { property } }, { typeof(IEnumerable>), () => new[] { new Tuple(propertyInfo, typeof(object)) } @@ -62,9 +65,12 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled() { typeof(IReadOnlyList), () => new[] { new Exception() } }, { typeof(IProperty), () => property }, { typeof(INavigation), () => navigation }, + { typeof(IReadOnlyNavigation), () => navigation }, { typeof(ISkipNavigation), () => skipNavigation }, + { typeof(IReadOnlySkipNavigation), () => skipNavigation }, { typeof(INavigationBase), () => navigationBase }, { typeof(IForeignKey), () => foreignKey }, + { typeof(IReadOnlyForeignKey), () => foreignKey }, { typeof(InternalEntityEntry), () => new FakeInternalEntityEntry(entityType) }, { typeof(ISet), () => new HashSet() }, { @@ -122,10 +128,17 @@ public bool IsCollection public override Type ClrType => throw new NotImplementedException(); + IReadOnlyEntityType IReadOnlyNavigationBase.DeclaringEntityType + => (IReadOnlyEntityType)DeclaringType; + + IReadOnlyEntityType IReadOnlyNavigationBase.TargetEntityType + => throw new NotImplementedException(); + + IReadOnlyNavigationBase IReadOnlyNavigationBase.Inverse + => throw new NotImplementedException(); + public IClrCollectionAccessor GetCollectionAccessor() - { - throw new NotImplementedException(); - } + => throw new NotImplementedException(); } } } diff --git a/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs index ae101a74bb3..9bd0fa4e0fe 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs @@ -392,8 +392,8 @@ public void Does_not_throw_if_explicit_binding_has_been_set() new[] { typeof(string), typeof(int) }), new[] { - new PropertyParameterBinding(e.FindProperty(nameof(Blog.Title))), - new PropertyParameterBinding(e.FindProperty(nameof(Blog.Id))) + new PropertyParameterBinding((IProperty)e.FindProperty(nameof(Blog.Title))), + new PropertyParameterBinding((IProperty)e.FindProperty(nameof(Blog.Id))) })); Assert.NotNull(constructorBinding); diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs index 02c38d471f8..55ecbe96e72 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConventionDispatcherTest.cs @@ -115,11 +115,11 @@ public void OnModelFinalized_calls_conventions_in_order(bool useBuilder) if (useBuilder) { - Assert.Null(new InternalModelBuilder(model).Metadata.FinalizeModel()); + Assert.NotNull(new InternalModelBuilder(model).Metadata.FinalizeModel()); } else { - Assert.Null(model.FinalizeModel()); + Assert.NotNull(model.FinalizeModel()); } Assert.Equal(1, convention1.Calls); @@ -145,7 +145,7 @@ public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConven if (_terminate) { - context.StopProcessing(); + context.StopProcessing(modelBuilder); } } } diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs index 5458fcf520b..885c333a649 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions public class ConventionSetBuilderTests { [ConditionalFact] - public virtual IModel Can_build_a_model_with_default_conventions_without_DI() + public virtual IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI() { var modelBuilder = new ModelBuilder(GetConventionSet()); modelBuilder.Entity(); @@ -24,7 +24,7 @@ public virtual IModel Can_build_a_model_with_default_conventions_without_DI() } [ConditionalFact] - public virtual IModel Can_build_a_model_with_default_conventions_without_DI_new() + public virtual IReadOnlyModel Can_build_a_model_with_default_conventions_without_DI_new() { var modelBuilder = GetModelBuilder(); modelBuilder.Entity(); diff --git a/test/EFCore.Tests/Metadata/Conventions/DiscriminatorConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/DiscriminatorConventionTest.cs index 6946ba433fd..825e9eb92f7 100644 --- a/test/EFCore.Tests/Metadata/Conventions/DiscriminatorConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/DiscriminatorConventionTest.cs @@ -24,7 +24,7 @@ public void Sets_discriminator_for_two_level_hierarchy() RunConvention(entityTypeBuilder, null); - Assert.Null(((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Null(entityTypeBuilder.Metadata.GetDiscriminatorValue()); var baseTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(typeof(EntityBase), ConfigurationSource.Explicit); @@ -32,17 +32,17 @@ public void Sets_discriminator_for_two_level_hierarchy() RunConvention(entityTypeBuilder, null); - var discriminator = ((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); + var discriminator = ((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); Assert.NotNull(discriminator); - Assert.Same(discriminator, ((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Equal(typeof(EntityBase).Name, baseTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Equal(typeof(Entity).Name, entityTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.NotNull(entityTypeBuilder.HasBaseType((Type)null, ConfigurationSource.DataAnnotation)); RunConvention(entityTypeBuilder, baseTypeBuilder.Metadata); - Assert.Null(((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); - Assert.Null(((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); } [ConditionalFact] @@ -52,7 +52,7 @@ public void Sets_discriminator_for_three_level_hierarchy() RunConvention(entityTypeBuilder, null); - Assert.Null(((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Null(entityTypeBuilder.Metadata.GetDiscriminatorValue()); var baseTypeBuilder = entityTypeBuilder.ModelBuilder.Entity(typeof(EntityBase), ConfigurationSource.Explicit); @@ -68,10 +68,10 @@ public void Sets_discriminator_for_three_level_hierarchy() RunConvention(entityTypeBuilder, null); - var discriminator = ((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); + var discriminator = ((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); Assert.NotNull(discriminator); - Assert.Same(discriminator, ((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); - Assert.Same(discriminator, ((IEntityType)derivedTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)derivedTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Equal(typeof(EntityBase).Name, baseTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Equal(typeof(Entity).Name, entityTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Equal(typeof(DerivedEntity).Name, derivedTypeBuilder.Metadata.GetDiscriminatorValue()); @@ -80,10 +80,10 @@ public void Sets_discriminator_for_three_level_hierarchy() RunConvention(entityTypeBuilder, baseTypeBuilder.Metadata); - Assert.Null(((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); - discriminator = ((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); + Assert.Null(((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + discriminator = ((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); Assert.NotNull(discriminator); - Assert.Same(discriminator, ((IEntityType)derivedTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)derivedTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Equal(typeof(Entity).Name, entityTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Equal(typeof(DerivedEntity).Name, derivedTypeBuilder.Metadata.GetDiscriminatorValue()); } @@ -99,9 +99,9 @@ public void Uses_explicit_discriminator_if_compatible() RunConvention(entityTypeBuilder, null); - var discriminator = ((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); + var discriminator = ((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); Assert.NotNull(discriminator); - Assert.Same(discriminator, ((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Equal("T", discriminator.Name); Assert.Equal(typeof(string), discriminator.ClrType); Assert.Equal(typeof(EntityBase).Name, baseTypeBuilder.Metadata.GetDiscriminatorValue()); @@ -119,9 +119,9 @@ public void Does_nothing_if_explicit_discriminator_is_not_compatible() RunConvention(entityTypeBuilder, null); - var discriminator = ((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); + var discriminator = ((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty(); Assert.NotNull(discriminator); - Assert.Same(discriminator, ((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Same(discriminator, ((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Equal("T", discriminator.Name); Assert.Equal(typeof(int), discriminator.ClrType); Assert.Null(baseTypeBuilder.Metadata.GetDiscriminatorValue()); @@ -140,8 +140,8 @@ public void Does_nothing_if_explicit_discriminator_set_on_derived_type() RunConvention(entityTypeBuilder, null); - Assert.Null(((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); - Assert.Null(((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Null(baseTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Null(entityTypeBuilder.Metadata.GetDiscriminatorValue()); @@ -149,8 +149,8 @@ public void Does_nothing_if_explicit_discriminator_set_on_derived_type() RunConvention(entityTypeBuilder, baseTypeBuilder.Metadata); - Assert.Null(((IEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); - Assert.NotNull(((IEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.Null(((IReadOnlyEntityType)baseTypeBuilder.Metadata).GetDiscriminatorProperty()); + Assert.NotNull(((IReadOnlyEntityType)entityTypeBuilder.Metadata).GetDiscriminatorProperty()); Assert.Null(baseTypeBuilder.Metadata.GetDiscriminatorValue()); Assert.Null(entityTypeBuilder.Metadata.GetDiscriminatorValue()); } diff --git a/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs index c64373d2647..ae707c922ba 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ForeignKeyPropertyDiscoveryConventionTest.cs @@ -49,7 +49,7 @@ public void Does_not_override_explicit_foreign_key_created_using_given_property( RunConvention(relationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fk, dependentTypeBuilder.Metadata.GetForeignKeys().Single()); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -84,7 +84,7 @@ public void Does_not_override_explicit_composite_foreign_key_created_using_given RunConvention(relationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fk, DependentTypeWithCompositeKey.GetForeignKeys().Single()); Assert.Same(fkProperty1.Metadata, fk.Properties[0]); Assert.Same(fkProperty2.Metadata, fk.Properties[1]); @@ -135,7 +135,7 @@ public void Matches_navigation_plus_PK_name_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -170,7 +170,7 @@ public void Matches_navigation_plus_PK_name_properties() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty1.Metadata, fk.Properties[0]); Assert.Same(fkProperty2.Metadata, fk.Properties[1]); @@ -199,7 +199,7 @@ public void Matches_navigation_plus_Id_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -227,7 +227,7 @@ public void Matches_principal_type_plus_PK_name_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -252,7 +252,7 @@ public void Matches_principal_type_plus_Id_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -281,7 +281,7 @@ public void Matches_principal_type_plus_PK_name_properties() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty1.Metadata, fk.Properties[0]); Assert.Same(fkProperty2.Metadata, fk.Properties[1]); @@ -306,7 +306,7 @@ public void Does_not_match_PK_name_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Equal("SomeNav" + PrimaryKey.Name, fk.Properties.Single().Name); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -332,7 +332,7 @@ public void Matches_key_Id_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -356,7 +356,7 @@ public void Does_not_match_non_key_Id_property() Assert.NotEqual(DependentEntity.IDProperty.Name, DependentType.FindPrimaryKey().Properties.Single().Name); - var fk = (IForeignKey)newRelationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)newRelationshipBuilder.Metadata; Assert.Same(fk, DependentType.GetForeignKeys().Single()); Assert.Equal("SomeNav" + PrimaryKey.Name, fk.Properties.Single().Name); Assert.True(fk.IsUnique); @@ -382,7 +382,7 @@ public void Does_not_match_PK_name_properties() var newRelationshipBuilder = RunConvention(relationshipBuilder); - var fk = (IForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Equal("NavProp" + CompositePrimaryKey[0].Name + "1", fk.Properties[0].Name); Assert.Equal("NavProp" + CompositePrimaryKey[1].Name + "1", fk.Properties[1].Name); @@ -417,7 +417,7 @@ public void Does_not_match_PK_name_properties_if_subset_of_dependent_PK_and_cont var newRelationshipBuilder = RunConvention(relationshipBuilder); - var fk = (IForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Equal("NavProp" + CompositePrimaryKey[0].Name + "1", fk.Properties[0].Name); Assert.Equal("NavProp" + CompositePrimaryKey[1].Name + "1", fk.Properties[1].Name); @@ -449,7 +449,7 @@ public void Matches_PK_name_properties_if_subset_of_dependent_PK() var newRelationshipBuilder = RunConvention(relationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties[0]); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties[0]); @@ -473,7 +473,7 @@ public void Matches_dependent_PK_for_unique_FK_set_by_higher_source_than_convent var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -498,7 +498,7 @@ public void Does_not_match_principal_type_plus_PK_name_property_of_different_typ var newRelationshipBuilder = RunConvention(relationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.NotSame(fkProperty, fk.Properties.Single()); Assert.Null(((ForeignKey)fk).GetPropertiesConfigurationSource()); @@ -529,7 +529,7 @@ public void Does_not_match_dependent_PK_for_non_unique_FK() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fk, DependentType.GetForeignKeys().Single()); Assert.Equal(PrincipalType.DisplayName() + PrimaryKey.Name, fk.Properties.Single().Name); Assert.False(fk.IsUnique); @@ -549,7 +549,7 @@ public void Does_not_match_non_nullable_dependent_PK_for_optional_unique_FK() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.NotSame(fkProperty, fk.Properties.Single()); Assert.Equal("PrincipalEntity" + PrimaryKey.Name, fk.Properties.Single().Name); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -568,7 +568,7 @@ public void Does_not_match_dependent_PK_for_self_ref() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fk, PrincipalType.GetForeignKeys().Single()); Assert.Equal(PrincipalType.DisplayName() + PrimaryKey.Name, fk.Properties.Single().Name); Assert.True(fk.IsUnique); @@ -591,7 +591,7 @@ public void Does_not_match_for_convention_identifying_FK() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Equal(nameof(PrincipalEntity.PeeKay), fk.Properties.Single().Name); Assert.Same(fk, derivedType.Metadata.GetForeignKeys().Single()); Assert.True(fk.IsUnique); @@ -617,7 +617,7 @@ public void Matches_composite_dependent_PK_for_unique_FK() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentTypeWithCompositeKey.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty1, fk.Properties[0]); Assert.Same(fkProperty2, fk.Properties[1]); @@ -713,7 +713,7 @@ public void Does_not_match_composite_dependent_PK_for_unique_FK_if_count_mismatc var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fk, DependentTypeWithCompositeKey.GetForeignKeys().Single()); Assert.Equal(2, fk.Properties.Count); Assert.NotSame(fkProperty1, fk.Properties[0]); @@ -805,7 +805,7 @@ public void Does_not_match_if_a_foreign_key_on_the_best_candidate_property_alrea .Single(foreignKey => foreignKey != newRelationshipBuilder.Metadata) .Builder.HasForeignKey(new[] { fkProperty }, ConfigurationSource.Convention); - var fk = (IForeignKey)relationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)relationshipBuilder.Metadata; Assert.Same(fkProperty, fk.Properties.Single()); Assert.False(fk.IsUnique); @@ -870,7 +870,7 @@ public void Inverts_if_principal_entity_type_can_have_non_pk_fk_property() newRelationshipBuilder = RunConvention(newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(fkProperty, fk.Properties.Single()); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); @@ -895,7 +895,7 @@ public void Does_not_invert_if_weak_entity_type_can_have_non_pk_fk_property() newRelationshipBuilder = RunConvention(newRelationshipBuilder); - var fk = (IForeignKey)DependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)DependentType.GetForeignKeys().Single(); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(DependentType, fk.DeclaringEntityType); Assert.Same(fkProperty, fk.Properties.Single()); @@ -919,7 +919,7 @@ public void Does_not_invert_if_both_entity_types_can_have_non_pk_fk_property() var newRelationshipBuilder = RunConvention(relationshipBuilder); Assert.Same(relationshipBuilder, newRelationshipBuilder); - var fk = (IForeignKey)newRelationshipBuilder.Metadata; + var fk = (IReadOnlyForeignKey)newRelationshipBuilder.Metadata; Assert.Same(dependentTypeBuilder.Metadata, fk.DeclaringEntityType); Assert.Same(PrincipalType.FindPrimaryKey(), fk.PrincipalKey); Assert.True(fk.IsUnique); @@ -944,7 +944,7 @@ public void Does_not_invert_if_principal_entity_type_owns_the_weak_entity_type() newRelationshipBuilder = RunConvention(newRelationshipBuilder); - var fk = (IForeignKey)dependentTypeBuilder.Metadata.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)dependentTypeBuilder.Metadata.GetForeignKeys().Single(); Assert.Same(dependentTypeBuilder.Metadata, fk.DeclaringEntityType); Assert.Same(fk, newRelationshipBuilder.Metadata); Assert.Same(PrimaryKey, fk.PrincipalKey.Properties.Single()); diff --git a/test/EFCore.Tests/Metadata/Conventions/RelationshipDiscoveryConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/RelationshipDiscoveryConventionTest.cs index 48ad9cf2ca4..dd37771bd00 100644 --- a/test/EFCore.Tests/Metadata/Conventions/RelationshipDiscoveryConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/RelationshipDiscoveryConventionTest.cs @@ -423,7 +423,7 @@ public void Multiple_navigations_to_same_entity_type_are_discovered() Assert.Same(entityBuilder, RunConvention(entityBuilder)); - var model = (IModel)entityBuilder.Metadata.Model; + var model = (IReadOnlyModel)entityBuilder.Metadata.Model; Assert.Equal(2, model.GetEntityTypes().Count()); var firstEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(MultipleNavigationsFirst)); var secondEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(MultipleNavigationsSecond)); @@ -811,7 +811,7 @@ public void Bidirectional_ambiguous_cardinality_is_discovered() Assert.Same(entityBuilder, RunConvention(entityBuilder)); - var model = (IModel)entityBuilder.Metadata.Model; + var model = (IReadOnlyModel)entityBuilder.Metadata.Model; Assert.Equal(2, model.GetEntityTypes().Count()); var firstEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(AmbiguousCardinalityOne)); var secondEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(AmbiguousCardinalityTwo)); @@ -829,7 +829,7 @@ public void Unidirectional_ambiguous_cardinality_is_discovered() Assert.Same(entityBuilder, RunConvention(entityBuilder)); - var model = (IModel)entityBuilder.Metadata.Model; + var model = (IReadOnlyModel)entityBuilder.Metadata.Model; Assert.Equal(2, model.GetEntityTypes().Count()); var firstEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(AmbiguousCardinalityOne)); var secondEntityType = model.GetEntityTypes().Single(e => e.ClrType == typeof(AmbiguousCardinalityTwo)); @@ -979,7 +979,7 @@ public void Navigation_to_abstract_is_discovered() Assert.Same(entityBuilder, RunConvention(entityBuilder)); - IModel model = entityBuilder.Metadata.Model; + IReadOnlyModel model = entityBuilder.Metadata.Model; var entityType = model.GetEntityTypes().Single(); Assert.Equal(2, entityType.GetProperties().Count()); @@ -998,7 +998,7 @@ public void Collection_navigation_without_setter_is_discovered() Assert.Same(entityBuilder, RunConvention(entityBuilder)); - IModel model = entityBuilder.Metadata.Model; + IReadOnlyModel model = entityBuilder.Metadata.Model; Assert.NotNull(model.FindEntityType(typeof(EntityWithNoValidNavigations))); var entityType = entityBuilder.Metadata; @@ -1096,7 +1096,7 @@ private static void VerifyRelationship( bool unique, bool singleRelationship = true) { - IForeignKey fk = navigation.ForeignKey; + IReadOnlyForeignKey fk = navigation.ForeignKey; Assert.Equal(expectedInverseName, navigation.Inverse?.Name); Assert.Equal(unique, fk.IsUnique); Assert.NotSame(fk.Properties.Single(), fk.PrincipalKey.Properties.Single()); @@ -1131,7 +1131,7 @@ private static void VerifySelfRef( bool unique, bool singleRelationship = true) { - IForeignKey fk = navigation.ForeignKey; + IReadOnlyForeignKey fk = navigation.ForeignKey; Assert.Single(fk.DeclaringEntityType.Model.GetEntityTypes()); Assert.Equal(expectedInverseName, navigation.Inverse?.Name); Assert.Equal(unique, fk.IsUnique); diff --git a/test/EFCore.Tests/Metadata/Conventions/ValueGeneratorConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ValueGeneratorConventionTest.cs index cfdefec16a8..fb6292a88db 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ValueGeneratorConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ValueGeneratorConventionTest.cs @@ -361,8 +361,8 @@ public void Identity_is_recomputed_when_primary_key_is_changed() Assert.Same(idProperty, entityBuilder.Metadata.FindProperty("Id")); Assert.Same(numberProperty, entityBuilder.Metadata.FindProperty("Number")); - Assert.Equal(ValueGenerated.Never, ((IProperty)idProperty).ValueGenerated); - Assert.Equal(ValueGenerated.OnAdd, ((IProperty)numberProperty).ValueGenerated); + Assert.Equal(ValueGenerated.Never, ((IReadOnlyProperty)idProperty).ValueGenerated); + Assert.Equal(ValueGenerated.OnAdd, ((IReadOnlyProperty)numberProperty).ValueGenerated); } [ConditionalFact] @@ -399,7 +399,7 @@ public void Identity_is_removed_when_foreign_key_is_added() var property = keyBuilder.Metadata.Properties.First(); - Assert.Equal(ValueGenerated.OnAdd, ((IProperty)property).ValueGenerated); + Assert.Equal(ValueGenerated.OnAdd, ((IReadOnlyProperty)property).ValueGenerated); var relationshipBuilder = referencedEntityBuilder.HasRelationship( principalEntityBuilder.Metadata, @@ -408,7 +408,7 @@ public void Identity_is_removed_when_foreign_key_is_added() RunConvention(relationshipBuilder); - Assert.Equal(ValueGenerated.Never, ((IProperty)property).ValueGenerated); + Assert.Equal(ValueGenerated.Never, ((IReadOnlyProperty)property).ValueGenerated); } [ConditionalFact] @@ -435,7 +435,7 @@ public void Identity_is_added_when_foreign_key_is_removed_and_key_is_primary_key RunConvention(relationshipBuilder); - Assert.Equal(ValueGenerated.Never, ((IProperty)property).ValueGenerated); + Assert.Equal(ValueGenerated.Never, ((IReadOnlyProperty)property).ValueGenerated); referencedEntityBuilder.HasNoRelationship(relationshipBuilder.Metadata, ConfigurationSource.Convention); diff --git a/test/EFCore.Tests/Metadata/EntityTypeExtensionsTest.cs b/test/EFCore.Tests/Metadata/EntityTypeExtensionsTest.cs index 97bf689f032..d9ecca18c36 100644 --- a/test/EFCore.Tests/Metadata/EntityTypeExtensionsTest.cs +++ b/test/EFCore.Tests/Metadata/EntityTypeExtensionsTest.cs @@ -25,8 +25,8 @@ public void Can_get_all_properties_and_navigations() var principalToDependent = fk.SetPrincipalToDependent(nameof(SelfRef.SelfRefDependent)); Assert.Equal( - new IPropertyBase[] { pk.Properties.Single(), fkProp, principalToDependent, dependentToPrincipal }, - entityType.GetPropertiesAndNavigations().ToArray()); + new IReadOnlyPropertyBase[] { pk.Properties.Single(), fkProp, principalToDependent, dependentToPrincipal }, + ((IEntityType)entityType).GetPropertiesAndNavigations().ToArray()); } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs index 7bec8ce7395..7024ccc2727 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrCollectionAccessorFactoryTest.cs @@ -44,12 +44,12 @@ public void Navigation_is_returned_if_it_implements_IClrCollectionAccessor() private class FakeNavigation : Annotatable, INavigation, IClrCollectionAccessor { public string Name { get; } - public ITypeBase DeclaringType { get; } + public IReadOnlyTypeBase DeclaringType { get; } public Type ClrType { get; } public PropertyInfo PropertyInfo { get; set; } public FieldInfo FieldInfo { get; } public IEntityType DeclaringEntityType { get; } - public IForeignKey ForeignKey { get; set; } + public IReadOnlyForeignKey ForeignKey { get; set; } public bool Add(object entity, object value, bool forMaterialization) => throw new NotImplementedException(); @@ -66,6 +66,8 @@ public object Create() public object GetOrCreate(object entity, bool forMaterialization) => throw new NotImplementedException(); + public IClrPropertyGetter GetGetter() => throw new NotImplementedException(); + public Type CollectionType { get; } } @@ -268,7 +270,7 @@ public void Delegate_accessor_always_creates_collections_that_use_reference_equa RunConvention(navigation); - var accessor = new ClrCollectionAccessorFactory().Create(navigation); + var accessor = new ClrCollectionAccessorFactory().Create((INavigation)navigation); var entity = new MyEntity(initialize: false); var value = new MyEntityWithCustomComparer { Id = 1 }; @@ -409,7 +411,7 @@ public void Initialization_for_navigation_without_parameterless_constructor_thro () => accessor.Add(new MyEntity(false), new MyOtherEntity(), forMaterialization: false)).Message); } - private IMutableNavigation CreateNavigation(string navigationName) + private INavigation CreateNavigation(string navigationName) { IMutableModel model = new Model(); var entityType = model.AddEntityType(typeof(MyEntity)); @@ -424,7 +426,7 @@ private IMutableNavigation CreateNavigation(string navigationName) RunConvention(navigation); - return navigation; + return (INavigation)navigation; } private void RunConvention(IMutableNavigation navigation) diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs index 4adcd6d37f6..4c438ce2d99 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs @@ -29,6 +29,18 @@ public object GetClrValue(object entity) public bool HasDefaultValue(object entity) => throw new NotImplementedException(); + public IEnumerable GetContainingForeignKeys() + => throw new NotImplementedException(); + + public IEnumerable GetContainingIndexes() + => throw new NotImplementedException(); + + public IEnumerable GetContainingKeys() + => throw new NotImplementedException(); + + public IClrPropertyGetter GetGetter() + => throw new NotImplementedException(); + public string Name { get; } public ITypeBase DeclaringType { get; } public Type ClrType { get; } @@ -38,6 +50,10 @@ public bool HasDefaultValue(object entity) public bool IsConcurrencyToken { get; } public PropertyInfo PropertyInfo { get; } public FieldInfo FieldInfo { get; } + + IReadOnlyEntityType IReadOnlyProperty.DeclaringEntityType => throw new NotImplementedException(); + + IReadOnlyTypeBase IReadOnlyPropertyBase.DeclaringType => throw new NotImplementedException(); } [ConditionalFact] @@ -48,7 +64,7 @@ public void Delegate_getter_is_returned_for_IProperty_property() modelBuilder.FinalizeModel(); Assert.Equal( - 7, new ClrPropertyGetterFactory().Create(idProperty).GetClrValue( + 7, new ClrPropertyGetterFactory().Create((IPropertyBase)idProperty).GetClrValue( new Customer { Id = 7 })); } @@ -70,7 +86,7 @@ public void Delegate_getter_is_returned_for_IProperty_struct_property() Assert.Equal( new Fuel(1.0), - new ClrPropertyGetterFactory().Create(fuelProperty).GetClrValue( + new ClrPropertyGetterFactory().Create((IPropertyBase)fuelProperty).GetClrValue( new Customer { Id = 7, Fuel = new Fuel(1.0) })); } @@ -92,8 +108,8 @@ public void Delegate_getter_is_returned_for_index_property() var propertyB = modelBuilder.Entity().Metadata.AddIndexerProperty("PropertyB", typeof(int)); modelBuilder.FinalizeModel(); - Assert.Equal("ValueA", new ClrPropertyGetterFactory().Create(propertyA).GetClrValue(new IndexedClass { Id = 7 })); - Assert.Equal(123, new ClrPropertyGetterFactory().Create(propertyB).GetClrValue(new IndexedClass { Id = 7 })); + Assert.Equal("ValueA", new ClrPropertyGetterFactory().Create((IPropertyBase)propertyA).GetClrValue(new IndexedClass { Id = 7 })); + Assert.Equal(123, new ClrPropertyGetterFactory().Create((IPropertyBase)propertyB).GetClrValue(new IndexedClass { Id = 7 })); } private static ModelBuilder CreateModelBuilder() diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs index 3fe4c6b8489..881db0b345a 100644 --- a/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertySetterFactoryTest.cs @@ -24,9 +24,6 @@ public void Property_is_returned_if_it_implements_IClrPropertySetter() private class FakeProperty : Annotatable, IProperty, IClrPropertySetter { - public void SetClrValue(object instance, object value) - => throw new NotImplementedException(); - public string Name { get; } public ITypeBase DeclaringType { get; } public Type ClrType { get; } @@ -39,6 +36,25 @@ public void SetClrValue(object instance, object value) public bool IsConcurrencyToken { get; } public PropertyInfo PropertyInfo { get; } public FieldInfo FieldInfo { get; } + + IReadOnlyEntityType IReadOnlyProperty.DeclaringEntityType => throw new NotImplementedException(); + + IReadOnlyTypeBase IReadOnlyPropertyBase.DeclaringType => throw new NotImplementedException(); + + public void SetClrValue(object instance, object value) + => throw new NotImplementedException(); + + public IEnumerable GetContainingForeignKeys() + => throw new NotImplementedException(); + + public IEnumerable GetContainingIndexes() + => throw new NotImplementedException(); + + public IEnumerable GetContainingKeys() + => throw new NotImplementedException(); + + public IClrPropertyGetter GetGetter() + => throw new NotImplementedException(); } [ConditionalFact] @@ -49,7 +65,7 @@ public void Delegate_setter_is_returned_for_IProperty_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, 77); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, 77); Assert.Equal(77, customer.Id); } @@ -72,7 +88,7 @@ public void Delegate_setter_can_set_value_type_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, 1); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, 1); Assert.Equal(1, customer.Id); } @@ -85,7 +101,7 @@ public void Delegate_setter_can_set_reference_type_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, "MyString"); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, "MyString"); Assert.Equal("MyString", customer.Content); } @@ -98,7 +114,7 @@ public void Delegate_setter_can_set_nullable_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, 3); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, 3); Assert.Equal(3, customer.OptionalInt); } @@ -111,7 +127,7 @@ public void Delegate_setter_can_set_nullable_property_with_null_value() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, null); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, null); Assert.Null(customer.OptionalInt); } @@ -124,7 +140,7 @@ public void Delegate_setter_can_set_enum_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, Flag.One); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, Flag.One); Assert.Equal(Flag.One, customer.Flag); } @@ -137,7 +153,7 @@ public void Delegate_setter_can_set_nullable_enum_property() var customer = new Customer { Id = 7 }; - new ClrPropertySetterFactory().Create(idProperty).SetClrValue(customer, Flag.Two); + new ClrPropertySetterFactory().Create((IProperty)idProperty).SetClrValue(customer, Flag.Two); Assert.Equal(Flag.Two, customer.OptionalFlag); } @@ -150,7 +166,7 @@ public void Delegate_setter_can_set_on_virtual_privatesetter_property_override_s typeof(ConcreteEntity1).GetProperty(nameof(ConcreteEntity1.VirtualPrivateProperty_Override))); var entity = new ConcreteEntity1(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.VirtualPrivateProperty_Override); } @@ -162,7 +178,7 @@ public void Delegate_setter_can_set_on_virtual_privatesetter_property_override_m typeof(ConcreteEntity2).GetProperty(nameof(ConcreteEntity2.VirtualPrivateProperty_Override))); var entity = new ConcreteEntity2(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.VirtualPrivateProperty_Override); } @@ -174,7 +190,7 @@ public void Delegate_setter_can_set_on_virtual_privatesetter_property_no_overrid typeof(ConcreteEntity1).GetProperty(nameof(ConcreteEntity1.VirtualPrivateProperty_NoOverride))); var entity = new ConcreteEntity1(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.VirtualPrivateProperty_NoOverride); } @@ -186,7 +202,7 @@ public void Delegate_setter_can_set_on_virtual_privatesetter_property_no_overrid typeof(ConcreteEntity2).GetProperty(nameof(ConcreteEntity2.VirtualPrivateProperty_NoOverride))); var entity = new ConcreteEntity2(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.VirtualPrivateProperty_NoOverride); } @@ -197,7 +213,7 @@ public void Delegate_setter_can_set_on_privatesetter_property_singlebasetype() var property = entityType.AddProperty(typeof(ConcreteEntity1).GetProperty(nameof(ConcreteEntity1.PrivateProperty))); var entity = new ConcreteEntity1(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.PrivateProperty); } @@ -208,7 +224,7 @@ public void Delegate_setter_can_set_on_privatesetter_property_multiplebasetypes( var property = entityType.AddProperty(typeof(ConcreteEntity2).GetProperty(nameof(ConcreteEntity2.PrivateProperty))); var entity = new ConcreteEntity2(); - new ClrPropertySetterFactory().Create(property).SetClrValue(entity, 100); + new ClrPropertySetterFactory().Create((IProperty)property).SetClrValue(entity, 100); Assert.Equal(100, entity.PrivateProperty); } @@ -219,13 +235,13 @@ public void Delegate_setter_throws_if_no_setter_found() var property = entityType.AddProperty(typeof(ConcreteEntity1).GetProperty(nameof(ConcreteEntity1.NoSetterProperty))); Assert.Throws( - () => new ClrPropertySetterFactory().Create(property)); + () => new ClrPropertySetterFactory().Create((IProperty)property)); entityType = CreateModel().AddEntityType(typeof(ConcreteEntity2)); property = entityType.AddProperty(typeof(ConcreteEntity2).GetProperty(nameof(ConcreteEntity2.NoSetterProperty))); Assert.Throws( - () => new ClrPropertySetterFactory().Create(property)); + () => new ClrPropertySetterFactory().Create((IProperty)property)); } [ConditionalFact] @@ -240,8 +256,8 @@ public void Delegate_setter_can_set_index_properties() Assert.Equal("ValueA", indexedClass["PropertyA"]); Assert.Equal(123, indexedClass["PropertyB"]); - new ClrPropertySetterFactory().Create(propertyA).SetClrValue(indexedClass, "UpdatedValue"); - new ClrPropertySetterFactory().Create(propertyB).SetClrValue(indexedClass, 42); + new ClrPropertySetterFactory().Create((IProperty)propertyA).SetClrValue(indexedClass, "UpdatedValue"); + new ClrPropertySetterFactory().Create((IProperty)propertyB).SetClrValue(indexedClass, 42); Assert.Equal("UpdatedValue", indexedClass["PropertyA"]); Assert.Equal(42, indexedClass["PropertyB"]); diff --git a/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs b/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs index a1b64bcb679..57f5578d5c1 100644 --- a/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs @@ -32,7 +32,7 @@ public void Throws_for_abstract_types() Assert.Equal( CoreStrings.CannotMaterializeAbstractType(nameof(SomeAbstractEntity)), - Assert.Throws(() => source.CreateMaterializeExpression(entityType, "", null!)).Message); + Assert.Throws(() => source.CreateMaterializeExpression((IEntityType)entityType, "", null!)).Message); } [ConditionalFact] @@ -45,8 +45,8 @@ public void Can_create_materializer_for_entity_with_constructor_properties() typeof(SomeEntity).GetTypeInfo().DeclaredConstructors.Single(c => c.GetParameters().Length == 2), new List { - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Id))), - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Goo))) + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Id))), + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Goo))) } ); ((Model)entityType.Model).FinalizeModel(); @@ -81,8 +81,8 @@ public void Can_create_materializer_for_entity_with_factory_method() typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod(nameof(SomeEntity.Factory)), new List { - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Id))), - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Goo))) + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Id))), + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Goo))) }, entityType.ClrType); ((Model)entityType.Model).FinalizeModel(); @@ -120,8 +120,8 @@ public void Can_create_materializer_for_entity_with_factory_method_with_object_a new ObjectArrayParameterBinding( new List { - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Id))), - new PropertyParameterBinding(entityType.FindProperty(nameof(SomeEntity.Goo))) + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Id))), + new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Goo))) }) }, entityType.ClrType); @@ -307,9 +307,9 @@ public void Throws_if_parameterless_constructor_is_not_defined_on_entity_type() private static readonly ParameterExpression _contextParameter = Expression.Parameter(typeof(MaterializationContext), "materializationContext"); - public virtual Func GetMaterializer(IEntityMaterializerSource source, IEntityType entityType) + public virtual Func GetMaterializer(IEntityMaterializerSource source, IReadOnlyEntityType entityType) => Expression.Lambda>( - source.CreateMaterializeExpression(entityType, "instance", _contextParameter), + source.CreateMaterializeExpression((IEntityType)entityType, "instance", _contextParameter), _contextParameter) .Compile(); diff --git a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs index 6f3fdbb7201..3e34ace1339 100644 --- a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs +++ b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs @@ -112,22 +112,24 @@ public void Properties_on_base_type_should_be_inherited() a.AddProperty(A.GProperty); a.AddProperty(A.EProperty); - var b = model.AddEntityType(typeof(B)); - b.AddProperty(B.HProperty); - b.AddProperty(B.FProperty); + var bType = model.AddEntityType(typeof(B)); + bType.AddProperty(B.HProperty); + bType.AddProperty(B.FProperty); - var c = model.AddEntityType(typeof(C)); - c.AddProperty(C.HProperty); - c.AddProperty("I", typeof(string)); + var cType = model.AddEntityType(typeof(C)); + cType.AddProperty(C.HProperty); + cType.AddProperty("I", typeof(string)); Assert.Equal(new[] { "E", "G" }, a.GetProperties().Select(p => p.Name).ToArray()); - Assert.Equal(new[] { "F", "H" }, b.GetProperties().Select(p => p.Name).ToArray()); - Assert.Equal(new[] { "H", "I" }, c.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "F", "H" }, bType.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "H", "I" }, cType.GetProperties().Select(p => p.Name).ToArray()); - b.BaseType = a; - c.BaseType = a; + bType.BaseType = a; + cType.BaseType = a; - model.FinalizeModel(); + var builtModel = model.FinalizeModel(); + var b = builtModel.FindEntityType(typeof(B)); + var c = builtModel.FindEntityType(typeof(C)); Assert.Equal(new[] { "E", "G" }, a.GetProperties().Select(p => p.Name).ToArray()); Assert.Equal(new[] { "E", "G", "F", "H" }, b.GetProperties().Select(p => p.Name).ToArray()); @@ -147,22 +149,24 @@ public void Properties_added_to_base_type_should_be_inherited() // B C var a = model.AddEntityType(typeof(A)); - var b = model.AddEntityType(typeof(B)); - var c = model.AddEntityType(typeof(C)); + var bType = model.AddEntityType(typeof(B)); + var cType = model.AddEntityType(typeof(C)); - b.BaseType = a; - c.BaseType = a; + bType.BaseType = a; + cType.BaseType = a; a.AddProperty(A.GProperty); a.AddProperty(A.EProperty); - b.AddProperty(B.HProperty); - b.AddProperty(B.FProperty); + bType.AddProperty(B.HProperty); + bType.AddProperty(B.FProperty); - c.AddProperty(C.HProperty); - c.AddProperty("I", typeof(string)); + cType.AddProperty(C.HProperty); + cType.AddProperty("I", typeof(string)); - model.FinalizeModel(); + var builtModel = model.FinalizeModel(); + var b = builtModel.FindEntityType(typeof(B)); + var c = builtModel.FindEntityType(typeof(C)); Assert.Equal(new[] { "E", "G" }, a.GetProperties().Select(p => p.Name).ToArray()); Assert.Equal(new[] { "E", "G", "F", "H" }, b.GetProperties().Select(p => p.Name).ToArray()); @@ -176,30 +180,33 @@ public void Properties_should_be_updated_when_base_type_changes() { var model = CreateModel(); - var c = model.AddEntityType(typeof(C)); - c.AddProperty(C.HProperty); - c.AddProperty(C.FProperty); + var cType = model.AddEntityType(typeof(C)); + cType.AddProperty(C.HProperty); + cType.AddProperty(C.FProperty); - var d = model.AddEntityType(typeof(D)); - d.AddProperty(A.EProperty); - d.AddProperty(A.GProperty); - d.BaseType = c; + var dType = model.AddEntityType(typeof(D)); + dType.AddProperty(A.EProperty); + dType.AddProperty(A.GProperty); + dType.BaseType = cType; - Assert.Equal(new[] { "F", "H" }, c.GetProperties().Select(p => p.Name).ToArray()); - Assert.Equal(new[] { "F", "H", "E", "G" }, d.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "F", "H" }, cType.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "F", "H", "E", "G" }, dType.GetProperties().Select(p => p.Name).ToArray()); - d.BaseType = null; + dType.BaseType = null; - Assert.Equal(new[] { "F", "H" }, c.GetProperties().Select(p => p.Name).ToArray()); - Assert.Equal(new[] { "E", "G" }, d.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "F", "H" }, cType.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "E", "G" }, dType.GetProperties().Select(p => p.Name).ToArray()); - var a = model.AddEntityType(typeof(A)); - a.AddProperty(A.EProperty); - a.AddProperty(A.GProperty); + var aType = model.AddEntityType(typeof(A)); + aType.AddProperty(A.EProperty); + aType.AddProperty(A.GProperty); - c.BaseType = a; + cType.BaseType = aType; - model.FinalizeModel(); + var builtModel = model.FinalizeModel(); + var a = builtModel.FindEntityType(typeof(A)); + var c = builtModel.FindEntityType(typeof(C)); + var d = builtModel.FindEntityType(typeof(D)); Assert.Equal(new[] { "E", "G" }, a.GetProperties().Select(p => p.Name).ToArray()); Assert.Equal(new[] { "E", "G", "F", "H" }, c.GetProperties().Select(p => p.Name).ToArray()); @@ -352,21 +359,22 @@ public void Keys_on_base_type_should_be_inherited() var pk = a.SetPrimaryKey(g); a.AddKey(e); - var b = model.AddEntityType(typeof(B)); - b.AddProperty(B.FProperty); + var bType = model.AddEntityType(typeof(B)); + bType.AddProperty(B.FProperty); Assert.Equal( new[] { new[] { "E" }, new[] { "G" } }, a.GetKeys().Select(fk => fk.Properties.Select(p => p.Name).ToArray()).ToArray()); Assert.Equal( Array.Empty(), - b.GetKeys().Select(fk => fk.Properties.Select(p => p.Name).ToArray()).ToArray()); + bType.GetKeys().Select(fk => fk.Properties.Select(p => p.Name).ToArray()).ToArray()); Assert.Equal(new[] { "G", "E" }, a.GetProperties().Select(p => p.Name).ToArray()); - Assert.Equal(new[] { "F" }, b.GetProperties().Select(p => p.Name).ToArray()); + Assert.Equal(new[] { "F" }, bType.GetProperties().Select(p => p.Name).ToArray()); - b.BaseType = a; + bType.BaseType = a; - model.FinalizeModel(); + var builtModel = model.FinalizeModel(); + var b = builtModel.FindEntityType(typeof(B)); Assert.Equal( new[] { new[] { "E" }, new[] { "G" } }, @@ -539,7 +547,7 @@ public void Navigations_on_base_type_should_be_inherited() Assert.Equal(new[] { "Orders" }, customerType.GetNavigations().Select(p => p.Name).ToArray()); Assert.Equal(new[] { "Orders", "DerivedOrders" }, specialCustomerType.GetNavigations().Select(p => p.Name).ToArray()); Assert.Equal( - new[] { "Orders", "DerivedOrders" }, ((IEntityType)specialCustomerType).GetNavigations().Select(p => p.Name).ToArray()); + new[] { "Orders", "DerivedOrders" }, ((IReadOnlyEntityType)specialCustomerType).GetNavigations().Select(p => p.Name).ToArray()); Assert.Same(customerType.FindNavigation("Orders"), specialCustomerType.FindNavigation("Orders")); } @@ -652,7 +660,7 @@ public void Adding_navigation_throws_when_grandparent_type_has_navigation_with_s Assert.Throws( () => specialCustomerForeignKey.SetPrincipalToDependent("Orders")).Message); - Assert.Equal("Orders", ((IEntityType)verySpecialCustomerType).GetNavigations().Single().Name); + Assert.Equal("Orders", ((IReadOnlyEntityType)verySpecialCustomerType).GetNavigations().Single().Name); } [ConditionalFact] @@ -710,7 +718,7 @@ public void Adding_navigation_throws_when_grandchild_type_has_navigation_with_sa () => customerForeignKey.SetPrincipalToDependent(Customer.OrdersProperty)).Message); - Assert.Equal(nameof(Customer.Orders), ((IEntityType)verySpecialCustomerType).GetNavigations().Single().Name); + Assert.Equal(nameof(Customer.Orders), ((IReadOnlyEntityType)verySpecialCustomerType).GetNavigations().Single().Name); } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs index ba11b6ca235..cff0cacc81e 100644 --- a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs @@ -32,65 +32,65 @@ public void Use_of_custom_IEntityType_throws() var type = new FakeEntityType(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IEntityType_throws), nameof(IEntityType), nameof(FakeEntityType)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IEntityType_throws), nameof(IReadOnlyEntityType), nameof(FakeEntityType)), Assert.Throws(() => type.AsEntityType()).Message); } - private class FakeEntityType : Annotatable, IEntityType + private class FakeEntityType : Annotatable, IReadOnlyEntityType { - public IModel Model { get; } + public IReadOnlyModel Model { get; } public string Name { get; } public bool HasSharedClrType { get; } public bool IsPropertyBag { get; } public Type ClrType { get; } - public IEntityType BaseType { get; } + public IReadOnlyEntityType BaseType { get; } public string DefiningNavigationName { get; } - public IEntityType DefiningEntityType { get; } + public IReadOnlyEntityType DefiningEntityType { get; } public LambdaExpression QueryFilter { get; } - public IKey FindPrimaryKey() + public IReadOnlyKey FindPrimaryKey() => throw new NotImplementedException(); - public IKey FindKey(IReadOnlyList properties) + public IReadOnlyKey FindKey(IReadOnlyList properties) => throw new NotImplementedException(); - public IEnumerable GetKeys() + public IEnumerable GetKeys() => throw new NotImplementedException(); - public IForeignKey FindForeignKey(IReadOnlyList properties, IKey principalKey, IEntityType principalEntityType) + public IReadOnlyForeignKey FindForeignKey(IReadOnlyList properties, IReadOnlyKey principalKey, IReadOnlyEntityType principalEntityType) => throw new NotImplementedException(); - public IEnumerable GetForeignKeys() + public IEnumerable GetForeignKeys() => throw new NotImplementedException(); - public IIndex FindIndex(IReadOnlyList properties) + public IReadOnlyIndex FindIndex(IReadOnlyList properties) => throw new NotImplementedException(); - public IIndex FindIndex(string name) + public IReadOnlyIndex FindIndex(string name) => throw new NotImplementedException(); - public IEnumerable GetIndexes() + public IEnumerable GetIndexes() => throw new NotImplementedException(); - public IProperty FindProperty(string name) + public IReadOnlyProperty FindProperty(string name) => throw new NotImplementedException(); - public IEnumerable GetProperties() + public IEnumerable GetProperties() => throw new NotImplementedException(); - public IServiceProperty FindServiceProperty(string name) + public IReadOnlyServiceProperty FindServiceProperty(string name) => throw new NotImplementedException(); - public IEnumerable GetServiceProperties() + public IEnumerable GetServiceProperties() => throw new NotImplementedException(); public IEnumerable> GetSeedData() => throw new NotImplementedException(); - public ISkipNavigation FindSkipNavigation([NotNull] string name) + public IReadOnlySkipNavigation FindSkipNavigation([NotNull] string name) => throw new NotImplementedException(); - public IEnumerable GetSkipNavigations() + public IEnumerable GetSkipNavigations() => throw new NotImplementedException(); } @@ -1013,12 +1013,12 @@ public void Can_add_and_remove_navigations() Assert.Same(customerNavigation, customerForeignKey.SetDependentToPrincipal((string)null)); Assert.Null(customerForeignKey.SetDependentToPrincipal((string)null)); Assert.Empty(orderType.GetNavigations()); - Assert.Empty(((IEntityType)orderType).GetNavigations()); + Assert.Empty(((IReadOnlyEntityType)orderType).GetNavigations()); Assert.Same(ordersNavigation, customerForeignKey.SetPrincipalToDependent((string)null)); Assert.Null(customerForeignKey.SetPrincipalToDependent((string)null)); Assert.Empty(customerType.GetNavigations()); - Assert.Empty(((IEntityType)customerType).GetNavigations()); + Assert.Empty(((IReadOnlyEntityType)customerType).GetNavigations()); } [ConditionalFact] @@ -1304,7 +1304,7 @@ public void Navigations_are_ordered_by_name() var navigation1 = specialCustomerForeignKey.SetPrincipalToDependent(SpecialCustomer.DerivedOrdersProperty); Assert.True(new[] { navigation1, navigation2 }.SequenceEqual(customerType.GetNavigations())); - Assert.True(new[] { navigation1, navigation2 }.SequenceEqual(((IEntityType)customerType).GetNavigations())); + Assert.True(new[] { navigation1, navigation2 }.SequenceEqual(((IReadOnlyEntityType)customerType).GetNavigations())); } [ConditionalFact] @@ -1742,7 +1742,7 @@ public void Can_add_and_remove_properties() Assert.False(property1.IsShadowProperty()); Assert.Equal("Id", property1.Name); Assert.Same(typeof(int), property1.ClrType); - Assert.False(((IProperty)property1).IsConcurrencyToken); + Assert.False(((IReadOnlyProperty)property1).IsConcurrencyToken); Assert.Same(entityType, property1.DeclaringEntityType); var property2 = entityType.AddProperty("Name", typeof(string)); @@ -2237,7 +2237,7 @@ public void FindProperty_return_null_when_passed_indexer_property_info() Assert.Same(property, entityType.FindProperty("Nation")); - Assert.Null(((IEntityType)entityType).FindProperty(indexerPropertyInfo)); + Assert.Null(((IReadOnlyEntityType)entityType).FindProperty(indexerPropertyInfo)); Assert.Null(entityType.FindProperty(indexerPropertyInfo)); Assert.Null(((IConventionEntityType)entityType).FindProperty(indexerPropertyInfo)); } @@ -2277,14 +2277,15 @@ public void AddIndexerProperty_throws_when_entitytype_have_property_with_same_na [ConditionalFact] public void Can_get_property_indexes() { - var model = CreateModel(); - var entityType = model.AddEntityType(typeof(Customer)); - - entityType.AddProperty(Customer.NameProperty); - entityType.AddProperty("Id_", typeof(int)); - entityType.AddProperty("Mane_", typeof(int)); + var modelBuilder = new ModelBuilder(); + modelBuilder.Entity(eb => + { + eb.Property(c => c.Name); + eb.Property("Id_"); + eb.Property("Mane_"); + }); - ((Model)entityType.Model).FinalizeModel(); + var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(Customer)); Assert.Equal(0, entityType.FindProperty("Id_").GetIndex()); Assert.Equal(1, entityType.FindProperty("Mane_").GetIndex()); @@ -2914,9 +2915,10 @@ List GetTypeNames() [ConditionalFact] public void All_properties_have_original_value_indexes_when_using_snapshot_change_tracking() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.Snapshot); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.Snapshot); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetOriginalValueIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetOriginalValueIndex()); @@ -2931,9 +2933,10 @@ public void All_properties_have_original_value_indexes_when_using_snapshot_chang [ConditionalFact] public void All_relationship_properties_have_relationship_indexes_when_using_snapshot_change_tracking() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.Snapshot); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.Snapshot); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetRelationshipIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetRelationshipIndex()); @@ -2950,9 +2953,10 @@ public void All_relationship_properties_have_relationship_indexes_when_using_sna [ConditionalFact] public void All_properties_have_original_value_indexes_when_using_changed_only_tracking() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetOriginalValueIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetOriginalValueIndex()); @@ -2967,9 +2971,10 @@ public void All_properties_have_original_value_indexes_when_using_changed_only_t [ConditionalFact] public void Collections_dont_have_relationship_indexes_when_using_changed_only_change_tracking() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangedNotifications); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetRelationshipIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetRelationshipIndex()); @@ -2986,9 +2991,10 @@ public void Collections_dont_have_relationship_indexes_when_using_changed_only_c [ConditionalFact] public void Only_concurrency_index_and_key_properties_have_original_value_indexes_when_using_full_notifications() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetOriginalValueIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetOriginalValueIndex()); @@ -3003,9 +3009,10 @@ public void Only_concurrency_index_and_key_properties_have_original_value_indexe [ConditionalFact] public void Collections_dont_have_relationship_indexes_when_using_full_notifications() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetRelationshipIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetRelationshipIndex()); @@ -3022,9 +3029,10 @@ public void Collections_dont_have_relationship_indexes_when_using_full_notificat [ConditionalFact] public void All_properties_have_original_value_indexes_when_full_notifications_with_original_values() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetOriginalValueIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetOriginalValueIndex()); @@ -3039,9 +3047,10 @@ public void All_properties_have_original_value_indexes_when_full_notifications_w [ConditionalFact] public void Collections_dont_have_relationship_indexes_when_full_notifications_with_original_values() { - var entityType = BuildFullNotificationEntityModel().FindEntityType(typeof(FullNotificationEntity)); - entityType.SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); - ((Model)entityType.Model).FinalizeModel(); + var model = BuildFullNotificationEntityModel(); + model.FindEntityType(typeof(FullNotificationEntity)) + .SetChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues); + var entityType = model.FinalizeModel().FindEntityType(typeof(FullNotificationEntity)); Assert.Equal(0, entityType.FindProperty("Id").GetRelationshipIndex()); Assert.Equal(1, entityType.FindProperty("AnotherEntityId").GetRelationshipIndex()); diff --git a/test/EFCore.Tests/Metadata/Internal/ForeignKeyTest.cs b/test/EFCore.Tests/Metadata/Internal/ForeignKeyTest.cs index a9de1ad1eee..5a88da7e8af 100644 --- a/test/EFCore.Tests/Metadata/Internal/ForeignKeyTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ForeignKeyTest.cs @@ -19,18 +19,18 @@ public void Use_of_custom_IForeignKey_throws() var foreignKey = new FakeForeignKey(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IForeignKey_throws), nameof(IForeignKey), nameof(FakeForeignKey)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IForeignKey_throws), nameof(IReadOnlyForeignKey), nameof(FakeForeignKey)), Assert.Throws(() => foreignKey.AsForeignKey()).Message); } - public class FakeForeignKey : Annotatable, IForeignKey + public class FakeForeignKey : Annotatable, IReadOnlyForeignKey { - public IEntityType DeclaringEntityType { get; } - public IReadOnlyList Properties { get; } - public IEntityType PrincipalEntityType { get; } - public IKey PrincipalKey { get; } - public INavigation DependentToPrincipal { get; set; } - public INavigation PrincipalToDependent { get; set; } + public IReadOnlyEntityType DeclaringEntityType { get; } + public IReadOnlyList Properties { get; } + public IReadOnlyEntityType PrincipalEntityType { get; } + public IReadOnlyKey PrincipalKey { get; } + public IReadOnlyNavigation DependentToPrincipal { get; set; } + public IReadOnlyNavigation PrincipalToDependent { get; set; } public bool IsUnique { get; } public bool IsRequired { get; } public bool IsRequiredDependent { get; } diff --git a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs index 4e8aec2ba84..9cae95383ff 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalEntityTypeBuilderTest.cs @@ -982,7 +982,7 @@ public void Removing_index_does_not_remove_contained_shadow_properties_if_refere Test_removing_index_does_not_remove_contained_shadow_properties_if_referenced_elsewhere( (entityBuilder, property) => entityBuilder.Property( - property.ClrType, ((IProperty)property).Name, ConfigurationSource.Explicit)); + property.ClrType, ((IReadOnlyProperty)property).Name, ConfigurationSource.Explicit)); } private void Test_removing_index_does_not_remove_contained_shadow_properties_if_referenced_elsewhere( @@ -1048,7 +1048,7 @@ public void Key_sets_properties_to_required() Assert.NotNull( entityBuilder.PrimaryKey(new[] { Order.IdProperty, Order.CustomerUniqueProperty }, ConfigurationSource.DataAnnotation)); - Assert.False(entityBuilder.Metadata.FindProperty(Order.CustomerUniqueProperty).IsNullable); + Assert.False(((IReadOnlyEntityType)entityBuilder.Metadata).FindProperty(Order.CustomerUniqueProperty).IsNullable); Assert.Null( entityBuilder.Property(Order.CustomerUniqueProperty, ConfigurationSource.Convention) @@ -1057,7 +1057,7 @@ public void Key_sets_properties_to_required() entityBuilder.Property(Order.CustomerUniqueProperty, ConfigurationSource.Convention) .IsRequired(false, ConfigurationSource.DataAnnotation)); - Assert.True(entityBuilder.Metadata.FindProperty(Order.CustomerUniqueProperty).IsNullable); + Assert.True(((IReadOnlyEntityType)entityBuilder.Metadata).FindProperty(Order.CustomerUniqueProperty).IsNullable); Assert.Null(entityBuilder.Metadata.FindPrimaryKey()); } diff --git a/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs index 4d42d89efd0..847e28b5491 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalForeignKeyBuilderTest.cs @@ -202,13 +202,13 @@ public void ForeignKey_can_be_set_independently_from_requiredness() var nullableId = orderEntityBuilder.Property(typeof(int?), "NullableId", ConfigurationSource.Explicit); relationshipBuilder = relationshipBuilder.HasForeignKey(new[] { nullableId.Metadata.Name }, ConfigurationSource.Convention); - Assert.False(((IForeignKey)relationshipBuilder.Metadata).IsRequired); + Assert.False(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsRequired); Assert.Equal( new[] { nullableId.Metadata.Name }, relationshipBuilder.Metadata.Properties.Select(p => p.Name)); relationshipBuilder = relationshipBuilder.HasForeignKey(new[] { Order.CustomerIdProperty }, ConfigurationSource.Convention); - Assert.False(((IForeignKey)relationshipBuilder.Metadata).IsRequired); + Assert.False(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsRequired); Assert.Equal( new[] { Order.CustomerIdProperty.Name }, relationshipBuilder.Metadata.Properties.Select(p => p.Name)); @@ -410,18 +410,18 @@ public void Can_only_override_existing_Unique_value_explicitly() relationshipBuilder = relationshipBuilder .HasPrincipalKey(customerKeyBuilder.Metadata.Properties, ConfigurationSource.Convention); Assert.Same(foreignKey, relationshipBuilder.Metadata); - Assert.True(((IForeignKey)relationshipBuilder.Metadata).IsUnique); + Assert.True(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsUnique); Assert.Null(relationshipBuilder.IsUnique(false, ConfigurationSource.Convention)); - Assert.True(((IForeignKey)relationshipBuilder.Metadata).IsUnique); + Assert.True(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsUnique); relationshipBuilder = relationshipBuilder.IsUnique(true, ConfigurationSource.Convention); Assert.NotNull(relationshipBuilder); - Assert.True(((IForeignKey)relationshipBuilder.Metadata).IsUnique); + Assert.True(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsUnique); relationshipBuilder = relationshipBuilder.IsUnique(false, ConfigurationSource.Explicit); Assert.NotNull(relationshipBuilder); - Assert.False(((IForeignKey)relationshipBuilder.Metadata).IsUnique); + Assert.False(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsUnique); } [ConditionalFact] @@ -489,12 +489,12 @@ public void Can_set_Required_independently_from_nullability() var relationshipBuilder = orderEntityBuilder.HasRelationship( customerEntityBuilder.Metadata, fk.Properties, ConfigurationSource.Explicit); relationshipBuilder = relationshipBuilder.IsRequired(false, ConfigurationSource.Convention); - Assert.False(((IForeignKey)relationshipBuilder.Metadata).IsRequired); + Assert.False(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsRequired); Assert.False(customerIdProperty.IsNullable); Assert.True(customerUniqueProperty.IsNullable); relationshipBuilder = relationshipBuilder.IsRequired(true, ConfigurationSource.Convention); - Assert.True(((IForeignKey)relationshipBuilder.Metadata).IsRequired); + Assert.True(((IReadOnlyForeignKey)relationshipBuilder.Metadata).IsRequired); Assert.False(customerIdProperty.IsNullable); Assert.True(customerUniqueProperty.IsNullable); diff --git a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs index 9767372f231..58982179849 100644 --- a/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/InternalPropertyBuilderTest.cs @@ -208,7 +208,7 @@ public void Can_only_override_lower_or_equal_source_CustomValueGenerator_factory [ConditionalFact] public void Can_only_override_existing_CustomValueGenerator_factory_explicitly() { - ValueGenerator factory(IProperty p, IEntityType e) + ValueGenerator factory(IReadOnlyProperty p, IReadOnlyEntityType e) => new CustomValueGenerator1(); var metadata = CreateProperty(); @@ -238,13 +238,13 @@ public void Can_clear_CustomValueGenerator_factory() Assert.Equal(ValueGenerated.Never, metadata.ValueGenerated); Assert.True(metadata.RequiresValueGenerator()); - Assert.Null(builder.HasValueGenerator((Func)null, ConfigurationSource.Convention)); + Assert.Null(builder.HasValueGenerator((Func)null, ConfigurationSource.Convention)); Assert.IsType(metadata.GetValueGeneratorFactory()(null, null)); Assert.Equal(ValueGenerated.Never, metadata.ValueGenerated); Assert.True(metadata.RequiresValueGenerator()); - Assert.NotNull(builder.HasValueGenerator((Func)null, ConfigurationSource.Explicit)); + Assert.NotNull(builder.HasValueGenerator((Func)null, ConfigurationSource.Explicit)); Assert.Null(metadata.GetValueGeneratorFactory()); Assert.Equal(ValueGenerated.Never, metadata.ValueGenerated); diff --git a/test/EFCore.Tests/Metadata/Internal/KeyTest.cs b/test/EFCore.Tests/Metadata/Internal/KeyTest.cs index 435f4b27591..4223a61353c 100644 --- a/test/EFCore.Tests/Metadata/Internal/KeyTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/KeyTest.cs @@ -19,14 +19,14 @@ public void Use_of_custom_IKey_throws() var key = new FakeKey(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IKey_throws), nameof(IKey), nameof(FakeKey)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IKey_throws), nameof(IReadOnlyKey), nameof(FakeKey)), Assert.Throws(() => key.AsKey()).Message); } - private class FakeKey : Annotatable, IKey + private class FakeKey : Annotatable, IReadOnlyKey { - public IReadOnlyList Properties { get; } - public IEntityType DeclaringEntityType { get; } + public IReadOnlyList Properties { get; } + public IReadOnlyEntityType DeclaringEntityType { get; } } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/Internal/ModelTest.cs b/test/EFCore.Tests/Metadata/Internal/ModelTest.cs index 5c60d28c21f..efeeb2f6f4c 100644 --- a/test/EFCore.Tests/Metadata/Internal/ModelTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/ModelTest.cs @@ -21,19 +21,19 @@ public void Use_of_custom_IModel_throws() var model = new FakeModel(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IModel_throws), nameof(IModel), nameof(FakeModel)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IModel_throws), nameof(IReadOnlyModel), nameof(FakeModel)), Assert.Throws(() => model.AsModel()).Message); } - private class FakeModel : Annotatable, IModel + private class FakeModel : Annotatable, IReadOnlyModel { - public IEnumerable GetEntityTypes() + public IEnumerable GetEntityTypes() => throw new NotImplementedException(); - public IEntityType FindEntityType(string name) + public IReadOnlyEntityType FindEntityType(string name) => throw new NotImplementedException(); - public IEntityType FindEntityType(string name, string definingNavigationName, IEntityType definingEntityType) + public IReadOnlyEntityType FindEntityType(string name, string definingNavigationName, IReadOnlyEntityType definingEntityType) => throw new NotImplementedException(); } diff --git a/test/EFCore.Tests/Metadata/Internal/NavigationTest.cs b/test/EFCore.Tests/Metadata/Internal/NavigationTest.cs index ea21040f4ec..d466e20f10b 100644 --- a/test/EFCore.Tests/Metadata/Internal/NavigationTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/NavigationTest.cs @@ -18,19 +18,19 @@ public void Use_of_custom_INavigation_throws() var navigation = new FakeNavigation(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_INavigation_throws), nameof(INavigation), nameof(FakeNavigation)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_INavigation_throws), nameof(IReadOnlyNavigation), nameof(FakeNavigation)), Assert.Throws(() => navigation.AsNavigation()).Message); } - private class FakeNavigation : Annotatable, INavigation + private class FakeNavigation : Annotatable, IReadOnlyNavigation { public string Name { get; } - public ITypeBase DeclaringType { get; } + public IReadOnlyTypeBase DeclaringType { get; } public Type ClrType { get; } public PropertyInfo PropertyInfo { get; } public FieldInfo FieldInfo { get; } - public IEntityType DeclaringEntityType { get; } - public IForeignKey ForeignKey { get; } + public IReadOnlyEntityType DeclaringEntityType { get; } + public IReadOnlyForeignKey ForeignKey { get; } public bool IsEagerLoaded { get; } } diff --git a/test/EFCore.Tests/Metadata/Internal/PropertyAccessorsFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/PropertyAccessorsFactoryTest.cs index 05dd1f6be47..61dc963edf4 100644 --- a/test/EFCore.Tests/Metadata/Internal/PropertyAccessorsFactoryTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/PropertyAccessorsFactoryTest.cs @@ -29,9 +29,9 @@ public void Can_use_PropertyAccessorsFactory_on_indexed_property() var factory = contextServices.GetRequiredService(); var entity = new IndexedClass(); - var entry = factory.Create(stateManager, entityTypeBuilder.Metadata, entity); + var entry = factory.Create(stateManager, (IEntityType)entityTypeBuilder.Metadata, entity); - var propertyAccessors = new PropertyAccessorsFactory().Create(propertyA); + var propertyAccessors = new PropertyAccessorsFactory().Create((IProperty)propertyA); Assert.Equal("ValueA", ((Func)propertyAccessors.CurrentValueGetter)(entry)); Assert.Equal("ValueA", ((Func)propertyAccessors.OriginalValueGetter)(entry)); Assert.Equal("ValueA", ((Func)propertyAccessors.PreStoreGeneratedCurrentValueGetter)(entry)); @@ -56,9 +56,9 @@ public void Can_use_PropertyAccessorsFactory_on_non_indexed_property() var factory = contextServices.GetRequiredService(); var entity = new NonIndexedClass(); - var entry = factory.Create(stateManager, entityTypeBuilder.Metadata, entity); + var entry = factory.Create(stateManager, (IEntityType)entityTypeBuilder.Metadata, entity); - var propertyAccessors = new PropertyAccessorsFactory().Create(propA); + var propertyAccessors = new PropertyAccessorsFactory().Create((IProperty)propA); Assert.Equal("ValueA", ((Func)propertyAccessors.CurrentValueGetter)(entry)); Assert.Equal("ValueA", ((Func)propertyAccessors.OriginalValueGetter)(entry)); Assert.Equal("ValueA", ((Func)propertyAccessors.PreStoreGeneratedCurrentValueGetter)(entry)); diff --git a/test/EFCore.Tests/Metadata/Internal/PropertyBaseTest.cs b/test/EFCore.Tests/Metadata/Internal/PropertyBaseTest.cs index ad604831923..3d55d780ffc 100644 --- a/test/EFCore.Tests/Metadata/Internal/PropertyBaseTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/PropertyBaseTest.cs @@ -790,7 +790,7 @@ private void MemberInfoTest( { property.SetPropertyAccessMode(accessMode); - MemberInfoTestCommon(property, accessMode, forConstruction, forSet, forGet); + MemberInfoTestCommon((IPropertyBase)property, accessMode, forConstruction, forSet, forGet); } private void MemberInfoTest( @@ -802,7 +802,7 @@ private void MemberInfoTest( { navigation.SetPropertyAccessMode(accessMode); - MemberInfoTestCommon(navigation, accessMode, forConstruction, forSet, forGet); + MemberInfoTestCommon((IPropertyBase)navigation, accessMode, forConstruction, forSet, forGet); } private void MemberInfoTestCommon( diff --git a/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs b/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs index 8d4cf132c13..dd9a2e52d08 100644 --- a/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/PropertyTest.cs @@ -23,7 +23,7 @@ public void Use_of_custom_IProperty_throws() var property = new FakeProperty(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IProperty_throws), nameof(IProperty), nameof(FakeProperty)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IProperty_throws), nameof(IReadOnlyProperty), nameof(FakeProperty)), Assert.Throws(() => property.AsProperty()).Message); } @@ -33,16 +33,16 @@ public void Use_of_custom_IPropertyBase_throws() var property = new FakeProperty(); Assert.Equal( - CoreStrings.CustomMetadata(nameof(Use_of_custom_IPropertyBase_throws), nameof(IPropertyBase), nameof(FakeProperty)), + CoreStrings.CustomMetadata(nameof(Use_of_custom_IPropertyBase_throws), nameof(IReadOnlyPropertyBase), nameof(FakeProperty)), Assert.Throws(() => property.AsPropertyBase()).Message); } - private class FakeProperty : Annotatable, IProperty + private class FakeProperty : Annotatable, IReadOnlyProperty { public string Name { get; } - public ITypeBase DeclaringType { get; } + public IReadOnlyTypeBase DeclaringType { get; } public Type ClrType { get; } - public IEntityType DeclaringEntityType { get; } + public IReadOnlyEntityType DeclaringEntityType { get; } public bool IsNullable { get; } public bool IsStoreGeneratedAlways { get; } public ValueGenerated ValueGenerated { get; } diff --git a/test/EFCore.Tests/Metadata/Internal/SkipNavigationTest.cs b/test/EFCore.Tests/Metadata/Internal/SkipNavigationTest.cs index aecfa4d2425..4f78720ce3d 100644 --- a/test/EFCore.Tests/Metadata/Internal/SkipNavigationTest.cs +++ b/test/EFCore.Tests/Metadata/Internal/SkipNavigationTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Microsoft.EntityFrameworkCore.Diagnostics; using Xunit; @@ -83,6 +84,13 @@ public void Gets_expected_default_values() Assert.Equal(ConfigurationSource.Convention, navigation.GetForeignKeyConfigurationSource()); Assert.Null(navigation.GetInverseConfigurationSource()); Assert.Equal(ConfigurationSource.Convention, navigation.GetConfigurationSource()); + Assert.Equal(PropertyAccessMode.PreferField, navigation.GetPropertyAccessMode()); + Assert.Null(navigation.GetPropertyAccessModeConfigurationSource()); + + Assert.Same(navigation, firstEntity.FindDeclaredSkipNavigation(navigation.Name)); + Assert.Same(navigation, firstEntity.FindSkipNavigation(navigation.Name)); + Assert.Same(navigation, firstEntity.FindSkipNavigation(navigation.GetIdentifyingMemberInfo())); + Assert.Same(navigation, firstEntity.GetDeclaredSkipNavigations().Single()); } [ConditionalFact] diff --git a/test/EFCore.Tests/Metadata/NavigationExtensionsTest.cs b/test/EFCore.Tests/Metadata/NavigationExtensionsTest.cs index fad8d497a79..e5e39ee97b7 100644 --- a/test/EFCore.Tests/Metadata/NavigationExtensionsTest.cs +++ b/test/EFCore.Tests/Metadata/NavigationExtensionsTest.cs @@ -102,7 +102,7 @@ private class Product public Category Category { get; set; } } - private static IModel BuildModel( + private static IReadOnlyModel BuildModel( bool createProducts = true, bool createCategory = true, bool createFeaturedProductCategory = true, diff --git a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs index 5ef286c71d0..ad5c2c68431 100644 --- a/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ManyToManyTestBase.cs @@ -54,7 +54,7 @@ public virtual void Discovers_navigations() public virtual void Finds_existing_navigations_and_uses_associated_FK() { var modelBuilder = CreateModelBuilder(); - var model = (IModel)modelBuilder.Model; + var model = (IReadOnlyModel)modelBuilder.Model; modelBuilder.Entity().Ignore(c => c.Products); modelBuilder.Entity().Ignore(p => p.Categories); @@ -99,7 +99,7 @@ public virtual void Finds_existing_navigations_and_uses_associated_FK() public virtual void Finds_existing_navigations_and_uses_associated_FK_with_fields() { var modelBuilder = CreateModelBuilder(); - var model = (IModel)modelBuilder.Model; + var model = (IReadOnlyModel)modelBuilder.Model; modelBuilder.Entity() .HasMany(p => p.Dependents) diff --git a/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs b/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs index ba4dc295a61..a3c79c5eaf0 100644 --- a/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs @@ -595,7 +595,7 @@ public virtual void Creates_both_navigations_and_creates_shadow_FK() modelBuilder.FinalizeModel(); var fk = dependentType.GetForeignKeys().Single(); - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.Equal("BigMakId", fkProperty.Name); Assert.True(fkProperty.IsShadowProperty()); @@ -632,7 +632,7 @@ public virtual void Creates_shadow_FK_with_navigation_to_principal() modelBuilder.FinalizeModel(); var fk = dependentType.GetNavigations().Single().ForeignKey; - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.True(fkProperty.IsShadowProperty()); Assert.Same(typeof(int?), fkProperty.ClrType); @@ -666,7 +666,7 @@ public virtual void Creates_shadow_FK_with_navigation_to_dependent() modelBuilder.FinalizeModel(); var fk = principalType.GetNavigations().Single().ForeignKey; - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.True(fkProperty.IsShadowProperty()); Assert.Same(typeof(int?), fkProperty.ClrType); @@ -702,7 +702,7 @@ public virtual void Creates_shadow_FK_with_no_navigations_with() modelBuilder.FinalizeModel(); var newFk = dependentType.GetForeignKeys().Single(foreignKey => foreignKey != fk); - var fkProperty = (IProperty)newFk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)newFk.Properties.Single(); Assert.True(fkProperty.IsShadowProperty()); Assert.Same(typeof(int?), fkProperty.ClrType); @@ -773,7 +773,7 @@ public virtual void Creates_both_navigations_and_overrides_existing_FK_if_unique modelBuilder.FinalizeModel(); Assert.Single(dependentType.GetForeignKeys()); - var fk = (IForeignKey)dependentType.GetForeignKeys().Single(); + var fk = (IReadOnlyForeignKey)dependentType.GetForeignKeys().Single(); Assert.False(fk.IsUnique); Assert.Equal(nameof(Pickle.BigMak), dependentType.GetNavigations().Single().Name); @@ -1719,7 +1719,7 @@ public virtual void Nullable_FK_are_optional_by_default() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Nob.HobId1)); @@ -1742,7 +1742,7 @@ public virtual void Non_nullable_FK_are_required_by_default() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.True(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -1766,7 +1766,7 @@ public virtual void Nullable_FK_can_be_made_required() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); var fk = entityType.GetForeignKeys().Single(); Assert.True(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Nob.HobId1)); @@ -1790,7 +1790,7 @@ public virtual void Non_nullable_FK_can_be_made_optional() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -1817,7 +1817,7 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -1832,7 +1832,7 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() public virtual void Can_change_delete_behavior() { var modelBuilder = HobNobBuilder(); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); modelBuilder .Entity().HasOne(e => e.Hob).WithMany(e => e.Nobs) @@ -1980,8 +1980,8 @@ public virtual void Navigation_properties_can_set_access_mode() .Navigation(e => e.OneToManyPrincipal) .UsePropertyAccessMode(PropertyAccessMode.Property); - var principal = (IEntityType)model.FindEntityType(typeof(OneToManyNavPrincipal)); - var dependent = (IEntityType)model.FindEntityType(typeof(NavDependent)); + var principal = (IReadOnlyEntityType)model.FindEntityType(typeof(OneToManyNavPrincipal)); + var dependent = (IReadOnlyEntityType)model.FindEntityType(typeof(NavDependent)); Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("Dependents").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToManyPrincipal").GetPropertyAccessMode()); diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs index 3291da3853a..307abd03289 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs @@ -473,7 +473,7 @@ public override TestPropertyBuilder HasValueGenerator() public override TestPropertyBuilder HasValueGenerator(Type valueGeneratorType) => new GenericTestPropertyBuilder(PropertyBuilder.HasValueGenerator(valueGeneratorType)); - public override TestPropertyBuilder HasValueGenerator(Func factory) + public override TestPropertyBuilder HasValueGenerator(Func factory) => new GenericTestPropertyBuilder(PropertyBuilder.HasValueGenerator(factory)); public override TestPropertyBuilder HasField(string fieldName) diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs index fd4e1171dd3..1e9b4d4257c 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs @@ -552,7 +552,7 @@ public override TestPropertyBuilder HasValueGenerator() public override TestPropertyBuilder HasValueGenerator(Type valueGeneratorType) => new NonGenericTestPropertyBuilder(PropertyBuilder.HasValueGenerator(valueGeneratorType)); - public override TestPropertyBuilder HasValueGenerator(Func factory) + public override TestPropertyBuilder HasValueGenerator(Func factory) => new NonGenericTestPropertyBuilder(PropertyBuilder.HasValueGenerator(factory)); public override TestPropertyBuilder HasField(string fieldName) diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs index 81316aa32f9..7c919278fdd 100644 --- a/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs @@ -35,62 +35,62 @@ protected void AssertEqual( } protected void AssertEqual( - IEnumerable expectedProperties, - IEnumerable actualProperties, + IEnumerable expectedProperties, + IEnumerable actualProperties, PropertyComparer propertyComparer = null) { propertyComparer ??= new PropertyComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedProperties, propertyComparer), - new SortedSet(actualProperties, propertyComparer), + new SortedSet(expectedProperties, propertyComparer), + new SortedSet(actualProperties, propertyComparer), propertyComparer); } protected void AssertEqual( - IEnumerable expectedNavigations, - IEnumerable actualNavigations, + IEnumerable expectedNavigations, + IEnumerable actualNavigations, NavigationComparer navigationComparer = null) { navigationComparer ??= new NavigationComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedNavigations, navigationComparer), - new SortedSet(actualNavigations, navigationComparer), + new SortedSet(expectedNavigations, navigationComparer), + new SortedSet(actualNavigations, navigationComparer), navigationComparer); } protected void AssertEqual( - IEnumerable expectedKeys, - IEnumerable actualKeys, + IEnumerable expectedKeys, + IEnumerable actualKeys, TestKeyComparer testKeyComparer = null) { testKeyComparer ??= new TestKeyComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedKeys, testKeyComparer), - new SortedSet(actualKeys, testKeyComparer), + new SortedSet(expectedKeys, testKeyComparer), + new SortedSet(actualKeys, testKeyComparer), testKeyComparer); } protected void AssertEqual( - IEnumerable expectedForeignKeys, - IEnumerable actualForeignKeys, + IEnumerable expectedForeignKeys, + IEnumerable actualForeignKeys, ForeignKeyStrictComparer foreignKeyComparer = null) { foreignKeyComparer ??= new ForeignKeyStrictComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedForeignKeys, foreignKeyComparer), - new SortedSet(actualForeignKeys, foreignKeyComparer), + new SortedSet(expectedForeignKeys, foreignKeyComparer), + new SortedSet(actualForeignKeys, foreignKeyComparer), foreignKeyComparer); } protected void AssertEqual( - IEnumerable expectedIndexes, - IEnumerable actualIndexes, + IEnumerable expectedIndexes, + IEnumerable actualIndexes, TestIndexComparer testIndexComparer = null) { testIndexComparer ??= new TestIndexComparer(compareAnnotations: false); Assert.Equal( - new SortedSet(expectedIndexes, testIndexComparer), - new SortedSet(actualIndexes, testIndexComparer), + new SortedSet(expectedIndexes, testIndexComparer), + new SortedSet(actualIndexes, testIndexComparer), testIndexComparer); } @@ -395,7 +395,7 @@ public abstract TestPropertyBuilder HasValueGenerator() where TGenerator : ValueGenerator; public abstract TestPropertyBuilder HasValueGenerator(Type valueGeneratorType); - public abstract TestPropertyBuilder HasValueGenerator(Func factory); + public abstract TestPropertyBuilder HasValueGenerator(Func factory); public abstract TestPropertyBuilder HasField(string fieldName); public abstract TestPropertyBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode); diff --git a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs index 74212113ec8..8135cedb9d0 100644 --- a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs @@ -222,7 +222,7 @@ public virtual void Can_upgrade_candidate_key_to_primary_key() Assert.False(nameProperty.RequiresValueGenerator()); Assert.Equal(ValueGenerated.Never, nameProperty.ValueGenerated); - var idProperty = (IProperty)entity.FindProperty(Customer.IdProperty); + var idProperty = (IReadOnlyProperty)entity.FindProperty(Customer.IdProperty); Assert.Equal(ValueGenerated.Never, idProperty.ValueGenerated); } @@ -347,7 +347,7 @@ public virtual void Properties_are_required_by_default_only_if_CLR_type_is_nulla b.Property("Bottom"); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.False(entityType.FindProperty("Up").IsNullable); Assert.True(entityType.FindProperty("Down").IsNullable); @@ -362,7 +362,7 @@ public virtual void Properties_can_be_ignored() { var modelBuilder = CreateModelBuilder(); - var entityType = (IEntityType)modelBuilder.Entity().Metadata; + var entityType = (IReadOnlyEntityType)modelBuilder.Entity().Metadata; modelBuilder.Entity( b => @@ -490,7 +490,7 @@ public virtual void Properties_can_be_made_required() b.Property("Bottom").IsRequired(); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.False(entityType.FindProperty("Up").IsNullable); Assert.False(entityType.FindProperty("Down").IsNullable); @@ -514,7 +514,7 @@ public virtual void Properties_can_be_made_optional() b.Property("Bottom").IsRequired(false); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.True(entityType.FindProperty("Down").IsNullable); Assert.True(entityType.FindProperty("Strange").IsNullable); @@ -559,7 +559,7 @@ public virtual void Non_nullable_properties_cannot_be_made_optional() Assert.Throws(() => b.Property("Top").IsRequired(false)).Message); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.False(entityType.FindProperty("Up").IsNullable); Assert.False(entityType.FindProperty("Charm").IsNullable); @@ -581,9 +581,7 @@ public virtual void Properties_specified_by_string_are_shadow_properties_unless_ b.Property("Photon"); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); - - modelBuilder.FinalizeModel(); + var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(Quarks)); Assert.False(entityType.FindProperty("Up").IsShadowProperty()); Assert.False(entityType.FindProperty("Down").IsShadowProperty()); @@ -614,9 +612,7 @@ public virtual void Properties_can_be_made_concurrency_tokens() b.HasChangeTrackingStrategy(ChangeTrackingStrategy.ChangingAndChangedNotifications); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); - - modelBuilder.FinalizeModel(); + var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(Quarks)); Assert.False(entityType.FindProperty(Customer.IdProperty.Name).IsConcurrencyToken); Assert.True(entityType.FindProperty("Up").IsConcurrencyToken); @@ -652,7 +648,7 @@ public virtual void Properties_can_have_access_mode_set() b.Property("Strange").UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Equal(PropertyAccessMode.PreferField, entityType.FindProperty("Up").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Field, entityType.FindProperty("Down").GetPropertyAccessMode()); @@ -679,11 +675,11 @@ public virtual void Access_mode_can_be_overridden_at_entity_and_property_levels( Assert.Equal(PropertyAccessMode.Field, model.GetPropertyAccessMode()); - var hobsType = (IEntityType)model.FindEntityType(typeof(Hob)); + var hobsType = (IReadOnlyEntityType)model.FindEntityType(typeof(Hob)); Assert.Equal(PropertyAccessMode.Field, hobsType.GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Field, hobsType.FindProperty("Id1").GetPropertyAccessMode()); - var quarksType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var quarksType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Equal(PropertyAccessMode.FieldDuringConstruction, quarksType.GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.FieldDuringConstruction, quarksType.FindProperty("Down").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, quarksType.FindProperty("Up").GetPropertyAccessMode()); @@ -705,7 +701,7 @@ public virtual void Properties_can_have_store_type_set() b.Property("Strange").HasConversion((Type)null); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Null(entityType.FindProperty("Up").GetProviderClrType()); Assert.Same(typeof(byte[]), entityType.FindProperty("Down").GetProviderClrType()); @@ -732,7 +728,7 @@ public virtual void Properties_can_have_value_converter_set_non_generic() b.Property("Strange").HasConversion((ValueConverter)null); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Null(entityType.FindProperty("Up").GetValueConverter()); Assert.Same(stringConverter, entityType.FindProperty("Down").GetValueConverter()); @@ -759,7 +755,7 @@ public virtual void Properties_can_have_value_converter_set_generic() b.Property("Strange").HasConversion((ValueConverter)null); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Null(entityType.FindProperty("Up").GetValueConverter()); Assert.Same(stringConverter, entityType.FindProperty("Down").GetValueConverter()); @@ -781,7 +777,7 @@ public virtual void Properties_can_have_value_converter_set_inline() b.Property("Charm").HasConversion(v => (long)v, v => (int)v); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Null(entityType.FindProperty("Up").GetValueConverter()); Assert.NotNull(entityType.FindProperty("Down").GetValueConverter()); @@ -809,7 +805,7 @@ public virtual void IEnumerable_properties_with_value_converter_set_are_not_disc modelBuilder.FinalizeModel(); - var entityType = (IEntityType)model.GetEntityTypes().Single(); + var entityType = (IReadOnlyEntityType)model.GetEntityTypes().Single(); Assert.NotNull(entityType.FindProperty(nameof(DynamicProperty.ExpandoObject)).GetValueConverter()); } @@ -837,7 +833,7 @@ public virtual void Value_converter_type_is_checked() new StringToBytesConverter(Encoding.UTF8))).Message); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Null(entityType.FindProperty("Up").GetValueConverter()); } @@ -855,7 +851,7 @@ public virtual void Properties_can_have_field_set() b.Property("_forWierd").HasField("_forWierd"); }); - var entityType = (IEntityType)model.FindEntityType(typeof(Quarks)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Quarks)); Assert.Equal("_forUp", entityType.FindProperty("Up").GetFieldName()); Assert.Equal("_forDown", entityType.FindProperty("Down").GetFieldName()); @@ -1275,11 +1271,11 @@ public virtual void Can_add_multiple_indexes() var firstIndexBuilder = entityBuilder.HasIndex(ix => ix.Id).IsUnique(); var secondIndexBuilder = entityBuilder.HasIndex(ix => ix.Name).HasAnnotation("A1", "V1"); - var entityType = (IEntityType)model.FindEntityType(typeof(Customer)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Customer)); Assert.Equal(2, entityType.GetIndexes().Count()); Assert.True(firstIndexBuilder.Metadata.IsUnique); - Assert.False(((IIndex)secondIndexBuilder.Metadata).IsUnique); + Assert.False(((IReadOnlyIndex)secondIndexBuilder.Metadata).IsUnique); Assert.Equal("V1", secondIndexBuilder.Metadata["A1"]); } @@ -1295,7 +1291,7 @@ public virtual void Can_add_contained_indexes() var secondIndexBuilder = entityBuilder.HasIndex( ix => new { ix.Id }); - var entityType = (IEntityType)model.FindEntityType(typeof(Customer)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(Customer)); Assert.Equal(2, entityType.GetIndexes().Count()); Assert.True(firstIndexBuilder.Metadata.IsUnique); @@ -1310,7 +1306,7 @@ public virtual void Can_set_primary_key_by_convention_for_user_specified_shadow_ var entityBuilder = modelBuilder.Entity(); - var entityType = (IEntityType)model.FindEntityType(typeof(EntityWithoutId)); + var entityType = (IReadOnlyEntityType)model.FindEntityType(typeof(EntityWithoutId)); Assert.Null(entityType.FindPrimaryKey()); diff --git a/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs b/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs index e13b3d9601b..3ffe9ea09c5 100644 --- a/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs @@ -599,7 +599,7 @@ public virtual void Creates_both_navigations_and_creates_shadow_FK() modelBuilder.FinalizeModel(); var fk = dependentType.GetForeignKeys().Single(); - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.Equal("BigMakId", fkProperty.Name); Assert.True(fkProperty.IsShadowProperty()); @@ -638,7 +638,7 @@ public virtual void Creates_shadow_FK_with_navigation_to_dependent() modelBuilder.FinalizeModel(); var fk = principalType.GetNavigations().Single().ForeignKey; - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.True(fkProperty.IsShadowProperty()); Assert.Same(typeof(int?), fkProperty.ClrType); @@ -673,7 +673,7 @@ public virtual void Creates_shadow_FK_with_navigation_to_principal() modelBuilder.FinalizeModel(); var fk = dependentType.GetNavigations().Single().ForeignKey; - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.True(fkProperty.IsShadowProperty()); Assert.Same(typeof(int?), fkProperty.ClrType); @@ -710,7 +710,7 @@ public virtual void Creates_shadow_FK_with_no_navigation() modelBuilder.FinalizeModel(); var fk = dependentType.GetForeignKeys().Single(foreignKey => foreignKey != existingFk); - var fkProperty = (IProperty)fk.Properties.Single(); + var fkProperty = (IReadOnlyProperty)fk.Properties.Single(); Assert.Equal("BigMakId1", fkProperty.Name); Assert.True(fkProperty.IsShadowProperty()); @@ -779,7 +779,7 @@ public virtual void Creates_both_navigations_and_overrides_existing_FK_when_uniq var fk = dependentType.GetForeignKeys() .Single(foreignKey => foreignKey.Properties.Any(p => p.Name == "BurgerId")); - Assert.True(((IForeignKey)fk).IsUnique); + Assert.True(((IReadOnlyForeignKey)fk).IsUnique); modelBuilder .Entity().HasMany(e => e.Pickles).WithOne(e => e.BigMak) @@ -2082,7 +2082,7 @@ public virtual void Nullable_FK_are_optional_by_default() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Nob.HobId1)); @@ -2105,7 +2105,7 @@ public virtual void Non_nullable_FK_are_required_by_default() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.True(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -2129,7 +2129,7 @@ public virtual void Nullable_FK_can_be_made_required() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); var fk = entityType.GetForeignKeys().Single(); Assert.True(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Nob.HobId1)); @@ -2152,7 +2152,7 @@ public virtual void Non_nullable_FK_can_be_made_optional() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -2178,7 +2178,7 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() modelBuilder.FinalizeModel(); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fk = entityType.GetForeignKeys().Single(); Assert.False(fk.IsRequired); var fkProperty1 = entityType.FindProperty(nameof(Hob.NobId1)); @@ -2193,7 +2193,7 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() public virtual void Can_change_delete_behavior() { var modelBuilder = HobNobBuilder(); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); modelBuilder .Entity().HasMany(e => e.Nobs).WithOne(e => e.Hob) @@ -2621,8 +2621,8 @@ public virtual void Navigation_properties_can_set_access_mode() .Navigation(e => e.Dependents) .UsePropertyAccessMode(PropertyAccessMode.Field); - var principal = (IEntityType)model.FindEntityType(typeof(OneToManyNavPrincipal)); - var dependent = (IEntityType)model.FindEntityType(typeof(NavDependent)); + var principal = (IReadOnlyEntityType)model.FindEntityType(typeof(OneToManyNavPrincipal)); + var dependent = (IReadOnlyEntityType)model.FindEntityType(typeof(NavDependent)); Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("Dependents").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToManyPrincipal").GetPropertyAccessMode()); diff --git a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs index e2d87b5de28..1d868f4a668 100644 --- a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs @@ -695,7 +695,7 @@ public virtual void Creates_both_navigations_and_overrides_existing_FK_when_uniq .HasForeignKey(e => e.BurgerId); modelBuilder.Ignore(); - var dependentType = (IEntityType)model.FindEntityType(typeof(Bun)); + var dependentType = (IReadOnlyEntityType)model.FindEntityType(typeof(Bun)); var principalType = model.FindEntityType(typeof(BigMak)); var fkProperty = dependentType.FindProperty(nameof(Bun.BurgerId)); @@ -2555,7 +2555,7 @@ public virtual void Principal_and_dependent_can_be_flipped_when_self_referencing Assert.Same(existingFk, entityType.GetForeignKeys().Single()); Assert.Equal(navigationToDependent?.Name, existingFk.PrincipalToDependent?.Name); Assert.Equal(navigationToPrincipal.Name, existingFk.DependentToPrincipal.Name); - Assert.True(((IForeignKey)existingFk).IsRequired); + Assert.True(((IReadOnlyForeignKey)existingFk).IsRequired); modelBuilder.Entity().HasOne().WithOne(e => e.SelfRef1); @@ -3232,7 +3232,7 @@ public virtual void Nullable_FK_are_optional_by_default() .HasForeignKey( e => new { e.HobId1, e.HobId2 }); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); Assert.False(entityType.GetForeignKeys().Single().IsRequired); Assert.True( @@ -3250,7 +3250,7 @@ public virtual void Non_nullable_FK_are_required_by_default() .HasForeignKey( e => new { e.NobId1, e.NobId2 }); - var entityType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var entityType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); Assert.False(entityType.FindProperty(nameof(Hob.NobId1)).IsNullable); Assert.False(entityType.FindProperty(nameof(Hob.NobId2)).IsNullable); @@ -3281,8 +3281,8 @@ public virtual void Nullable_FK_can_be_made_required() modelBuilder.FinalizeModel(); - var principalType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var principalType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); var expectedPrincipalProperties = principalType.GetProperties().ToList(); var expectedDependentProperties = dependentType.GetProperties().ToList(); var fkProperty1 = dependentType.FindProperty(nameof(Nob.HobId1)); @@ -3314,7 +3314,7 @@ public virtual void Non_nullable_FK_can_be_made_optional() modelBuilder.FinalizeModel(); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fkProperty1 = dependentType.FindProperty(nameof(Hob.NobId1)); var fkProperty2 = dependentType.FindProperty(nameof(Hob.NobId2)); var fk = dependentType.GetForeignKeys().Single(); @@ -3348,7 +3348,7 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() modelBuilder.FinalizeModel(); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var fkProperty1 = dependentType.FindProperty(nameof(Hob.NobId1)); var fkProperty2 = dependentType.FindProperty(nameof(Hob.NobId2)); var fk = dependentType.GetForeignKeys().Single(); @@ -3364,8 +3364,8 @@ public virtual void Non_nullable_FK_can_be_made_optional_separately() public virtual void Unspecified_FK_can_be_made_optional() { var modelBuilder = HobNobBuilder(); - var principalType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var principalType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var expectedPrincipalProperties = principalType.GetProperties().ToList(); var expectedDependentProperties = dependentType.GetProperties().ToList(); @@ -3386,8 +3386,8 @@ public virtual void Unspecified_FK_can_be_made_optional() public virtual void Unspecified_FK_can_be_made_optional_in_any_order() { var modelBuilder = HobNobBuilder(); - var principalType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var principalType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var expectedPrincipalProperties = principalType.GetProperties().ToList(); var expectedDependentProperties = dependentType.GetProperties().ToList(); @@ -3408,8 +3408,8 @@ public virtual void Unspecified_FK_can_be_made_optional_in_any_order() public virtual void Unspecified_FK_can_be_made_required() { var modelBuilder = HobNobBuilder(); - var principalType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); - var dependentType = (IEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); + var principalType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Nob)); + var dependentType = (IReadOnlyEntityType)modelBuilder.Model.FindEntityType(typeof(Hob)); var expectedPrincipalProperties = principalType.GetProperties().ToList(); var expectedDependentProperties = dependentType.GetProperties().ToList(); @@ -4082,8 +4082,8 @@ public virtual void Navigation_properties_can_set_access_mode() .Navigation(e => e.Dependent) .UsePropertyAccessMode(PropertyAccessMode.Field); - var principal = (IEntityType)model.FindEntityType(typeof(OneToOneNavPrincipal)); - var dependent = (IEntityType)model.FindEntityType(typeof(NavDependent)); + var principal = (IReadOnlyEntityType)model.FindEntityType(typeof(OneToOneNavPrincipal)); + var dependent = (IReadOnlyEntityType)model.FindEntityType(typeof(NavDependent)); Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("Dependent").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToOnePrincipal").GetPropertyAccessMode()); diff --git a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs index bd87e5aa688..d2bbf88bdd6 100644 --- a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs +++ b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs @@ -163,7 +163,7 @@ public virtual void Can_configure_one_to_many_owned_type_with_fields() public virtual void Can_configure_owned_type_inverse() { var modelBuilder = CreateModelBuilder(); - IModel model = modelBuilder.Model; + IReadOnlyModel model = modelBuilder.Model; modelBuilder.Entity().OwnsOne(c => c.Details); @@ -347,7 +347,7 @@ public virtual void Can_configure_one_to_one_relationship_from_an_owned_type() public virtual void Can_configure_owned_type_collection_from_an_owned_type() { var modelBuilder = CreateModelBuilder(); - IModel model = modelBuilder.Model; + IReadOnlyModel model = modelBuilder.Model; modelBuilder.Ignore(); var entityBuilder = modelBuilder.Entity().OwnsOne(o => o.Customer) @@ -882,7 +882,7 @@ public virtual void Can_configure_fk_on_multiple_ownerships() .WithOwner() .HasForeignKey("BookLabelId"); - IModel model = modelBuilder.Model; + IReadOnlyModel model = modelBuilder.Model; var bookOwnership1 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.Label)).ForeignKey; var bookOwnership2 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.AlternateLabel)).ForeignKey; @@ -951,7 +951,7 @@ public virtual void Can_map_derived_of_owned_type() modelBuilder.Entity().OwnsOne(c => c.Details); modelBuilder.Entity(); - IModel model = modelBuilder.Model; + IReadOnlyModel model = modelBuilder.Model; var owner = model.FindEntityType(typeof(OrderCombination)); var owned = owner.FindNavigation(nameof(OrderCombination.Details)).ForeignKey.DeclaringEntityType; @@ -980,7 +980,7 @@ public virtual void Can_map_derived_of_owned_type_first() modelBuilder.Entity().OwnsOne(c => c.Details); - IModel model = modelBuilder.Model; + IReadOnlyModel model = modelBuilder.Model; var owner = model.FindEntityType(typeof(OrderCombination)); var owned = owner.FindNavigation(nameof(OrderCombination.Details)).ForeignKey.DeclaringEntityType; @@ -1218,7 +1218,7 @@ public virtual void Can_configure_hierarchy_with_reference_navigations_as_owned_ VerifyOwnedBookLabelModel(model); } - protected virtual void VerifyOwnedBookLabelModel(IModel model) + protected virtual void VerifyOwnedBookLabelModel(IReadOnlyModel model) { var bookOwnership1 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.Label)).ForeignKey; var bookOwnership2 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.AlternateLabel)).ForeignKey; @@ -1454,8 +1454,8 @@ public virtual void Navigations_on_owned_type_can_set_access_mode_using_expressi .Navigation(e => e.OwnedDependent) .UsePropertyAccessMode(PropertyAccessMode.Field); - var principal = (IEntityType)model.FindEntityType(typeof(OneToOneNavPrincipalOwner)); - var dependent = (IEntityType)model.FindEntityType(typeof(OwnedNavDependent)); + var principal = (IReadOnlyEntityType)model.FindEntityType(typeof(OneToOneNavPrincipalOwner)); + var dependent = (IReadOnlyEntityType)model.FindEntityType(typeof(OwnedNavDependent)); Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependent").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToOneOwner").GetPropertyAccessMode()); @@ -1481,8 +1481,8 @@ public virtual void Navigations_on_owned_type_collection_can_set_access_mode() .Navigation(e => e.OwnedDependents) .UsePropertyAccessMode(PropertyAccessMode.Field); - var principal = (IEntityType)model.FindEntityType(typeof(OneToManyNavPrincipalOwner)); - var dependent = (IEntityType)model.FindEntityType(typeof(OwnedOneToManyNavDependent)); + var principal = (IReadOnlyEntityType)model.FindEntityType(typeof(OneToManyNavPrincipalOwner)); + var dependent = (IReadOnlyEntityType)model.FindEntityType(typeof(OwnedOneToManyNavDependent)); Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependents").GetPropertyAccessMode()); Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToManyOwner").GetPropertyAccessMode()); diff --git a/test/EFCore.Tests/Utilities/MultigraphTest.cs b/test/EFCore.Tests/Utilities/MultigraphTest.cs index a74a87a4bde..7651dfee3e7 100644 --- a/test/EFCore.Tests/Utilities/MultigraphTest.cs +++ b/test/EFCore.Tests/Utilities/MultigraphTest.cs @@ -73,9 +73,9 @@ private class E public int P2 { get; set; } } - private class EntityTypeGraph : Multigraph + private class EntityTypeGraph : Multigraph { - public void Populate(params IEntityType[] entityTypes) + public void Populate(params IReadOnlyEntityType[] entityTypes) { AddVertices(entityTypes); @@ -88,7 +88,7 @@ public void Populate(params IEntityType[] entityTypes) } } - protected override string ToString(IEntityType vertex) + protected override string ToString(IReadOnlyEntityType vertex) => vertex.DisplayName(); } diff --git a/test/EFCore.Tests/ValueGeneration/TemporaryNumberValueGeneratorFactoryTest.cs b/test/EFCore.Tests/ValueGeneration/TemporaryNumberValueGeneratorFactoryTest.cs index 041dbd59b7a..abd6a525151 100644 --- a/test/EFCore.Tests/ValueGeneration/TemporaryNumberValueGeneratorFactoryTest.cs +++ b/test/EFCore.Tests/ValueGeneration/TemporaryNumberValueGeneratorFactoryTest.cs @@ -11,7 +11,14 @@ namespace Microsoft.EntityFrameworkCore.ValueGeneration { public class TemporaryNumberValueGeneratorFactoryTest { - private static readonly IMutableModel _model = InMemoryTestHelpers.Instance.BuildModelFor(); + private static readonly IModel _model = BuildModel(); + + public static IModel BuildModel() + { + var builder = InMemoryTestHelpers.Instance.CreateConventionBuilder(); + builder.Entity(); + return (IModel)builder.Model; + } [ConditionalFact] public void Can_create_factories_for_all_integer_types()