From 5618bc6a0aa2e0dfed57c0f3cd64830733e7c924 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Wed, 22 Jun 2022 12:32:55 -0700 Subject: [PATCH] Add entity splitting support to relational model Add a linking FK for mapping fragments Make ITableMappingBase.IsSharedTablePrincipal and ITableMappingBase.IsSplitEntityTypePrincipal nullable Warn for FKs, indexes and keys split over several tables Move more logic from RelationalModel and RelationalModelValidator to extensions to avoid duplication Part of #620 --- .../Diagnostics/RelationalEventId.cs | 15 + .../Diagnostics/RelationalLoggerExtensions.cs | 41 + .../RelationalLoggingDefinitions.cs | 9 + .../RelationalEntityTypeExtensions.cs | 143 +++- .../RelationalForeignKeyExtensions.cs | 87 +- .../RelationalIndexBuilderExtensions.cs | 2 - .../Extensions/RelationalIndexExtensions.cs | 64 +- .../RelationalKeyBuilderExtensions.cs | 2 - .../Extensions/RelationalKeyExtensions.cs | 102 +-- .../RelationalPropertyExtensions.cs | 67 +- .../RelationalModelValidator.cs | 245 ++---- .../Conventions/EntitySplittingConvention.cs | 64 ++ .../RelationalConventionSetBuilder.cs | 1 + .../Metadata/ITableMappingBase.cs | 8 +- .../RelationalEntityTypeExtensions.cs | 36 + .../RelationalForeignKeyExtensions.cs | 174 ++++ .../Internal/RelationalIndexExtensions.cs | 177 +++++ .../Internal/RelationalKeyExtensions.cs | 155 ++++ .../Metadata/Internal/RelationalModel.cs | 317 ++++---- .../Metadata/Internal/TableMappingBase.cs | 4 +- .../Internal/TableMappingBaseComparer.cs | 45 +- .../Internal/MigrationsModelDiffer.cs | 2 +- .../Properties/RelationalStrings.Designer.cs | 33 + .../Properties/RelationalStrings.resx | 7 + .../Internal/SqlServerModelValidator.cs | 17 +- .../Internal/SqlServerAnnotationProvider.cs | 3 +- src/EFCore/Diagnostics/KeyEventData.cs | 33 + src/EFCore/Infrastructure/ModelValidator.cs | 14 +- src/EFCore/Metadata/IReadOnlyIndex.cs | 5 +- src/EFCore/Metadata/Internal/Property.cs | 6 +- .../Metadata/Internal/PropertyExtensions.cs | 12 - .../TestUtilities/CosmosTestHelpers.cs | 2 +- .../CosmosModelValidatorTest.cs | 42 +- .../Design/CSharpMigrationsGeneratorTest.cs | 2 +- .../CSharpRuntimeModelCodeGeneratorTest.cs | 26 +- .../TestUtilities/DesignTestHelpers.cs | 2 +- .../TestUtilities/InMemoryTestHelpers.cs | 2 +- .../InMemoryModelValidatorTest.cs | 2 +- .../RelationalModelValidatorTest.cs | 395 ++++++---- .../Metadata/RelationalModelTest.cs | 746 +++++++++++++++--- .../Internal/MigrationsModelDifferTestBase.cs | 2 +- .../RelationalApiConsistencyTest.cs | 3 - .../Storage/RelationalTypeMapperTest.cs | 2 +- .../TestUtilities/RelationalTestHelpers.cs | 2 +- .../TestUtilities/TestHelpers.cs | 18 +- .../TestUtilities/SqlServerTestHelpers.cs | 2 +- .../SqlServerModelValidatorTest.cs | 106 +-- .../Storage/SqlServerTypeMappingSourceTest.cs | 2 +- .../TestUtilities/SqliteTestHelpers.cs | 2 +- .../SqliteModelValidatorTest.cs | 10 +- .../ModelValidatorTest.PropertyMapping.cs | 2 +- .../Infrastructure/ModelValidatorTest.cs | 76 +- .../Infrastructure/ModelValidatorTestBase.cs | 2 +- 53 files changed, 2288 insertions(+), 1050 deletions(-) create mode 100644 src/EFCore.Relational/Metadata/Conventions/EntitySplittingConvention.cs create mode 100644 src/EFCore/Diagnostics/KeyEventData.cs diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs index 614e687e199..6b68afac485 100644 --- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs +++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs @@ -96,6 +96,7 @@ private enum Id DuplicateColumnOrders, ForeignKeyTpcPrincipalWarning, TpcStoreGeneratedIdentityWarning, + KeyUnmappedProperties, // Update events BatchReadyForExecution = CoreEventId.RelationalBaseId + 700, @@ -799,6 +800,20 @@ private static EventId MakeValidationId(Id id) public static readonly EventId IndexPropertiesMappedToNonOverlappingTables = MakeValidationId(Id.IndexPropertiesMappedToNonOverlappingTables); + /// + /// A key specifies properties which don't map to a single table. + /// + /// + /// + /// This event is in the category. + /// + /// + /// This event uses the payload when used with a . + /// + /// + public static readonly EventId KeyUnmappedProperties = + MakeValidationId(Id.KeyUnmappedProperties); + /// /// A foreign key specifies properties which don't map to the related tables. /// diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs index 82492b22aa6..93bf91d2cb6 100644 --- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs +++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs @@ -2774,6 +2774,47 @@ private static string NamedIndexPropertiesMappedToNonOverlappingTables(EventDefi p.TablesMappedToProperty2.FormatTables())); } + /// + /// Logs the event. + /// + /// The diagnostics logger to use. + /// The foreign key. + public static void KeyUnmappedProperties( + this IDiagnosticsLogger diagnostics, + IKey key) + { + var definition = RelationalResources.LogKeyUnmappedProperties(diagnostics); + + if (diagnostics.ShouldLog(definition)) + { + definition.Log( + diagnostics, + key.Properties.Format(), + key.DeclaringEntityType.DisplayName(), + key.DeclaringEntityType.GetSchemaQualifiedTableName()!); + } + + if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled)) + { + var eventData = new KeyEventData( + definition, + KeyUnmappedProperties, + key); + + diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled); + } + } + + private static string KeyUnmappedProperties(EventDefinitionBase definition, EventData payload) + { + var d = (EventDefinition)definition; + var p = (KeyEventData)payload; + return d.GenerateMessage( + p.Key.Properties.Format(), + p.Key.DeclaringEntityType.DisplayName(), + p.Key.DeclaringEntityType.GetSchemaQualifiedTableName()!); + } + /// /// Logs the event. /// diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs index d77e753cadf..ba7c869f997 100644 --- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs +++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs @@ -520,6 +520,15 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions [EntityFrameworkInternal] public EventDefinitionBase? LogUnnamedIndexPropertiesMappedToNonOverlappingTables; + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + public EventDefinitionBase? LogKeyUnmappedProperties; + /// /// 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/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 40837779424..bef4f64ea75 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -842,6 +842,36 @@ public static void SetComment(this IMutableEntityType entityType, string? commen public static IEnumerable GetMappingFragments(this IReadOnlyEntityType entityType) => EntityTypeMappingFragment.Get(entityType) ?? Enumerable.Empty(); + /// + /// + /// Returns all configured entity type mapping fragments. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments(this IMutableEntityType entityType) + => EntityTypeMappingFragment.Get(entityType)?.Cast() + ?? Enumerable.Empty(); + + /// + /// + /// Returns all configured entity type mapping fragments. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments(this IConventionEntityType entityType) + => EntityTypeMappingFragment.Get(entityType)?.Cast() + ?? Enumerable.Empty(); + /// /// /// Returns all configured entity type mapping fragments. @@ -857,6 +887,75 @@ public static IEnumerable GetMappingFragments(this I => EntityTypeMappingFragment.Get(entityType)?.Cast() ?? Enumerable.Empty(); + /// + /// + /// Returns all configured entity type mapping fragments of the given type. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The type of store object to get the mapping fragments for. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments( + this IReadOnlyEntityType entityType, StoreObjectType storeObjectType) + { + var fragments = EntityTypeMappingFragment.Get(entityType); + return fragments == null + ? Enumerable.Empty() + : fragments.Where(f => f.StoreObject.StoreObjectType == storeObjectType); + } + + /// + /// + /// Returns all configured entity type mapping fragments of the given type. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The type of store object to get the mapping fragments for. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments( + this IMutableEntityType entityType, StoreObjectType storeObjectType) + => GetMappingFragments((IReadOnlyEntityType)entityType, storeObjectType).Cast(); + + /// + /// + /// Returns all configured entity type mapping fragments of the given type. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The type of store object to get the mapping fragments for. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments( + this IConventionEntityType entityType, StoreObjectType storeObjectType) + => GetMappingFragments((IReadOnlyEntityType)entityType, storeObjectType).Cast(); + + /// + /// + /// Returns all configured entity type mapping fragments of the given type. + /// + /// + /// This method is typically used by database providers (and other extensions). It is generally + /// not used in application code. + /// + /// + /// The entity type. + /// The type of store object to get the mapping fragments for. + /// The configured entity type mapping fragments. + public static IEnumerable GetMappingFragments( + this IEntityType entityType, StoreObjectType storeObjectType) + => GetMappingFragments((IReadOnlyEntityType)entityType, storeObjectType).Cast(); + /// /// /// Returns the entity type mapping for a particular table-like store object. @@ -1019,46 +1118,22 @@ public static IEnumerable FindRowInternalForeignKeys( foreach (var foreignKey in entityType.GetForeignKeys()) { - var principalEntityType = foreignKey.PrincipalEntityType; if (!foreignKey.PrincipalKey.IsPrimaryKey() - || principalEntityType == foreignKey.DeclaringEntityType - || !foreignKey.IsUnique -#pragma warning disable EF1001 // Internal EF Core API usage. - || !PropertyListComparer.Instance.Equals(foreignKey.Properties, primaryKey.Properties)) -#pragma warning restore EF1001 // Internal EF Core API usage. + || foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType) + || !foreignKey.Properties.SequenceEqual(primaryKey.Properties) + || !IsMapped(foreignKey, storeObject)) { continue; } - switch (storeObject.StoreObjectType) - { - case StoreObjectType.Table: - if (storeObject.Name == principalEntityType.GetTableName() - && storeObject.Schema == principalEntityType.GetSchema()) - { - yield return foreignKey; - } - - break; - case StoreObjectType.View: - if (storeObject.Name == principalEntityType.GetViewName() - && storeObject.Schema == principalEntityType.GetViewSchema()) - { - yield return foreignKey; - } - - break; - case StoreObjectType.Function: - if (storeObject.Name == principalEntityType.GetFunctionName()) - { - yield return foreignKey; - } - - break; - default: - throw new NotSupportedException(storeObject.StoreObjectType.ToString()); - } + yield return foreignKey; } + + static bool IsMapped(IReadOnlyForeignKey foreignKey, StoreObjectIdentifier storeObject) + => (StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, storeObject.StoreObjectType) == storeObject + || foreignKey.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject)) + && (StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, storeObject.StoreObjectType) == storeObject + || foreignKey.PrincipalEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject)); } /// diff --git a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs index 5b31d18cea6..123e35010c7 100644 --- a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text; +using Microsoft.EntityFrameworkCore.Metadata.Internal; // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore; @@ -44,18 +45,7 @@ public static class RelationalForeignKeyExtensions this IReadOnlyForeignKey foreignKey, in StoreObjectIdentifier storeObject, in StoreObjectIdentifier principalStoreObject) - { - if (storeObject.StoreObjectType != StoreObjectType.Table - || principalStoreObject.StoreObjectType != StoreObjectType.Table) - { - return null; - } - - var annotation = foreignKey.FindAnnotation(RelationalAnnotationNames.Name); - return annotation != null - ? (string?)annotation.Value - : foreignKey.GetDefaultName(storeObject, principalStoreObject); - } + => foreignKey.GetConstraintName(storeObject, principalStoreObject, null); /// /// Returns the default constraint name that would be used for this foreign key. @@ -101,78 +91,7 @@ public static class RelationalForeignKeyExtensions this IReadOnlyForeignKey foreignKey, in StoreObjectIdentifier storeObject, in StoreObjectIdentifier principalStoreObject) - { - if (storeObject.StoreObjectType != StoreObjectType.Table - || principalStoreObject.StoreObjectType != StoreObjectType.Table) - { - return null; - } - - var propertyNames = foreignKey.Properties.GetColumnNames(storeObject); - var principalPropertyNames = foreignKey.PrincipalKey.Properties.GetColumnNames(principalStoreObject); - if (propertyNames == null - || principalPropertyNames == null) - { - return null; - } - - var rootForeignKey = foreignKey; - - // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) - // Using a hashset is detrimental to the perf when there are no cycles - for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) - { - IReadOnlyForeignKey? linkedForeignKey = null; - foreach (var otherForeignKey in rootForeignKey.DeclaringEntityType - .FindRowInternalForeignKeys(storeObject) - .SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())) - { - if (principalStoreObject.Name == otherForeignKey.PrincipalEntityType.GetTableName() - && principalStoreObject.Schema == otherForeignKey.PrincipalEntityType.GetSchema()) - { - var otherColumnNames = otherForeignKey.Properties.GetColumnNames(storeObject); - var otherPrincipalColumnNames = otherForeignKey.PrincipalKey.Properties.GetColumnNames(principalStoreObject); - if (otherColumnNames != null - && otherPrincipalColumnNames != null - && propertyNames.SequenceEqual(otherColumnNames) - && principalPropertyNames.SequenceEqual(otherPrincipalColumnNames)) - { - linkedForeignKey = otherForeignKey; - break; - } - } - } - - if (linkedForeignKey == null) - { - break; - } - - rootForeignKey = linkedForeignKey; - } - - if (rootForeignKey != foreignKey) - { - return rootForeignKey.GetConstraintName(storeObject, principalStoreObject); - } - - if (foreignKey.PrincipalEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy - && foreignKey.PrincipalEntityType.GetDerivedTypes().Any(et => StoreObjectIdentifier.Create(et, StoreObjectType.Table) != null)) - { - return null; - } - - var baseName = new StringBuilder() - .Append("FK_") - .Append(storeObject.Name) - .Append('_') - .Append(principalStoreObject.Name) - .Append('_') - .AppendJoin(propertyNames, "_") - .ToString(); - - return Uniquifier.Truncate(baseName, foreignKey.DeclaringEntityType.Model.GetMaxIdentifierLength()); - } + => foreignKey.GetDefaultName(storeObject, principalStoreObject, null); /// /// Sets the foreign key constraint name. diff --git a/src/EFCore.Relational/Extensions/RelationalIndexBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalIndexBuilderExtensions.cs index fedf1762784..aecd8fe5909 100644 --- a/src/EFCore.Relational/Extensions/RelationalIndexBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalIndexBuilderExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - - // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore; diff --git a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs index 7efc8078c15..76492dabbe6 100644 --- a/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalIndexExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text; +using Microsoft.EntityFrameworkCore.Metadata.Internal; // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore; @@ -33,11 +34,7 @@ public static class RelationalIndexExtensions /// The identifier of the store object. /// The name of the index in the database. public static string? GetDatabaseName(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) - => storeObject.StoreObjectType != StoreObjectType.Table - ? null - : (string?)index[RelationalAnnotationNames.Name] - ?? index.Name - ?? index.GetDefaultDatabaseName(storeObject); + => index.GetDatabaseName(storeObject, null); /// /// Returns the default name that would be used for this index. @@ -69,61 +66,8 @@ public static class RelationalIndexExtensions /// The identifier of the store object. /// The default name that would be used for this index. public static string? GetDefaultDatabaseName(this IReadOnlyIndex index, in StoreObjectIdentifier storeObject) - { - if (storeObject.StoreObjectType != StoreObjectType.Table) - { - return null; - } - - var columnNames = index.Properties.GetColumnNames(storeObject); - if (columnNames == null) - { - return null; - } - - var rootIndex = index; - - // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) - // Using a hashset is detrimental to the perf when there are no cycles - for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) - { - IReadOnlyIndex? linkedIndex = null; - foreach (var otherIndex in rootIndex.DeclaringEntityType - .FindRowInternalForeignKeys(storeObject) - .SelectMany(fk => fk.PrincipalEntityType.GetIndexes())) - { - var otherColumnNames = otherIndex.Properties.GetColumnNames(storeObject); - if ((otherColumnNames != null) - && otherColumnNames.SequenceEqual(columnNames)) - { - linkedIndex = otherIndex; - break; - } - } - - if (linkedIndex == null) - { - break; - } - - rootIndex = linkedIndex; - } - - if (rootIndex != index) - { - return rootIndex.GetDatabaseName(storeObject); - } - - var baseName = new StringBuilder() - .Append("IX_") - .Append(storeObject.Name) - .Append('_') - .AppendJoin(columnNames, "_") - .ToString(); - - return Uniquifier.Truncate(baseName, index.DeclaringEntityType.Model.GetMaxIdentifierLength()); - } - + => index.GetDefaultDatabaseName(storeObject, null); + /// /// Sets the name of the index in the database. /// diff --git a/src/EFCore.Relational/Extensions/RelationalKeyBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalKeyBuilderExtensions.cs index fbbeb6e93cf..af4468795a8 100644 --- a/src/EFCore.Relational/Extensions/RelationalKeyBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalKeyBuilderExtensions.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - - // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore; diff --git a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs index 0d5381af63b..200d63e33da 100644 --- a/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalKeyExtensions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text; +using Microsoft.EntityFrameworkCore.Metadata.Internal; // ReSharper disable once CheckNamespace namespace Microsoft.EntityFrameworkCore; @@ -32,22 +33,7 @@ public static class RelationalKeyExtensions /// The identifier of the containing store object. /// The key constraint name for this key. public static string? GetName(this IReadOnlyKey key, in StoreObjectIdentifier storeObject) - { - if (storeObject.StoreObjectType != StoreObjectType.Table) - { - return null; - } - - foreach (var containingType in key.DeclaringEntityType.GetDerivedTypesInclusive()) - { - if (StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType) == storeObject) - { - return (string?)key[RelationalAnnotationNames.Name] ?? key.GetDefaultName(storeObject); - } - } - - return null; - } + => key.GetName(storeObject, null); /// /// Returns the default key constraint name that would be used for this key. @@ -81,89 +67,7 @@ public static class RelationalKeyExtensions /// The identifier of the containing store object. /// The default key constraint name that would be used for this key. public static string? GetDefaultName(this IReadOnlyKey key, in StoreObjectIdentifier storeObject) - { - if (storeObject.StoreObjectType != StoreObjectType.Table) - { - return null; - } - - string? name; - if (key.IsPrimaryKey()) - { - var rootKey = key; - // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) - // Using a hashset is detrimental to the perf when there are no cycles - for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) - { - var linkingFk = rootKey!.DeclaringEntityType.FindRowInternalForeignKeys(storeObject) - .FirstOrDefault(); - if (linkingFk == null) - { - break; - } - - rootKey = linkingFk.PrincipalEntityType.FindPrimaryKey(); - } - - if (rootKey != null - && rootKey != key) - { - return rootKey.GetName(storeObject); - } - - name = "PK_" + storeObject.Name; - } - else - { - var columnNames = key.Properties.GetColumnNames(storeObject); - if (columnNames == null) - { - return null; - } - - var rootKey = key; - - // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) - // Using a hashset is detrimental to the perf when there are no cycles - for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) - { - IReadOnlyKey? linkedKey = null; - foreach (var otherKey in rootKey.DeclaringEntityType - .FindRowInternalForeignKeys(storeObject) - .SelectMany(fk => fk.PrincipalEntityType.GetKeys())) - { - var otherColumnNames = otherKey.Properties.GetColumnNames(storeObject); - if ((otherColumnNames != null) - && otherColumnNames.SequenceEqual(columnNames)) - { - linkedKey = otherKey; - break; - } - } - - if (linkedKey == null) - { - break; - } - - rootKey = linkedKey; - } - - if (rootKey != key) - { - return rootKey.GetName(storeObject); - } - - name = new StringBuilder() - .Append("AK_") - .Append(storeObject.Name) - .Append('_') - .AppendJoin(columnNames, "_") - .ToString(); - } - - return Uniquifier.Truncate(name, key.DeclaringEntityType.Model.GetMaxIdentifierLength()); - } + => key.GetDefaultName(storeObject, null); /// /// Sets the key constraint name for this key. diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs index ce5b852aaf3..7dff017dcce 100644 --- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs @@ -115,21 +115,24 @@ public static string GetColumnName(this IReadOnlyProperty property) return null; } } - else if (property.DeclaringEntityType.GetMappingFragments().Any()) + else { - if (overrides == null - && (declaringStoreObject != storeObject - || property.DeclaringEntityType.GetMappingFragments() - .Any(f => property.FindOverrides(f.StoreObject) != null))) + var fragments = property.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).ToList(); + if (fragments.Count > 0) { + if (overrides == null + && (declaringStoreObject != storeObject + || fragments.Any(f => property.FindOverrides(f.StoreObject) != null))) + { + return null; + } + } + else if (declaringStoreObject != storeObject) + { return null; } } - else if (declaringStoreObject != storeObject) - { - return null; - } } } @@ -181,6 +184,7 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property, in St var entityType = property.DeclaringEntityType; StringBuilder? builder = null; + var currentStoreObject = storeObject; while (true) { var ownership = entityType.GetForeignKeys().SingleOrDefault(fk => fk.IsOwnership); @@ -189,39 +193,10 @@ public static string GetDefaultColumnName(this IReadOnlyProperty property, in St break; } - var name = storeObject.Name; - var schema = storeObject.Schema; var ownerType = ownership.PrincipalEntityType; - switch (storeObject.StoreObjectType) - { - case StoreObjectType.Table: - if (name != ownerType.GetTableName() - || schema != ownerType.GetSchema()) - { - entityType = null; - } - - break; - case StoreObjectType.View: - if (name != ownerType.GetViewName() - || schema != ownerType.GetViewSchema()) - { - entityType = null; - } - - break; - case StoreObjectType.Function: - if (name != ownerType.GetFunctionName()) - { - entityType = null; - } - - break; - default: - throw new NotSupportedException(storeObject.StoreObjectType.ToString()); - } - - if (entityType == null) + if (StoreObjectIdentifier.Create(ownerType, currentStoreObject.StoreObjectType) != currentStoreObject + && ownerType.GetMappingFragments(storeObject.StoreObjectType) + .All(f => f.StoreObject != currentStoreObject)) { break; } @@ -1624,14 +1599,18 @@ public static IEnumerable GetMappedStoreObjects( yield break; } - foreach (var fragment in declaringType.GetMappingFragments()) + foreach (var fragment in declaringType.GetMappingFragments(storeObjectType)) { - if (fragment.StoreObject.StoreObjectType == storeObjectType - && property.GetColumnName(fragment.StoreObject) != null) + if (property.GetColumnName(fragment.StoreObject) != null) { yield return fragment.StoreObject; } } + + if (declaringType.GetMappingStrategy() == RelationalAnnotationNames.TphMappingStrategy) + { + yield break; + } foreach (var derivedType in declaringType.GetDerivedTypes()) { diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs index f4faed9457d..722d9e77ebb 100644 --- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs +++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs @@ -218,7 +218,7 @@ protected virtual void ValidateBoolsWithDefaults( continue; } - if (StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is StoreObjectIdentifier table + if (StoreObjectIdentifier.Create(property.DeclaringEntityType, StoreObjectType.Table) is { } table && (IsNotNullAndFalse(property.GetDefaultValue(table)) || property.GetDefaultValueSql(table) != null)) { @@ -270,13 +270,13 @@ protected virtual void ValidateSharedTableCompatibility( var tables = new Dictionary>(); foreach (var entityType in model.GetEntityTypes()) { - var tableName = entityType.GetTableName(); - if (tableName == null) + var tableId = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table); + if (tableId == null) { continue; } - var table = StoreObjectIdentifier.Table(tableName, entityType.GetSchema()); + var table = tableId.Value; if (!tables.TryGetValue(table, out var mappedTypes)) { mappedTypes = new List(); @@ -288,7 +288,7 @@ protected virtual void ValidateSharedTableCompatibility( foreach (var (table, mappedTypes) in tables) { - ValidateSharedTableCompatibility(mappedTypes, table.Name, table.Schema, logger); + ValidateSharedTableCompatibility(mappedTypes, table, logger); ValidateSharedColumnsCompatibility(mappedTypes, table, logger); ValidateSharedKeysCompatibility(mappedTypes, table, logger); ValidateSharedForeignKeysCompatibility(mappedTypes, table, logger); @@ -362,16 +362,17 @@ protected virtual void ValidateSharedTableCompatibility( foreach (var foreignKey in entityType.FindForeignKeys(entityType.FindPrimaryKey()!.Properties)) { var principalEntityType = foreignKey.PrincipalEntityType; - if (!mappedTypes.Contains(principalEntityType)) + if (foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType) + || !mappedTypes.Contains(principalEntityType)) { continue; } list.Add(principalEntityType); - var (entityTypes, innerOptional) = GetPrincipalEntityTypes(principalEntityType.GetRootType()); + var (entityTypes, _) = GetPrincipalEntityTypes(principalEntityType.GetRootType()); list.AddRange(entityTypes); - optional |= !foreignKey.IsRequiredDependent | innerOptional; + optional |= !foreignKey.IsRequiredDependent; } tuple = (list, optional); @@ -387,13 +388,11 @@ protected virtual void ValidateSharedTableCompatibility( /// Validates the compatibility of entity types sharing a given table. /// /// The mapped entity types. - /// The table name. - /// The schema. + /// The table identifier. /// The logger to use. protected virtual void ValidateSharedTableCompatibility( IReadOnlyList mappedTypes, - string tableName, - string? schema, + in StoreObjectIdentifier storeObject, IDiagnosticsLogger logger) { if (mappedTypes.Count == 1) @@ -401,7 +400,6 @@ protected virtual void ValidateSharedTableCompatibility( return; } - var storeObject = StoreObjectIdentifier.Table(tableName, schema); var unvalidatedTypes = new HashSet(mappedTypes); IEntityType? root = null; foreach (var mappedType in mappedTypes) @@ -416,7 +414,8 @@ protected virtual void ValidateSharedTableCompatibility( && (mappedType.FindForeignKeys(primaryKey.Properties) .FirstOrDefault( fk => fk.PrincipalKey.IsPrimaryKey() - && unvalidatedTypes.Contains(fk.PrincipalEntityType)) is IForeignKey linkingFK)) + && !fk.PrincipalEntityType.IsAssignableFrom(fk.DeclaringEntityType) + && unvalidatedTypes.Contains(fk.PrincipalEntityType)) is { } linkingFK)) { if (mappedType.BaseType != null) { @@ -526,7 +525,7 @@ protected virtual void ValidateSharedTableCompatibility( Check.DebugAssert(root != null, "root is null"); throw new InvalidOperationException( RelationalStrings.IncompatibleTableNoRelationship( - tableName, + storeObject.DisplayName(), invalidEntityType.DisplayName(), root.DisplayName())); } @@ -874,10 +873,8 @@ protected virtual void ValidateCompatible( var typeMapping = property.GetRelationalTypeMapping(); var duplicateTypeMapping = duplicateProperty.GetRelationalTypeMapping(); - var currentTypeString = property.GetColumnType(storeObject) - ?? typeMapping.StoreType; - var previousTypeString = duplicateProperty.GetColumnType(storeObject) - ?? duplicateTypeMapping.StoreType; + var currentTypeString = property.GetColumnType(storeObject); + var previousTypeString = duplicateProperty.GetColumnType(storeObject); if (!string.Equals(currentTypeString, previousTypeString, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException( @@ -1075,27 +1072,9 @@ protected virtual void ValidateSharedForeignKeysCompatibility( continue; } - var foreignKeyName = foreignKey.GetConstraintName(storeObject, principalTable.Value); + var foreignKeyName = foreignKey.GetConstraintName(storeObject, principalTable.Value, logger); if (foreignKeyName == null) { - if (foreignKey.PrincipalEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) - { - logger.ForeignKeyTpcPrincipalWarning(foreignKey); - } - - var derivedTables = foreignKey.DeclaringEntityType.GetDerivedTypes() - .Select(t => StoreObjectIdentifier.Create(t, StoreObjectType.Table)) - .Where(t => t != null); - if (foreignKey.GetConstraintName() != null - && derivedTables.All( - t => foreignKey.GetConstraintName( - t!.Value, - principalTable.Value) - == null)) - { - logger.ForeignKeyPropertiesMappedToUnrelatedTables(foreignKey); - } - continue; } @@ -1139,7 +1118,7 @@ protected virtual void ValidateSharedIndexesCompatibility( var indexMappings = new Dictionary(); foreach (var index in mappedTypes.SelectMany(et => et.GetDeclaredIndexes())) { - var indexName = index.GetDatabaseName(storeObject); + var indexName = index.GetDatabaseName(storeObject, logger); if (indexName == null) { continue; @@ -1185,7 +1164,7 @@ protected virtual void ValidateSharedKeysCompatibility( var keyMappings = new Dictionary(); foreach (var key in mappedTypes.SelectMany(et => et.GetDeclaredKeys())) { - var keyName = key.GetName(storeObject); + var keyName = key.GetName(storeObject, logger); if (keyName == null) { continue; @@ -1379,12 +1358,6 @@ protected override void ValidateInheritanceMapping( { logger.TpcStoreGeneratedIdentityWarning(storeGeneratedProperty); } - - foreach (var fk in entityType.GetDeclaredReferencingForeignKeys()) - { - AssertNonInternal(fk, StoreObjectType.View); - AssertNonInternal(fk, StoreObjectType.Table); - } } else if (primaryKey == null) { @@ -1420,32 +1393,6 @@ protected override void ValidateInheritanceMapping( } } } - - static void AssertNonInternal(IForeignKey foreignKey, StoreObjectType storeObjectType) - { - if (!foreignKey.PrincipalKey.IsPrimaryKey() - || foreignKey.PrincipalEntityType == foreignKey.DeclaringEntityType - || !foreignKey.IsUnique - || foreignKey.DeclaringEntityType.FindPrimaryKey() == null -#pragma warning disable EF1001 // Internal EF Core API usage. - || !PropertyListComparer.Instance.Equals(foreignKey.Properties, foreignKey.DeclaringEntityType.FindPrimaryKey()!.Properties)) -#pragma warning restore EF1001 // Internal EF Core API usage. - { - return; - } - - var storeObjectId = StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, storeObjectType); - if (storeObjectId == null - || storeObjectId != StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, storeObjectType)) - { - return; - } - - throw new InvalidOperationException(RelationalStrings.TpcTableSharing( - foreignKey.DeclaringEntityType.DisplayName(), - storeObjectId.Value.DisplayName(), - foreignKey.PrincipalEntityType.DisplayName())); - } } /// @@ -1469,6 +1416,7 @@ protected virtual void ValidateMappingStrategy(IEntityType entityType, string? m private static void ValidateNonTphMapping(IEntityType rootEntityType, bool forTables) { + var isTpc = rootEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy; var derivedTypes = new Dictionary<(string, string?), IEntityType>(); foreach (var entityType in rootEntityType.GetDerivedTypesInclusive()) { @@ -1489,17 +1437,31 @@ private static void ValidateNonTphMapping(IEntityType rootEntityType, bool forTa entityType.DisplayName(), otherType.DisplayName(), entityType.GetSchemaQualifiedViewName())); } + if (isTpc) + { + var storeObject = StoreObjectIdentifier.Create(entityType, forTables ? StoreObjectType.Table : StoreObjectType.View)!; + var rowInternalFk = entityType.FindDeclaredReferencingRowInternalForeignKeys(storeObject.Value) + .FirstOrDefault(); + if (rowInternalFk != null + && entityType.GetDirectlyDerivedTypes().Any()) + { + throw new InvalidOperationException(RelationalStrings.TpcTableSharing( + rowInternalFk.DeclaringEntityType.DisplayName(), + storeObject.Value.DisplayName(), + rowInternalFk.PrincipalEntityType.DisplayName())); + } + } + derivedTypes[(name, schema)] = entityType; } - var storeObject = StoreObjectIdentifier.Create(rootEntityType, forTables ? StoreObjectType.Table : StoreObjectType.View); - if (storeObject == null) + var rootStoreObject = StoreObjectIdentifier.Create(rootEntityType, forTables ? StoreObjectType.Table : StoreObjectType.View); + if (rootStoreObject == null) { return; } - var internalForeignKey = rootEntityType.FindRowInternalForeignKeys(storeObject.Value).FirstOrDefault(); - if (internalForeignKey != null + if (rootEntityType.FindRowInternalForeignKeys(rootStoreObject.Value).Any() && derivedTypes.Count > 1 && rootEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy) { @@ -1507,7 +1469,7 @@ private static void ValidateNonTphMapping(IEntityType rootEntityType, bool forTa var (derivedName, derivedSchema) = derivedTypePair.Key; throw new InvalidOperationException(RelationalStrings.TpcTableSharingDependent( rootEntityType.DisplayName(), - storeObject.Value.DisplayName(), + rootStoreObject.Value.DisplayName(), derivedTypePair.Value.DisplayName(), derivedSchema == null ? derivedName : $"{derivedSchema}.{derivedName}")); } @@ -1549,6 +1511,11 @@ private static void ValidateTphMapping(IEntityType rootEntityType, bool forTable } } + /// + protected override bool IsRedundant(IForeignKey foreignKey) + => base.IsRedundant(foreignKey) + && !foreignKey.DeclaringEntityType.GetMappingFragments().Any(); + /// /// Validates the entity type mapping fragments. /// @@ -1573,6 +1540,7 @@ protected virtual void ValidateMappingFragments( RelationalStrings.EntitySplittingHierarchy(entityType.DisplayName(), fragments.First().StoreObject.DisplayName())); } + // dependent table splitting and main fragment is not var anyTableFragments = false; var anyViewFragments = false; foreach (var fragment in fragments) @@ -1592,6 +1560,16 @@ protected virtual void ValidateMappingFragments( entityType.DisplayName(), fragment.StoreObject.DisplayName())); } + var unmatchedLeafRowInternalFk = entityType.FindRowInternalForeignKeys(fragment.StoreObject) + .FirstOrDefault( + fk => entityType.FindRowInternalForeignKeys(mainStoreObject.Value) + .All(mainFk => mainFk.PrincipalEntityType != fk.PrincipalEntityType)); + if (unmatchedLeafRowInternalFk != null) + { + throw new InvalidOperationException(RelationalStrings.EntitySplittingUnmatchedMainTableSplitting( + entityType.DisplayName(), fragment.StoreObject.DisplayName(), unmatchedLeafRowInternalFk.PrincipalEntityType.DisplayName())); + } + var propertiesFound = false; foreach (var property in entityType.GetProperties()) { @@ -1749,7 +1727,7 @@ private static IEnumerable GetAllMappedStoreObjects( yield break; } - foreach (var fragment in property.DeclaringEntityType.GetMappingFragments()) + foreach (var fragment in property.DeclaringEntityType.GetMappingFragments(storeObjectType)) { yield return fragment.StoreObject; } @@ -1783,17 +1761,24 @@ private static IEnumerable GetAllMappedStoreObjects( if (declaringStoreObject != null) { - if (property.DeclaringEntityType.GetMappingFragments().Any()) + var fragments = property.DeclaringEntityType.GetMappingFragments(storeObjectType).ToList(); + if (fragments.Count > 0) { - foreach (var fragment in property.DeclaringEntityType.GetMappingFragments()) + var overrides = RelationalPropertyOverrides.Find(property, declaringStoreObject.Value); + if (overrides != null) { - var overrides = RelationalPropertyOverrides.Find(property, fragment.StoreObject); + yield return declaringStoreObject.Value; + } + + foreach (var fragment in fragments) + { + overrides = RelationalPropertyOverrides.Find(property, fragment.StoreObject); if (overrides != null) { yield return fragment.StoreObject; - yield break; } } + yield break; } yield return declaringStoreObject.Value; @@ -1826,7 +1811,6 @@ private static IEnumerable GetAllMappedStoreObjects( { queue.Enqueue(containingType); } - continue; } } } @@ -1843,93 +1827,16 @@ protected virtual void ValidateIndexProperties( { foreach (var entityType in model.GetEntityTypes()) { - foreach (var index in entityType.GetDeclaredIndexes() - .Where(i => ConfigurationSource.Convention != ((IConventionIndex)i).GetConfigurationSource())) - { - IProperty? propertyNotMappedToAnyTable = null; - Tuple>? firstPropertyTables = null; - Tuple>? lastPropertyTables = null; - HashSet<(string Table, string? Schema)>? overlappingTables = null; - foreach (var property in index.Properties) - { - var tablesMappedToProperty = property.DeclaringEntityType.GetDerivedTypesInclusive() - .Select(t => (t.GetTableName(), t.GetSchema())).Distinct() - .Where(n => n.Item1 != null && property.GetColumnName(StoreObjectIdentifier.Table(n.Item1, n.Item2)) != null)! - .ToList<(string Table, string? Schema)>(); - if (tablesMappedToProperty.Count == 0) - { - propertyNotMappedToAnyTable = property; - overlappingTables = null; - - if (firstPropertyTables != null) - { - // Property is not mapped but we already found a property that is mapped. - break; - } - - continue; - } - - if (firstPropertyTables == null) - { - firstPropertyTables = - new Tuple>(property.Name, tablesMappedToProperty); - } - else - { - lastPropertyTables = - new Tuple>(property.Name, tablesMappedToProperty); - } - - if (propertyNotMappedToAnyTable != null) - { - // Property is mapped but we already found a property that is not mapped. - overlappingTables = null; - break; - } - - if (overlappingTables == null) - { - overlappingTables = new HashSet<(string Table, string? Schema)>(tablesMappedToProperty); - } - else - { - overlappingTables.IntersectWith(tablesMappedToProperty); - if (overlappingTables.Count == 0) - { - break; - } - } - } + if (entityType.GetTableName() != null) + { + continue; + } - if (overlappingTables == null) - { - if (firstPropertyTables == null) - { - logger.AllIndexPropertiesNotToMappedToAnyTable( - entityType, - index); - } - else - { - logger.IndexPropertiesBothMappedAndNotMappedToTable( - entityType, - index, - propertyNotMappedToAnyTable!.Name); - } - } - else if (overlappingTables.Count == 0) + foreach (var index in entityType.GetDeclaredIndexes()) + { + if (ConfigurationSource.Convention != ((IConventionIndex)index).GetConfigurationSource()) { - Check.DebugAssert(firstPropertyTables != null, nameof(firstPropertyTables)); - Check.DebugAssert(lastPropertyTables != null, nameof(lastPropertyTables)); - - logger.IndexPropertiesMappedToNonOverlappingTables( - entityType, - index, - firstPropertyTables.Item1, - firstPropertyTables.Item2, - lastPropertyTables.Item1, - lastPropertyTables.Item2); + index.GetDatabaseName(StoreObjectIdentifier.Table(""), logger); } } } @@ -1963,7 +1870,7 @@ protected virtual void ValidateTriggers( throw new InvalidOperationException( RelationalStrings.TriggerWithMismatchedTable( trigger.ModelName, - (trigger.TableName!, trigger.TableSchema).FormatTable(), + (trigger.TableName, trigger.TableSchema).FormatTable(), entityType.DisplayName(), entityType.GetSchemaQualifiedTableName()) ); diff --git a/src/EFCore.Relational/Metadata/Conventions/EntitySplittingConvention.cs b/src/EFCore.Relational/Metadata/Conventions/EntitySplittingConvention.cs new file mode 100644 index 00000000000..ee8e4fc30d6 --- /dev/null +++ b/src/EFCore.Relational/Metadata/Conventions/EntitySplittingConvention.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; + +/// +/// A convention that creates linking relationships for entity splitting. +/// +/// +/// See Model building conventions and +/// Entity type hierarchy mapping for more information and examples. +/// +public class EntitySplittingConvention : IModelFinalizingConvention +{ + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + /// Parameter object containing relational dependencies for this convention. + public EntitySplittingConvention( + ProviderConventionSetBuilderDependencies dependencies, + RelationalConventionSetBuilderDependencies relationalDependencies) + { + Dependencies = dependencies; + RelationalDependencies = relationalDependencies; + } + + /// + /// Dependencies for this service. + /// + protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } + + /// + /// Relational provider-specific dependencies for this service. + /// + protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; } + + /// + public virtual void ProcessModelFinalizing( + IConventionModelBuilder modelBuilder, + IConventionContext context) + { + foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) + { + if (!entityType.GetMappingFragments().Any() + || entityType.GetTableName() == null) + { + continue; + } + + var pk = entityType.FindPrimaryKey(); + if (pk != null + && !entityType.FindDeclaredForeignKeys(pk.Properties) + .Any(fk => fk.PrincipalKey.IsPrimaryKey() + && fk.PrincipalEntityType.IsAssignableFrom(entityType) + && fk.PrincipalEntityType != entityType)) + { + entityType.Builder.HasRelationship(entityType, pk.Properties, pk) + ?.IsUnique(true) + ?.IsRequiredDependent(true); + } + } + } +} diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs index 3937f1281e1..a14ff223ec2 100644 --- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs +++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs @@ -112,6 +112,7 @@ public override ConventionSet CreateConventionSet() conventionSet.ModelFinalizingConventions.Add(dbFunctionAttributeConvention); conventionSet.ModelFinalizingConventions.Add(tableNameFromDbSetConvention); conventionSet.ModelFinalizingConventions.Add(storeGenerationConvention); + conventionSet.ModelFinalizingConventions.Add(new EntitySplittingConvention(Dependencies, RelationalDependencies)); conventionSet.ModelFinalizingConventions.Add(new EntityTypeHierarchyMappingConvention(Dependencies, RelationalDependencies)); conventionSet.ModelFinalizingConventions.Add(new SequenceUniquificationConvention(Dependencies, RelationalDependencies)); conventionSet.ModelFinalizingConventions.Add(new SharedTableConvention(Dependencies, RelationalDependencies)); diff --git a/src/EFCore.Relational/Metadata/ITableMappingBase.cs b/src/EFCore.Relational/Metadata/ITableMappingBase.cs index c47ad15840f..10722e17882 100644 --- a/src/EFCore.Relational/Metadata/ITableMappingBase.cs +++ b/src/EFCore.Relational/Metadata/ITableMappingBase.cs @@ -28,15 +28,15 @@ public interface ITableMappingBase : IAnnotatable /// /// Gets the value indicating whether this is the mapping for the principal entity type - /// if the table-like object is shared. + /// if the table-like object is shared. is the table-like object is not shared. /// - bool IsSharedTablePrincipal { get; } + bool? IsSharedTablePrincipal { get; } /// /// Gets the value indicating whether this is the mapping for the principal table-like object - /// if the entity type is split. + /// if the entity type is split. is the entity type is not split. /// - bool IsSplitEntityTypePrincipal { get; } + bool? IsSplitEntityTypePrincipal { get; } /// /// Gets the value indicating whether the mapped table-like object includes rows for the derived entity types. diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs index 8bab3bf5e09..437797dcbde 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalEntityTypeExtensions.cs @@ -19,6 +19,42 @@ public static class RelationalEntityTypeExtensions /// public const int MaxEntityTypesSharingTable = 128; + /// + /// 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 FindDeclaredReferencingRowInternalForeignKeys( + this IEntityType entityType, + StoreObjectIdentifier storeObject) + { + foreach (var foreignKey in entityType.GetDeclaredReferencingForeignKeys()) + { + var dependentPrimaryKey = foreignKey.DeclaringEntityType.FindPrimaryKey(); + if (dependentPrimaryKey == null) + { + yield break; + } + + if (!foreignKey.PrincipalKey.IsPrimaryKey() + || foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType) + || !foreignKey.Properties.SequenceEqual(dependentPrimaryKey.Properties) + || !IsMapped(foreignKey, storeObject)) + { + continue; + } + + yield return foreignKey; + } + + static bool IsMapped(IReadOnlyForeignKey foreignKey, StoreObjectIdentifier storeObject) + => (StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, storeObject.StoreObjectType) == storeObject + || foreignKey.DeclaringEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject)) + && (StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, storeObject.StoreObjectType) == storeObject + || foreignKey.PrincipalEntityType.GetMappingFragments(storeObject.StoreObjectType).Any(f => f.StoreObject == storeObject)); + } + /// /// 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/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs index 96ccf2e0c18..aa6f1b81d99 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalForeignKeyExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; + namespace Microsoft.EntityFrameworkCore.Metadata.Internal; /// @@ -161,4 +163,176 @@ public static bool AreCompatible( return true; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetConstraintName( + this IReadOnlyForeignKey foreignKey, + in StoreObjectIdentifier storeObject, + in StoreObjectIdentifier principalStoreObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table + || principalStoreObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + var defaultName = foreignKey.GetDefaultName(storeObject, principalStoreObject, logger); + var annotation = foreignKey.FindAnnotation(RelationalAnnotationNames.Name); + return annotation != null && defaultName != null + ? (string?)annotation.Value + : defaultName; + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetDefaultName( + this IReadOnlyForeignKey foreignKey, + in StoreObjectIdentifier storeObject, + in StoreObjectIdentifier principalStoreObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table + || principalStoreObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + var propertyNames = foreignKey.Properties.GetColumnNames(storeObject); + var principalPropertyNames = foreignKey.PrincipalKey.Properties.GetColumnNames(principalStoreObject); + if (propertyNames == null + || principalPropertyNames == null) + { + if (logger != null) + { + var principalTable = principalStoreObject; + var derivedTables = foreignKey.DeclaringEntityType.GetDerivedTypes() + .Select(t => StoreObjectIdentifier.Create(t, StoreObjectType.Table)) + .Where(t => t != null); + if (foreignKey.GetConstraintName() != null + && derivedTables.All( + t => foreignKey.GetConstraintName( + t!.Value, + principalTable) + == null)) + { + logger.ForeignKeyPropertiesMappedToUnrelatedTables((IForeignKey)foreignKey); + } + } + + return null; + } + + var rootForeignKey = foreignKey; + + // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) + // Using a hashset is detrimental to the perf when there are no cycles + for (var i = 0; i < Metadata.Internal.RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) + { + IReadOnlyForeignKey? linkedForeignKey = null; + foreach (var otherForeignKey in rootForeignKey.DeclaringEntityType + .FindRowInternalForeignKeys(storeObject) + .SelectMany(fk => fk.PrincipalEntityType.GetForeignKeys())) + { + if (principalStoreObject.Name == otherForeignKey.PrincipalEntityType.GetTableName() + && principalStoreObject.Schema == otherForeignKey.PrincipalEntityType.GetSchema()) + { + var otherColumnNames = otherForeignKey.Properties.GetColumnNames(storeObject); + var otherPrincipalColumnNames = otherForeignKey.PrincipalKey.Properties.GetColumnNames(principalStoreObject); + if (otherColumnNames != null + && otherPrincipalColumnNames != null + && propertyNames.SequenceEqual(otherColumnNames) + && principalPropertyNames.SequenceEqual(otherPrincipalColumnNames)) + { + var nameAnnotation = otherForeignKey.FindAnnotation(RelationalAnnotationNames.Name); + if (nameAnnotation != null) + { + return (string?)nameAnnotation.Value; + } + + linkedForeignKey = otherForeignKey; + break; + } + } + } + + if (linkedForeignKey == null) + { + break; + } + + rootForeignKey = linkedForeignKey; + } + + if (foreignKey.PrincipalEntityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy + && foreignKey.PrincipalEntityType.GetDerivedTypes().Any(et => StoreObjectIdentifier.Create(et, StoreObjectType.Table) != null)) + { + logger?.ForeignKeyTpcPrincipalWarning((IForeignKey)foreignKey); + return null; + } + + if (storeObject == principalStoreObject + && propertyNames.SequenceEqual(principalPropertyNames)) + { + // Redundant FK + return null; + } + + if (foreignKey.PrincipalKey.IsPrimaryKey() + && foreignKey.DeclaringEntityType.FindPrimaryKey() is IKey pk + && foreignKey.Properties.SequenceEqual(pk.Properties)) + { + if (!foreignKey.PrincipalEntityType.IsAssignableFrom(foreignKey.DeclaringEntityType) + && (StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, StoreObjectType.Table) != storeObject + || StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, StoreObjectType.Table) != principalStoreObject) + && ShareAnyFragments(foreignKey.DeclaringEntityType, foreignKey.PrincipalEntityType)) + { + // Row-internal FK + return null; + } + + if (foreignKey.PrincipalEntityType == foreignKey.DeclaringEntityType + && StoreObjectIdentifier.Create(foreignKey.PrincipalEntityType, StoreObjectType.Table) != principalStoreObject) + { + // Only create entity-splitting linking FKs to the main fragment + return null; + } + } + + if (foreignKey.DeclaringEntityType.GetMappingStrategy() == RelationalAnnotationNames.TptMappingStrategy + && StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, StoreObjectType.Table) != storeObject + && foreignKey.DeclaringEntityType.FindPrimaryKey() is IKey primaryKey + && foreignKey.Properties.SequenceEqual(primaryKey.Properties)) + { + // The identifying FK constraint is needed to be created only on the table that corresponds + // to the declaring entity type + return null; + } + + var baseName = new StringBuilder() + .Append("FK_") + .Append(storeObject.Name) + .Append('_') + .Append(principalStoreObject.Name) + .Append('_') + .AppendJoin(propertyNames, "_") + .ToString(); + + return Uniquifier.Truncate(baseName, foreignKey.DeclaringEntityType.Model.GetMaxIdentifierLength()); + + static bool ShareAnyFragments(IReadOnlyEntityType entityType1, IReadOnlyEntityType entityType2) + => new[] { StoreObjectIdentifier.Create(entityType1, StoreObjectType.Table)!.Value } + .Concat(entityType1.GetMappingFragments(StoreObjectType.Table).Select(f => f.StoreObject)) + .Intersect(new[] { StoreObjectIdentifier.Create(entityType2, StoreObjectType.Table)!.Value } + .Concat(entityType2.GetMappingFragments(StoreObjectType.Table).Select(f => f.StoreObject))).Any(); + } } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs index 2dd404a4500..74868b04a53 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalIndexExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; + namespace Microsoft.EntityFrameworkCore.Metadata.Internal; /// @@ -121,4 +123,179 @@ public static bool AreCompatible( return true; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetDatabaseName( + this IReadOnlyIndex index, + in StoreObjectIdentifier storeObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + var defaultName = index.GetDefaultDatabaseName(storeObject, logger); + var annotation = index.FindAnnotation(RelationalAnnotationNames.Name); + return annotation != null && defaultName != null + ? (string?)annotation.Value + : defaultName != null + ? index.Name ?? defaultName + : defaultName; + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetDefaultDatabaseName( + this IReadOnlyIndex index, + in StoreObjectIdentifier storeObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + var columnNames = index.Properties.GetColumnNames(storeObject); + if (columnNames == null) + { + if (logger != null + && ((IConventionIndex)index).GetConfigurationSource() != ConfigurationSource.Convention) + { + IReadOnlyProperty? propertyNotMappedToAnyTable = null; + (string, List)? firstPropertyTables = null; + (string, List)? lastPropertyTables = null; + HashSet? overlappingTables = null; + foreach (var property in index.Properties) + { + var tablesMappedToProperty = property.GetMappedStoreObjects(storeObject.StoreObjectType).ToList(); + if (tablesMappedToProperty.Count == 0) + { + propertyNotMappedToAnyTable = property; + overlappingTables = null; + + if (firstPropertyTables != null) + { + // Property is not mapped but we already found a property that is mapped. + break; + } + + continue; + } + + if (firstPropertyTables == null) + { + firstPropertyTables = (property.Name, tablesMappedToProperty); + } + else + { + lastPropertyTables = (property.Name, tablesMappedToProperty); + } + + if (propertyNotMappedToAnyTable != null) + { + // Property is mapped but we already found a property that is not mapped. + overlappingTables = null; + break; + } + + if (overlappingTables == null) + { + overlappingTables = new(tablesMappedToProperty); + } + else + { + overlappingTables.IntersectWith(tablesMappedToProperty); + if (overlappingTables.Count == 0) + { + break; + } + } + } + + if (overlappingTables == null) + { + if (firstPropertyTables == null) + { + logger.AllIndexPropertiesNotToMappedToAnyTable( + (IEntityType)index.DeclaringEntityType, + (IIndex)index); + } + else + { + logger.IndexPropertiesBothMappedAndNotMappedToTable( + (IEntityType)index.DeclaringEntityType, + (IIndex)index, + propertyNotMappedToAnyTable!.Name); + } + } + else if (overlappingTables.Count == 0) + { + Check.DebugAssert(firstPropertyTables != null, nameof(firstPropertyTables)); + Check.DebugAssert(lastPropertyTables != null, nameof(lastPropertyTables)); + + logger.IndexPropertiesMappedToNonOverlappingTables( + (IEntityType)index.DeclaringEntityType, + (IIndex)index, + firstPropertyTables.Value.Item1, + firstPropertyTables.Value.Item2.Select(t => (t.Name, t.Schema)).ToList(), + lastPropertyTables.Value.Item1, + lastPropertyTables.Value.Item2.Select(t => (t.Name, t.Schema)).ToList()); + } + } + + return null; + } + + var rootIndex = index; + + // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) + // Using a hashset is detrimental to the perf when there are no cycles + for (var i = 0; i < RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) + { + IReadOnlyIndex? linkedIndex = null; + foreach (var otherIndex in rootIndex.DeclaringEntityType + .FindRowInternalForeignKeys(storeObject) + .SelectMany(fk => fk.PrincipalEntityType.GetIndexes())) + { + var otherColumnNames = otherIndex.Properties.GetColumnNames(storeObject); + if ((otherColumnNames != null) + && otherColumnNames.SequenceEqual(columnNames)) + { + linkedIndex = otherIndex; + break; + } + } + + if (linkedIndex == null) + { + break; + } + + rootIndex = linkedIndex; + } + + if (rootIndex != index) + { + return rootIndex.GetDatabaseName(storeObject); + } + + var baseName = new StringBuilder() + .Append("IX_") + .Append(storeObject.Name) + .Append('_') + .AppendJoin(columnNames, "_") + .ToString(); + + return Uniquifier.Truncate(baseName, index.DeclaringEntityType.Model.GetMaxIdentifierLength()); + } } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs b/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs index a63c9829f10..a50faacc0d4 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalKeyExtensions.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text; + namespace Microsoft.EntityFrameworkCore.Metadata.Internal; /// @@ -65,4 +67,157 @@ public static bool AreCompatible( return true; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetName( + this IReadOnlyKey key, + in StoreObjectIdentifier storeObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + var defaultName = key.GetDefaultName(storeObject, logger); + var declaringType = key.DeclaringEntityType; + var fragment = declaringType.FindMappingFragment(storeObject); + if (fragment != null) + { + return defaultName != null + ? (string?)key[RelationalAnnotationNames.Name] ?? defaultName + : null; + } + + foreach (var containingType in declaringType.GetDerivedTypesInclusive()) + { + if (StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType) == storeObject) + { + return defaultName != null + ? (string?)key[RelationalAnnotationNames.Name] ?? defaultName + : null; + } + } + + return null; + } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public static string? GetDefaultName( + this IReadOnlyKey key, + in StoreObjectIdentifier storeObject, + IDiagnosticsLogger? logger) + { + if (storeObject.StoreObjectType != StoreObjectType.Table) + { + return null; + } + + string? name; + if (key.IsPrimaryKey()) + { + var rootKey = key; + // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) + // Using a hashset is detrimental to the perf when there are no cycles + for (var i = 0; i < RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) + { + var linkingFk = rootKey!.DeclaringEntityType.FindRowInternalForeignKeys(storeObject) + .FirstOrDefault(); + if (linkingFk == null) + { + break; + } + + rootKey = linkingFk.PrincipalEntityType.FindPrimaryKey(); + } + + if (rootKey != null + && rootKey != key) + { + return rootKey.GetName(storeObject); + } + + name = "PK_" + storeObject.Name; + } + else + { + var columnNames = key.Properties.GetColumnNames(storeObject); + if (columnNames == null) + { + if (logger != null) + { + var table = storeObject; + if (key.DeclaringEntityType.GetMappingFragments(StoreObjectType.Table) + .Any(t => t.StoreObject != table && key.Properties.GetColumnNames(t.StoreObject) != null)) + { + return null; + } + + if (key.DeclaringEntityType.GetMappingStrategy() != RelationalAnnotationNames.TphMappingStrategy + && key.DeclaringEntityType.GetDerivedTypes() + .Select(e => StoreObjectIdentifier.Create(e, StoreObjectType.Table)) + .Any(t => t != null && key.Properties.GetColumnNames(t.Value) != null)) + { + return null; + } + + logger.KeyUnmappedProperties((IKey)key); + } + + return null; + } + + var rootKey = key; + + // Limit traversal to avoid getting stuck in a cycle (validation will throw for these later) + // Using a hashset is detrimental to the perf when there are no cycles + for (var i = 0; i < RelationalEntityTypeExtensions.MaxEntityTypesSharingTable; i++) + { + IReadOnlyKey? linkedKey = null; + foreach (var otherKey in rootKey.DeclaringEntityType + .FindRowInternalForeignKeys(storeObject) + .SelectMany(fk => fk.PrincipalEntityType.GetKeys())) + { + var otherColumnNames = otherKey.Properties.GetColumnNames(storeObject); + if ((otherColumnNames != null) + && otherColumnNames.SequenceEqual(columnNames)) + { + linkedKey = otherKey; + break; + } + } + + if (linkedKey == null) + { + break; + } + + rootKey = linkedKey; + } + + if (rootKey != key) + { + return rootKey.GetName(storeObject); + } + + name = new StringBuilder() + .Append("AK_") + .Append(storeObject.Name) + .Append('_') + .AppendJoin(columnNames, "_") + .ToString(); + } + + return Uniquifier.Truncate(name, key.DeclaringEntityType.Model.GetMaxIdentifierLength()); + } } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs index 52fdf3dc224..6bce5b2c861 100644 --- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs +++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs @@ -273,12 +273,7 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp databaseModel.DefaultTables.Add(mappedTableName, defaultTable); } - var tableMapping = new TableMappingBase(entityType, defaultTable, includesDerivedTypes: !isTpc && mappedType == entityType) - { - // Table splitting is not supported for default mapping - IsSharedTablePrincipal = true, - IsSplitEntityTypePrincipal = true - }; + var tableMapping = new TableMappingBase(entityType, defaultTable, includesDerivedTypes: !isTpc && mappedType == entityType); foreach (var property in entityType.GetProperties()) { @@ -338,8 +333,7 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp private static void AddTables(RelationalModel databaseModel, IEntityType entityType) { - var tableName = entityType.GetTableName(); - if (tableName == null) + if (entityType.GetTableName() == null) { return; } @@ -350,8 +344,8 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT var tableMappings = new List(); entityType.SetRuntimeAnnotation(RelationalAnnotationNames.TableMappings, tableMappings); - var isTpc = entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy; - var isTph = entityType.FindDiscriminatorProperty() != null; + var mappingStrategy = entityType.GetMappingStrategy(); + var isTpc = mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy; while (mappedType != null) { var mappedTableName = mappedType.GetTableName(); @@ -368,75 +362,107 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT continue; } - if (!databaseModel.Tables.TryGetValue((mappedTableName, mappedSchema), out var table)) + foreach (var fragment in mappedType.GetMappingFragments(StoreObjectType.Table)) { - table = new Table(mappedTableName, mappedSchema, databaseModel); - databaseModel.Tables.Add((mappedTableName, mappedSchema), table); + CreateTableMapping( + entityType, + mappedType, + fragment.StoreObject, + databaseModel, + tableMappings, + includesDerivedTypes: !isTpc && mappedType == entityType, + isSplitEntityTypePrincipal: false); } - var mappedTable = StoreObjectIdentifier.Table(mappedTableName, mappedSchema); - var tableMapping = new TableMapping(entityType, table, includesDerivedTypes: !isTpc && mappedType == entityType) - { - IsSplitEntityTypePrincipal = true - }; - foreach (var property in mappedType.GetProperties()) + CreateTableMapping( + entityType, + mappedType, + StoreObjectIdentifier.Table(mappedTableName, mappedSchema), + databaseModel, + tableMappings, + includesDerivedTypes: !isTpc && mappedType == entityType, + isSplitEntityTypePrincipal: mappedType.GetMappingFragments(StoreObjectType.Table).Any() ? true : null); + + if (isTpc || mappingStrategy == RelationalAnnotationNames.TphMappingStrategy) { - var columnName = property.GetColumnName(mappedTable); - if (columnName == null) - { - continue; - } + break; + } - var column = (Column?)table.FindColumn(columnName); - if (column == null) - { - column = new(columnName, property.GetColumnType(mappedTable), table) - { - IsNullable = property.IsColumnNullable(mappedTable) - }; - table.Columns.Add(columnName, column); - } - else if (!property.IsColumnNullable(mappedTable)) - { - column.IsNullable = false; - } + mappedType = mappedType.BaseType; + } - var columnMapping = new ColumnMapping(property, column, tableMapping); - tableMapping.AddColumnMapping(columnMapping); - column.AddPropertyMapping(columnMapping); + tableMappings.Reverse(); + } - if (property.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableColumnMappings) - is not SortedSet columnMappings) - { - columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance); - property.AddRuntimeAnnotation(RelationalAnnotationNames.TableColumnMappings, columnMappings); - } + private static TableMapping CreateTableMapping( + IEntityType entityType, + IEntityType mappedType, + StoreObjectIdentifier mappedTable, + RelationalModel databaseModel, + List tableMappings, + bool includesDerivedTypes, + bool? isSplitEntityTypePrincipal = null) + { + if (!databaseModel.Tables.TryGetValue((mappedTable.Name, mappedTable.Schema), out var table)) + { + table = new Table(mappedTable.Name, mappedTable.Schema, databaseModel); + databaseModel.Tables.Add((mappedTable.Name, mappedTable.Schema), table); + } - columnMappings.Add(columnMapping); + var tableMapping = new TableMapping(entityType, table, includesDerivedTypes) + { + IsSplitEntityTypePrincipal = isSplitEntityTypePrincipal + }; + + foreach (var property in mappedType.GetProperties()) + { + var columnName = property.GetColumnName(mappedTable); + if (columnName == null) + { + continue; } - if (((ITableMappingBase)tableMapping).ColumnMappings.Any() - || tableMappings.Count == 0) + var column = (Column?)table.FindColumn(columnName); + if (column == null) { - tableMappings.Add(tableMapping); - table.EntityTypeMappings.Add(tableMapping); + column = new(columnName, property.GetColumnType(mappedTable), table) + { + IsNullable = property.IsColumnNullable(mappedTable) + }; + table.Columns.Add(columnName, column); + } + else if (!property.IsColumnNullable(mappedTable)) + { + column.IsNullable = false; } - if (isTpc || isTph) + var columnMapping = new ColumnMapping(property, column, tableMapping); + tableMapping.AddColumnMapping(columnMapping); + column.AddPropertyMapping(columnMapping); + + if (property.FindRuntimeAnnotationValue(RelationalAnnotationNames.TableColumnMappings) + is not SortedSet columnMappings) { - break; + columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance); + property.AddRuntimeAnnotation(RelationalAnnotationNames.TableColumnMappings, columnMappings); } - mappedType = mappedType.BaseType; + columnMappings.Add(columnMapping); } - tableMappings.Reverse(); + if (((ITableMappingBase)tableMapping).ColumnMappings.Any() + || tableMappings.Count == 0) + { + tableMappings.Add(tableMapping); + table.EntityTypeMappings.Add(tableMapping); + } + + return tableMapping; } private static void AddViews(RelationalModel databaseModel, IEntityType entityType) { - var viewName = entityType.GetViewName(); - if (viewName == null) + if (entityType.GetViewName() == null) { return; } @@ -447,8 +473,8 @@ private static void AddViews(RelationalModel databaseModel, IEntityType entityTy var viewMappings = new List(); entityType.SetRuntimeAnnotation(RelationalAnnotationNames.ViewMappings, viewMappings); - var isTpc = entityType.GetMappingStrategy() == RelationalAnnotationNames.TpcMappingStrategy; - var isTph = entityType.FindDiscriminatorProperty() != null; + var mappingStrategy = entityType.GetMappingStrategy(); + var isTpc = mappingStrategy == RelationalAnnotationNames.TpcMappingStrategy; while (mappedType != null) { var mappedViewName = mappedType.GetViewName(); @@ -465,69 +491,99 @@ private static void AddViews(RelationalModel databaseModel, IEntityType entityTy continue; } - if (!databaseModel.Views.TryGetValue((mappedViewName, mappedSchema), out var view)) + foreach (var fragment in mappedType.GetMappingFragments(StoreObjectType.View)) { - view = new View(mappedViewName, mappedSchema, databaseModel); - databaseModel.Views.Add((mappedViewName, mappedSchema), view); + CreateViewMapping( + entityType, + mappedType, + fragment.StoreObject, + databaseModel, + viewMappings, + includesDerivedTypes: !isTpc && mappedType == entityType, + isSplitEntityTypePrincipal: false); } - var mappedView = StoreObjectIdentifier.View(mappedViewName, mappedSchema); - var viewMapping = new ViewMapping(entityType, view, includesDerivedTypes: !isTpc && mappedType == entityType) - { - IsSplitEntityTypePrincipal = true - }; - foreach (var property in mappedType.GetProperties()) + CreateViewMapping( + entityType, + mappedType, + StoreObjectIdentifier.View(mappedViewName, mappedSchema), + databaseModel, + viewMappings, + includesDerivedTypes: !isTpc && mappedType == entityType, + isSplitEntityTypePrincipal: mappedType.GetMappingFragments(StoreObjectType.View).Any() ? true : null); + + if (isTpc || mappingStrategy == RelationalAnnotationNames.TphMappingStrategy) { - var columnName = property.GetColumnName(mappedView); - if (columnName == null) - { - continue; - } + break; + } - var column = (ViewColumn?)view.FindColumn(columnName); - if (column == null) - { - column = new ViewColumn(columnName, property.GetColumnType(mappedView), view) - { - IsNullable = property.IsColumnNullable(mappedView) - }; - view.Columns.Add(columnName, column); - } - else if (!property.IsColumnNullable(mappedView)) - { - column.IsNullable = false; - } + mappedType = mappedType.BaseType; + } - var columnMapping = new ViewColumnMapping(property, column, viewMapping); - viewMapping.AddColumnMapping(columnMapping); - column.AddPropertyMapping(columnMapping); + viewMappings.Reverse(); + } - if (property.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewColumnMappings) - is not SortedSet columnMappings) - { - columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance); - property.AddRuntimeAnnotation(RelationalAnnotationNames.ViewColumnMappings, columnMappings); - } + private static void CreateViewMapping( + IEntityType entityType, + IEntityType mappedType, + StoreObjectIdentifier mappedView, + RelationalModel databaseModel, + List viewMappings, + bool includesDerivedTypes, + bool? isSplitEntityTypePrincipal = null) + { + if (!databaseModel.Views.TryGetValue((mappedView.Name, mappedView.Schema), out var view)) + { + view = new View(mappedView.Name, mappedView.Schema, databaseModel); + databaseModel.Views.Add((mappedView.Name, mappedView.Schema), view); + } - columnMappings.Add(columnMapping); + var viewMapping = new ViewMapping(entityType, view, includesDerivedTypes) + { + IsSplitEntityTypePrincipal = isSplitEntityTypePrincipal + }; + foreach (var property in mappedType.GetProperties()) + { + var columnName = property.GetColumnName(mappedView); + if (columnName == null) + { + continue; } - if (((ITableMappingBase)viewMapping).ColumnMappings.Any() - || viewMappings.Count == 0) + var column = (ViewColumn?)view.FindColumn(columnName); + if (column == null) + { + column = new ViewColumn(columnName, property.GetColumnType(mappedView), view) + { + IsNullable = property.IsColumnNullable(mappedView) + }; + view.Columns.Add(columnName, column); + } + else if (!property.IsColumnNullable(mappedView)) { - viewMappings.Add(viewMapping); - view.EntityTypeMappings.Add(viewMapping); + column.IsNullable = false; } - if (isTpc || isTph) + var columnMapping = new ViewColumnMapping(property, column, viewMapping); + viewMapping.AddColumnMapping(columnMapping); + column.AddPropertyMapping(columnMapping); + + if (property.FindRuntimeAnnotationValue(RelationalAnnotationNames.ViewColumnMappings) + is not SortedSet columnMappings) { - break; + columnMappings = new SortedSet(ColumnMappingBaseComparer.Instance); + property.AddRuntimeAnnotation(RelationalAnnotationNames.ViewColumnMappings, columnMappings); } - mappedType = mappedType.BaseType; + columnMappings.Add(columnMapping); } - viewMappings.Reverse(); + if (((ITableMappingBase)viewMapping).ColumnMappings.Any() + || viewMappings.Count == 0) + { + viewMappings.Add(viewMapping); + view.EntityTypeMappings.Add(viewMapping); + } } private static void AddSqlQueries(RelationalModel databaseModel, IEntityType entityType) @@ -576,9 +632,7 @@ private static void AddSqlQueries(RelationalModel databaseModel, IEntityType ent var queryMapping = new SqlQueryMapping(entityType, sqlQuery, includesDerivedTypes: true) { - IsDefaultSqlQueryMapping = true, - IsSharedTablePrincipal = true, - IsSplitEntityTypePrincipal = true + IsDefaultSqlQueryMapping = true }; foreach (var property in mappedType.GetProperties()) @@ -725,10 +779,7 @@ private static FunctionMapping CreateFunctionMapping( var mappedFunction = StoreObjectIdentifier.DbFunction(dbFunction.Name); var functionMapping = new FunctionMapping(entityType, storeFunction, dbFunction, includesDerivedTypes: true) { - IsDefaultFunctionMapping = @default, - // See Issue #19970 - IsSharedTablePrincipal = true, - IsSplitEntityTypePrincipal = true + IsDefaultFunctionMapping = @default }; foreach (var property in mappedType.GetProperties()) @@ -947,6 +998,11 @@ private static void PopulateRowInternalForeignKeys(TableBase tab var mappedEntityTypes = new HashSet(); foreach (TableMappingBase entityTypeMapping in table.EntityTypeMappings) { + if (table.EntityTypeMappings.Count > 1) + { + entityTypeMapping.IsSharedTablePrincipal = false; + } + var entityType = entityTypeMapping.EntityType; mappedEntityTypes.Add(entityType); var primaryKey = entityType.FindPrimaryKey(); @@ -1015,10 +1071,13 @@ private static void PopulateRowInternalForeignKeys(TableBase tab mainMapping is not null, $"{nameof(mainMapping)} is neither a {nameof(TableMapping)} nor a {nameof(ViewMapping)}"); - // Re-add the mapping to update the order - mainMapping.Table.EntityTypeMappings.Remove(mainMapping); - mainMapping.IsSharedTablePrincipal = true; - mainMapping.Table.EntityTypeMappings.Add(mainMapping); + if (table.EntityTypeMappings.Count > 1) + { + // Re-add the mapping to update the order + mainMapping.Table.EntityTypeMappings.Remove(mainMapping); + mainMapping.IsSharedTablePrincipal = true; + mainMapping.Table.EntityTypeMappings.Add(mainMapping); + } if (referencingInternalForeignKeyMap != null) { @@ -1065,7 +1124,7 @@ private static void PopulateRowInternalForeignKeys(TableBase tab } else { - table.OptionalEntityTypes = table.EntityTypeMappings.ToDictionary(etm => etm.EntityType, et => false); + table.OptionalEntityTypes = table.EntityTypeMappings.ToDictionary(etm => etm.EntityType, _ => false); } } @@ -1083,20 +1142,8 @@ private static void PopulateForeignKeyConstraints(Table table) var entityType = entityTypeMapping.EntityType; foreach (var foreignKey in entityType.GetForeignKeys()) { - var firstPrincipalMapping = true; foreach (var principalMapping in foreignKey.PrincipalEntityType.GetTableMappings().Reverse()) { - if (firstPrincipalMapping - && !principalMapping.IncludesDerivedTypes - && foreignKey.PrincipalEntityType.GetDirectlyDerivedTypes().Any(e => e.GetTableMappings().Any())) - { - // Derived principal entity types are mapped to different tables, so the constraint is not enforceable - // TODO: Allow this to be overriden #15854 - break; - } - - firstPrincipalMapping = false; - var principalTable = (Table)principalMapping.Table; var principalStoreObject = StoreObjectIdentifier.Table(principalTable.Name, principalTable.Schema); var name = foreignKey.GetConstraintName(storeObject, principalStoreObject); @@ -1138,6 +1185,7 @@ private static void PopulateForeignKeyConstraints(Table table) if (principalColumns == null) { + Check.DebugAssert(false, "Should not get here if name is not null"); continue; } @@ -1155,14 +1203,10 @@ private static void PopulateForeignKeyConstraints(Table table) } } - if (columns == null) - { - break; - } - - if (columns.SequenceEqual(principalColumns)) + if (columns == null + || columns.SequenceEqual(principalColumns)) { - // Principal and dependent properties are mapped to the same columns so the constraint is redundant + Check.DebugAssert(false, "Should not get here if name is not null"); break; } @@ -1171,8 +1215,7 @@ private static void PopulateForeignKeyConstraints(Table table) && 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 - // to the declaring entity type + Check.DebugAssert(false, "Should not get here if name is not null"); break; } diff --git a/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs b/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs index 619a422333d..6cde87ddd45 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableMappingBase.cs @@ -80,10 +80,10 @@ public virtual bool AddColumnMapping(TColumnMapping columnMapping) public virtual bool IncludesDerivedTypes { get; } /// - public virtual bool IsSharedTablePrincipal { get; set; } + public virtual bool? IsSharedTablePrincipal { get; set; } /// - public virtual bool IsSplitEntityTypePrincipal { get; set; } + public virtual bool? IsSplitEntityTypePrincipal { get; set; } IEnumerable ITableMappingBase.ColumnMappings { diff --git a/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs b/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs index a445e6600e2..4a9f4ebefd1 100644 --- a/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs +++ b/src/EFCore.Relational/Metadata/Internal/TableMappingBaseComparer.cs @@ -47,22 +47,53 @@ public int Compare(ITableMappingBase? x, ITableMappingBase? y) return 1; } - var result = y.IsSharedTablePrincipal.CompareTo(x.IsSharedTablePrincipal); - if (result != 0) + var result = 0; + if (y.IsSharedTablePrincipal == null) { - return result; + if (x.IsSharedTablePrincipal != null) + { + return 1; + } } - + else + { + if (x.IsSharedTablePrincipal == null) + { + return -1; + } + + result = y.IsSharedTablePrincipal.Value.CompareTo(x.IsSharedTablePrincipal.Value); + if (result != 0) + { + return result; + } + } + result = y.IncludesDerivedTypes.CompareTo(x.IncludesDerivedTypes); if (result != 0) { return result; } - result = y.IsSplitEntityTypePrincipal.CompareTo(x.IsSplitEntityTypePrincipal); - if (result != 0) + if (y.IsSplitEntityTypePrincipal == null) { - return result; + if (x.IsSplitEntityTypePrincipal != null) + { + return 1; + } + } + else + { + if (x.IsSplitEntityTypePrincipal == null) + { + return -1; + } + + result = y.IsSplitEntityTypePrincipal.Value.CompareTo(x.IsSplitEntityTypePrincipal.Value); + if (result != 0) + { + return result; + } } result = EntityTypeFullNameComparer.Instance.Compare(x.EntityType, y.EntityType); diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index dc97a4c75b4..1ef190f855d 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -2396,7 +2396,7 @@ protected virtual IEnumerable GetSchemas(IRelationalModel model) => (property.FindRelationalTypeMapping() ?? typeMapping)?.Converter; private static IEntityType GetMainType(ITable table) - => table.EntityTypeMappings.First(t => t.IsSharedTablePrincipal).EntityType; + => table.EntityTypeMappings.First(t => t.IsSharedTablePrincipal ?? true).EntityType; private static object?[,] ToMultidimensionalArray(IReadOnlyList values) { diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs index 6fefaeab639..eb87d469013 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs +++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs @@ -661,6 +661,14 @@ public static string EntitySplittingUnmappedMainFragment(object? entityType, obj GetString("EntitySplittingUnmappedMainFragment", nameof(entityType), nameof(storeObject), nameof(storeObjectType)), entityType, storeObject, storeObjectType); + /// + /// Entity type '{entityType}' has a split mapping for '{storeObject}' that shares the table with '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to a table that '{principalEntityType}' is also mapped to. + /// + public static string EntitySplittingUnmatchedMainTableSplitting(object? entityType, object? storeObject, object? principalEntityType) + => string.Format( + GetString("EntitySplittingUnmatchedMainTableSplitting", nameof(entityType), nameof(storeObject), nameof(principalEntityType)), + entityType, storeObject, principalEntityType); + /// /// An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information. /// @@ -2470,6 +2478,31 @@ public static EventDefinition LogKeyHasDefaultValue(IDiagnostics return (EventDefinition)definition; } + /// + /// The key {keyProperties} on the entity type '{entityType}' cannot be represented in the database. Either all or some of the properties aren't mapped to table '{table}'. All key properties must be mapped to a single table for the unique constraint to be created. + /// + public static EventDefinition LogKeyUnmappedProperties(IDiagnosticsLogger logger) + { + var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogKeyUnmappedProperties; + if (definition == null) + { + definition = NonCapturingLazyInitializer.EnsureInitialized( + ref ((RelationalLoggingDefinitions)logger.Definitions).LogKeyUnmappedProperties, + logger, + static logger => new EventDefinition( + logger.Options, + RelationalEventId.KeyUnmappedProperties, + LogLevel.Error, + "RelationalEventId.KeyUnmappedProperties", + level => LoggerMessage.Define( + level, + RelationalEventId.KeyUnmappedProperties, + _resourceManager.GetString("LogKeyUnmappedProperties")!))); + } + + return (EventDefinition)definition; + } + /// /// Migrating using database '{database}' on server '{dataSource}'. /// diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx index 472df3e9f01..3645da60074 100644 --- a/src/EFCore.Relational/Properties/RelationalStrings.resx +++ b/src/EFCore.Relational/Properties/RelationalStrings.resx @@ -361,6 +361,9 @@ Entity type '{entityType}' has a split mapping for '{storeObject}', but it doesn't have a main mapping of the same type. Map '{entityType}' to '{storeObjectType}'. + + Entity type '{entityType}' has a split mapping for '{storeObject}' that shares the table with '{principalEntityType}', but the main mappings of these types do not share a table. Map the split fragments of '{entityType}' to non-shared tables or map the main fragment to a table that '{principalEntityType}' is also mapped to. + An error occurred while reading a database value for property '{entityType}.{property}'. See the inner exception for more information. @@ -616,6 +619,10 @@ Property '{property}' on entity type '{entityType}' is part of a primary or alternate key, but has a constant default value set. Constant default values are not useful for primary or alternate keys since these properties must always have non-null unique values. Warning RelationalEventId.ModelValidationKeyDefaultValueWarning string string + + The key {keyProperties} on the entity type '{entityType}' cannot be represented in the database. Either all or some of the properties aren't mapped to table '{table}'. All key properties must be mapped to a single table for the unique constraint to be created. + Error RelationalEventId.KeyUnmappedProperties string string string + Migrating using database '{database}' on server '{dataSource}'. Debug RelationalEventId.MigrateUsingConnection string string diff --git a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs index 529f038944f..7739f408730 100644 --- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs +++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerModelValidator.cs @@ -60,8 +60,7 @@ protected virtual void ValidateDecimalColumns( { foreach (IConventionProperty property in model.GetEntityTypes() .SelectMany(t => t.GetDeclaredProperties()) - .Where( - p => p.ClrType.UnwrapNullableType() == typeof(decimal) + .Where(p => p.ClrType.UnwrapNullableType() == typeof(decimal) && !p.IsForeignKey())) { var valueConverterConfigurationSource = property.GetValueConverterConfigurationSource(); @@ -314,8 +313,7 @@ private static void ValidateTemporalPeriodProperty(IEntityType temporalEntityTyp /// protected override void ValidateSharedTableCompatibility( IReadOnlyList mappedTypes, - string tableName, - string? schema, + in StoreObjectIdentifier storeObject, IDiagnosticsLogger logger) { var firstMappedType = mappedTypes[0]; @@ -326,7 +324,7 @@ protected override void ValidateSharedTableCompatibility( { throw new InvalidOperationException( SqlServerStrings.IncompatibleTableMemoryOptimizedMismatch( - tableName, firstMappedType.DisplayName(), otherMappedType.DisplayName(), + storeObject.DisplayName(), firstMappedType.DisplayName(), otherMappedType.DisplayName(), isMemoryOptimized ? firstMappedType.DisplayName() : otherMappedType.DisplayName(), !isMemoryOptimized ? firstMappedType.DisplayName() : otherMappedType.DisplayName())); } @@ -354,10 +352,9 @@ protected override void ValidateSharedTableCompatibility( var periodStartProperty = mappedType.GetProperty(periodStartPropertyName!); var periodEndProperty = mappedType.GetProperty(periodEndPropertyName!); - - var storeObjectIdentifier = StoreObjectIdentifier.Table(tableName, mappedType.GetSchema()); - var periodStartColumnName = periodStartProperty.GetColumnName(storeObjectIdentifier); - var periodEndColumnName = periodEndProperty.GetColumnName(storeObjectIdentifier); + + var periodStartColumnName = periodStartProperty.GetColumnName(storeObject); + var periodEndColumnName = periodEndProperty.GetColumnName(storeObject); if (expectedPeriodStartColumnName == null) { @@ -391,7 +388,7 @@ protected override void ValidateSharedTableCompatibility( } } - base.ValidateSharedTableCompatibility(mappedTypes, tableName, schema, logger); + base.ValidateSharedTableCompatibility(mappedTypes, storeObject, logger); } /// diff --git a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs index e8c2670cd64..fd9f2604c1f 100644 --- a/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs +++ b/src/EFCore.SqlServer/Metadata/Internal/SqlServerAnnotationProvider.cs @@ -219,7 +219,8 @@ public override IEnumerable For(IColumn column, bool designTime) var table = StoreObjectIdentifier.Table(column.Table.Name, column.Table.Schema); var identityProperty = column.PropertyMappings.Where( - m => m.TableMapping.IsSharedTablePrincipal && m.TableMapping.EntityType == m.Property.DeclaringEntityType) + m => (m.TableMapping.IsSharedTablePrincipal ?? true) + && m.TableMapping.EntityType == m.Property.DeclaringEntityType) .Select(m => m.Property) .FirstOrDefault( p => p.GetValueGenerationStrategy(table) diff --git a/src/EFCore/Diagnostics/KeyEventData.cs b/src/EFCore/Diagnostics/KeyEventData.cs new file mode 100644 index 00000000000..0755183e20c --- /dev/null +++ b/src/EFCore/Diagnostics/KeyEventData.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Diagnostics; + +/// +/// A event payload class for events that have a key. +/// +/// +/// See Logging, events, and diagnostics for more information and examples. +/// +public class KeyEventData : EventData +{ + /// + /// Constructs the event payload. + /// + /// The event definition. + /// A delegate that generates a log message for this event. + /// The key. + public KeyEventData( + EventDefinitionBase eventDefinition, + Func messageGenerator, + IReadOnlyKey key) + : base(eventDefinition, messageGenerator) + { + Key = key; + } + + /// + /// The foreign key. + /// + public virtual IReadOnlyKey Key { get; } +} diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index 69e622a108b..7f6554003cb 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -684,6 +684,8 @@ protected virtual void ValidateOwnership( foreach (var referencingFk in entityType.GetReferencingForeignKeys().Where( fk => !fk.IsOwnership + && (fk.PrincipalEntityType != fk.DeclaringEntityType + || !fk.Properties.SequenceEqual(entityType.FindPrimaryKey()!.Properties)) && !Contains(fk.DeclaringEntityType.FindOwnership(), fk))) { throw new InvalidOperationException( @@ -738,8 +740,7 @@ protected virtual void ValidateForeignKeys( { foreach (var declaredForeignKey in entityType.GetDeclaredForeignKeys()) { - if (declaredForeignKey.PrincipalEntityType == declaredForeignKey.DeclaringEntityType - && declaredForeignKey.PrincipalKey.Properties.SequenceEqual(declaredForeignKey.Properties)) + if (IsRedundant(declaredForeignKey)) { logger.RedundantForeignKeyWarning(declaredForeignKey); } @@ -783,6 +784,15 @@ static bool ContainedInForeignKeyForAllConcreteTypes(IEntityType entityType, IPr .Any(fk => fk.Properties.Contains(property))); } + /// + /// Returns a value indicating whether the given foreign key is redundant. + /// + /// A foreign key. + /// A value indicating whether the given foreign key is redundant. + protected virtual bool IsRedundant(IForeignKey foreignKey) + => foreignKey.PrincipalEntityType == foreignKey.DeclaringEntityType + && foreignKey.PrincipalKey.Properties.SequenceEqual(foreignKey.Properties); + /// /// Validates the mapping/configuration of properties mapped to fields in the model. /// diff --git a/src/EFCore/Metadata/IReadOnlyIndex.cs b/src/EFCore/Metadata/IReadOnlyIndex.cs index f7ab17a9a4a..f061bc98189 100644 --- a/src/EFCore/Metadata/IReadOnlyIndex.cs +++ b/src/EFCore/Metadata/IReadOnlyIndex.cs @@ -82,7 +82,10 @@ string ToDebugString(MetadataDebugStringOptions options = MetadataDebugStringOpt ? p.DeclaringEntityType.DisplayName(omitSharedType: true) + "." + p.Name : p.Name)); - builder.Append(" " + (Name ?? "")); + if (Name != null) + { + builder.Append(" " + Name); + } if (IsUnique) { diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 3679a32197b..4bda85d0126 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -1170,7 +1170,7 @@ public static bool AreCompatible(IReadOnlyList properties, EntityType /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public override string ToString() - => this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault); + => ((IReadOnlyProperty)this).ToDebugString(MetadataDebugStringOptions.SingleLineDefault); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -1180,8 +1180,8 @@ public override string ToString() /// public virtual DebugView DebugView => new( - () => this.ToDebugString(), - () => this.ToDebugString(MetadataDebugStringOptions.LongDefault)); + () => ((IReadOnlyProperty)this).ToDebugString(), + () => ((IReadOnlyProperty)this).ToDebugString(MetadataDebugStringOptions.LongDefault)); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/Internal/PropertyExtensions.cs b/src/EFCore/Metadata/Internal/PropertyExtensions.cs index f0714f44d7f..c14d32bfad4 100644 --- a/src/EFCore/Metadata/Internal/PropertyExtensions.cs +++ b/src/EFCore/Metadata/Internal/PropertyExtensions.cs @@ -151,16 +151,4 @@ public static bool RequiresOriginalValue(this IReadOnlyProperty property) || property.IsKey() || property.IsForeignKey() || property.IsUniqueIndex(); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public static string ToDebugString( - this Property property, - MetadataDebugStringOptions options = MetadataDebugStringOptions.ShortDefault, - int indent = 0) - => ((IReadOnlyProperty)property).ToDebugString(options, indent); } diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestHelpers.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestHelpers.cs index 29fdfb3520a..0425cd294e4 100644 --- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestHelpers.cs +++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestHelpers.cs @@ -16,7 +16,7 @@ protected CosmosTestHelpers() public override IServiceCollection AddProviderServices(IServiceCollection services) => services.AddEntityFrameworkCosmos(); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseCosmos( TestEnvironment.DefaultConnection, TestEnvironment.AuthToken, diff --git a/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs index 706e2152c50..deb8a117c46 100644 --- a/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs +++ b/test/EFCore.Cosmos.Tests/Infrastructure/CosmosModelValidatorTest.cs @@ -10,7 +10,7 @@ public class CosmosModelValidatorTest : ModelValidatorTestBase [ConditionalFact] public virtual void Passes_on_valid_model() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); Validate(modelBuilder); @@ -19,7 +19,7 @@ public virtual void Passes_on_valid_model() [ConditionalFact] public virtual void Passes_on_valid_keyless_entity_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasPartitionKey(c => c.PartitionId).HasNoKey(); var model = Validate(modelBuilder); @@ -87,7 +87,7 @@ public virtual void Detects_non_string_id_property() [ConditionalFact] public virtual void Passes_on_valid_partition_keys() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId) .HasAnalyticalStoreTimeToLive(-1) .HasDefaultTimeToLive(100) @@ -101,7 +101,7 @@ public virtual void Passes_on_valid_partition_keys() [ConditionalFact] public virtual void Passes_PK_partition_key() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( b => { @@ -137,7 +137,7 @@ public virtual void Detects_non_key_partition_key_property() [ConditionalFact] public virtual void Detects_missing_partition_key_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasPartitionKey("PartitionKey"); @@ -147,7 +147,7 @@ public virtual void Detects_missing_partition_key_property() [ConditionalFact] public virtual void Detects_missing_partition_key_on_first_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders"); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId); @@ -157,7 +157,7 @@ public virtual void Detects_missing_partition_key_on_first_type() [ConditionalFact] public virtual void Detects_missing_partition_keys_one_last_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId); modelBuilder.Entity().ToContainer("Orders"); @@ -167,7 +167,7 @@ public virtual void Detects_missing_partition_keys_one_last_type() [ConditionalFact] public virtual void Detects_partition_keys_mapped_to_different_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId) .Property(c => c.PartitionId).ToJsonProperty("pk"); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId); @@ -181,7 +181,7 @@ public virtual void Detects_partition_keys_mapped_to_different_properties() [ConditionalFact] public virtual void Detects_partition_key_of_different_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(c => c.PartitionId); modelBuilder.Entity().ToContainer("Orders").HasPartitionKey(o => o.PartitionId) .Property(c => c.PartitionId).HasConversion(); @@ -194,7 +194,7 @@ public virtual void Detects_partition_key_of_different_type() [ConditionalFact] public virtual void Detects_conflicting_analytical_ttl() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders") .HasAnalyticalStoreTimeToLive(-1); modelBuilder.Entity().ToContainer("Orders") @@ -206,7 +206,7 @@ public virtual void Detects_conflicting_analytical_ttl() [ConditionalFact] public virtual void Detects_conflicting_default_ttl() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders") .HasDefaultTimeToLive(100); modelBuilder.Entity().ToContainer("Orders") @@ -218,7 +218,7 @@ public virtual void Detects_conflicting_default_ttl() [ConditionalFact] public virtual void Detects_conflicting_throughput() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders") .HasAutoscaleThroughput(200); modelBuilder.Entity().ToContainer("Orders") @@ -230,7 +230,7 @@ public virtual void Detects_conflicting_throughput() [ConditionalFact] public virtual void Detects_conflicting_throughput_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders") .HasManualThroughput(200); modelBuilder.Entity().ToContainer("Orders") @@ -242,7 +242,7 @@ public virtual void Detects_conflicting_throughput_type() [ConditionalFact] public virtual void Detects_properties_mapped_to_same_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( ob => @@ -259,7 +259,7 @@ public virtual void Detects_properties_mapped_to_same_property() [ConditionalFact] public virtual void Detects_property_and_embedded_type_mapped_to_same_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( ob => @@ -276,7 +276,7 @@ public virtual void Detects_property_and_embedded_type_mapped_to_same_property() [ConditionalFact] public virtual void Detects_missing_discriminator() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasNoDiscriminator(); modelBuilder.Entity().ToContainer("Orders"); @@ -286,7 +286,7 @@ public virtual void Detects_missing_discriminator() [ConditionalFact] public virtual void Detects_missing_discriminator_value() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasDiscriminator().HasValue(null); modelBuilder.Entity().ToContainer("Orders"); @@ -296,7 +296,7 @@ public virtual void Detects_missing_discriminator_value() [ConditionalFact] public virtual void Detects_duplicate_discriminator_values() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToContainer("Orders").HasDiscriminator().HasValue("type"); modelBuilder.Entity().ToContainer("Orders").HasDiscriminator().HasValue("type"); @@ -307,7 +307,7 @@ public virtual void Detects_duplicate_discriminator_values() [ConditionalFact] public virtual void Passes_on_valid_concurrency_token() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .ToContainer("Orders") .Property("_etag") @@ -319,7 +319,7 @@ public virtual void Passes_on_valid_concurrency_token() [ConditionalFact] public virtual void Detects_invalid_concurrency_token() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .ToContainer("Orders") .Property("_not_etag") @@ -331,7 +331,7 @@ public virtual void Detects_invalid_concurrency_token() [ConditionalFact] public virtual void Detects_nonString_concurrency_token() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .ToContainer("Orders") .Property("_etag") diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index 444a365cc41..3365dfeda36 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -557,7 +557,7 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationCode, ignoreLineEndingDifferences: true); - var modelBuilder = SqlServerTestHelpers.Instance.CreateConventionBuilder(configure: c => c.RemoveAllConventions()); + var modelBuilder = SqlServerTestHelpers.Instance.CreateConventionBuilder(configureModel: c => c.RemoveAllConventions()); modelBuilder.HasAnnotation("Some:EnumValue", RegexOptions.Multiline); modelBuilder.HasAnnotation(RelationalAnnotationNames.DbFunctions, new SortedDictionary()); modelBuilder.Entity( diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs index 0496f1e09b6..ef23e7ee37d 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs @@ -916,6 +916,7 @@ partial void Initialize() DependentBasebyteEntityType.CreateForeignKey1(dependentBasebyte, principalBase); DependentBasebyteEntityType.CreateForeignKey2(dependentBasebyte, principalDerivedDependentBasebyte); OwnedTypeEntityType.CreateForeignKey1(ownedType, principalBase); + OwnedTypeEntityType.CreateForeignKey2(ownedType, ownedType); OwnedType0EntityType.CreateForeignKey1(ownedType0, principalDerivedDependentBasebyte); PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateForeignKey1(principalBasePrincipalDerivedDependentBasebyte, principalDerivedDependentBasebyte); PrincipalBasePrincipalDerivedDependentBasebyteEntityType.CreateForeignKey2(principalBasePrincipalDerivedDependentBasebyte, principalBase); @@ -1277,6 +1278,19 @@ public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEnt return runtimeForeignKey; } + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty(""PrincipalBaseId"")!, declaringEntityType.FindProperty(""PrincipalBaseAlternateId"")! }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty(""PrincipalBaseId"")!, principalEntityType.FindProperty(""PrincipalBaseAlternateId"")! })!, + principalEntityType, + deleteBehavior: DeleteBehavior.ClientCascade, + unique: true, + required: true, + requiredDependent: true); + + return runtimeForeignKey; + } + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) { var fragments = new StoreObjectDictionary(); @@ -3165,8 +3179,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Null(dataEntity.FindPrimaryKey()); var dataEntityFunctionMapping = dataEntity.GetFunctionMappings().Single(m => m.IsDefaultFunctionMapping); Assert.True(dataEntityFunctionMapping.IncludesDerivedTypes); - Assert.True(dataEntityFunctionMapping.IsSharedTablePrincipal); - Assert.True(dataEntityFunctionMapping.IsSplitEntityTypePrincipal); + Assert.Null(dataEntityFunctionMapping.IsSharedTablePrincipal); + Assert.Null(dataEntityFunctionMapping.IsSplitEntityTypePrincipal); Assert.Same(getDataParameterless, dataEntityFunctionMapping.DbFunction); var getDataStoreFunction = dataEntityFunctionMapping.StoreFunction; @@ -3175,8 +3189,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) var dataEntityOtherFunctionMapping = dataEntity.GetFunctionMappings().Single(m => !m.IsDefaultFunctionMapping); Assert.True(dataEntityOtherFunctionMapping.IncludesDerivedTypes); - Assert.True(dataEntityOtherFunctionMapping.IsSharedTablePrincipal); - Assert.True(dataEntityOtherFunctionMapping.IsSplitEntityTypePrincipal); + Assert.Null(dataEntityOtherFunctionMapping.IsSharedTablePrincipal); + Assert.Null(dataEntityOtherFunctionMapping.IsSplitEntityTypePrincipal); Assert.Same(getData, dataEntityOtherFunctionMapping.DbFunction); var getDataOtherStoreFunction = dataEntityOtherFunctionMapping.StoreFunction; @@ -3204,8 +3218,8 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) Assert.Null(objectEntity.FindPrimaryKey()); var objectEntityFunctionMapping = objectEntity.GetFunctionMappings().Single(m => m.IsDefaultFunctionMapping); Assert.True(objectEntityFunctionMapping.IncludesDerivedTypes); - Assert.True(objectEntityFunctionMapping.IsSharedTablePrincipal); - Assert.True(objectEntityFunctionMapping.IsSplitEntityTypePrincipal); + Assert.Null(objectEntityFunctionMapping.IsSharedTablePrincipal); + Assert.Null(objectEntityFunctionMapping.IsSplitEntityTypePrincipal); Assert.Same(getBlobs, objectEntityFunctionMapping.DbFunction); }); diff --git a/test/EFCore.Design.Tests/TestUtilities/DesignTestHelpers.cs b/test/EFCore.Design.Tests/TestUtilities/DesignTestHelpers.cs index 28d1844302b..a36b1eab231 100644 --- a/test/EFCore.Design.Tests/TestUtilities/DesignTestHelpers.cs +++ b/test/EFCore.Design.Tests/TestUtilities/DesignTestHelpers.cs @@ -16,7 +16,7 @@ protected DesignTestHelpers() public override IServiceCollection AddProviderServices(IServiceCollection services) => FakeRelationalOptionsExtension.AddEntityFrameworkRelationalDatabase(services); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseFakeRelational(); public override LoggingDefinitions LoggingDefinitions { get; } = new TestRelationalLoggingDefinitions(); diff --git a/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs b/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs index 278b4a61983..1c2b460be11 100644 --- a/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs +++ b/test/EFCore.InMemory.FunctionalTests/TestUtilities/InMemoryTestHelpers.cs @@ -16,7 +16,7 @@ protected InMemoryTestHelpers() public override IServiceCollection AddProviderServices(IServiceCollection services) => services.AddEntityFrameworkInMemoryDatabase(); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(nameof(InMemoryTestHelpers)); public override LoggingDefinitions LoggingDefinitions { get; } = new InMemoryLoggingDefinitions(); diff --git a/test/EFCore.InMemory.Tests/Infrastructure/InMemoryModelValidatorTest.cs b/test/EFCore.InMemory.Tests/Infrastructure/InMemoryModelValidatorTest.cs index a4afdbdf797..6399817a087 100644 --- a/test/EFCore.InMemory.Tests/Infrastructure/InMemoryModelValidatorTest.cs +++ b/test/EFCore.InMemory.Tests/Infrastructure/InMemoryModelValidatorTest.cs @@ -8,7 +8,7 @@ public class InMemoryModelValidatorTest : ModelValidatorTestBase [ConditionalFact] public virtual void Detects_ToQuery_on_derived_keyless_types() { - var modelBuilder = base.CreateConventionalModelBuilder(); + var modelBuilder = base.CreateConventionModelBuilder(); var context = new DbContext(new DbContextOptions()); modelBuilder.Entity().HasNoKey().ToInMemoryQuery(() => context.Set()); diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index c3fe92aeb28..99afb33690f 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -13,7 +13,7 @@ public partial class RelationalModelValidatorTest : ModelValidatorTest { public override void Detects_key_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -29,7 +29,7 @@ public override void Detects_key_property_which_cannot_be_compared() public override void Detects_unique_index_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -45,7 +45,7 @@ public override void Detects_unique_index_property_which_cannot_be_compared() public override void Ignores_normal_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -331,7 +331,7 @@ public virtual void Passes_for_duplicate_table_names_for_inherited_entities() [ConditionalFact] public virtual void Detects_incompatible_primary_keys_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().HasKey(a => a.Id).HasName("Key"); @@ -347,7 +347,7 @@ public virtual void Detects_incompatible_primary_keys_with_shared_table() [ConditionalFact] public virtual void Detects_incompatible_comments_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table").HasComment("My comment"); @@ -362,7 +362,7 @@ public virtual void Detects_incompatible_comments_with_shared_table() [ConditionalFact] public virtual void Passes_on_null_comments() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table").HasComment("My comment"); @@ -374,7 +374,7 @@ public virtual void Passes_on_null_comments() [ConditionalFact] public virtual void Detects_incompatible_primary_key_columns_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.Id).ValueGeneratedNever().HasColumnName("Key"); @@ -390,7 +390,7 @@ public virtual void Detects_incompatible_primary_key_columns_with_shared_table() [ConditionalFact] public virtual void Passes_on_not_configured_shared_columns_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)); @@ -405,7 +405,7 @@ public virtual void Passes_on_not_configured_shared_columns_with_shared_table() [ConditionalFact] public virtual void Throws_on_not_configured_shared_columns_with_shared_table_with_dependents() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)); @@ -419,7 +419,7 @@ public virtual void Throws_on_not_configured_shared_columns_with_shared_table_wi [ConditionalFact] public virtual void Warns_on_not_configured_shared_columns_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().OwnsOne(e => e.Owned); @@ -431,7 +431,7 @@ public virtual void Warns_on_not_configured_shared_columns_with_shared_table() [ConditionalFact] public virtual void Detects_incompatible_shared_columns_in_shared_table_with_different_data_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)).HasColumnType("someInt"); @@ -448,7 +448,7 @@ public virtual void Detects_incompatible_shared_columns_in_shared_table_with_dif [ConditionalFact] public virtual void Detects_incompatible_shared_columns_in_shared_table_with_different_provider_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().Property(a => a.P0).HasColumnName(nameof(A.P0)).HasColumnType("someInt").HasConversion(); @@ -465,7 +465,7 @@ public virtual void Detects_incompatible_shared_columns_in_shared_table_with_dif [ConditionalFact] public virtual void Detects_incompatible_shared_check_constraints_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().HasCheckConstraint("SomeCK", "Id > 0", c => c.HasName("CK_Table_SomeCK")); @@ -482,7 +482,7 @@ public virtual void Detects_incompatible_shared_check_constraints_with_shared_ta [ConditionalFact] public virtual void Passes_for_incompatible_uniquified_check_constraints_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().HasCheckConstraint("CK_Table_SomeCK", "Id > 0"); @@ -499,7 +499,7 @@ public virtual void Passes_for_incompatible_uniquified_check_constraints_with_sh [ConditionalFact] public virtual void Passes_for_compatible_shared_check_constraints_with_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne(b => b.A).HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().HasCheckConstraint("CK_Table_SomeCK", "Id > 0"); @@ -516,7 +516,7 @@ public virtual void Passes_for_compatible_shared_check_constraints_with_shared_t [ConditionalFact] public virtual void Detects_multiple_shared_table_roots() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table"); @@ -532,7 +532,7 @@ public virtual void Detects_multiple_shared_table_roots() [ConditionalFact] public virtual void Detects_shared_table_root_cycle() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table"); @@ -547,7 +547,7 @@ public virtual void Detects_shared_table_root_cycle() [ConditionalFact] public virtual void Passes_for_compatible_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); @@ -571,7 +571,7 @@ public virtual void Passes_for_compatible_shared_table() [ConditionalFact] public virtual void Passes_for_compatible_excluded_shared_table_inverted() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table", t => t.ExcludeFromMigrations()); @@ -583,7 +583,7 @@ public virtual void Passes_for_compatible_excluded_shared_table_inverted() [ConditionalFact] public virtual void Passes_for_compatible_excluded_shared_table_owned() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().OwnsOne(b => b.A); modelBuilder.Entity().ToTable("Table", t => t.ExcludeFromMigrations()); @@ -598,7 +598,7 @@ public virtual void Passes_for_compatible_excluded_shared_table_owned() [ConditionalFact] public virtual void Passes_for_compatible_excluded_table_derived() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Table", t => t.ExcludeFromMigrations()); modelBuilder.Entity(); @@ -613,7 +613,7 @@ public virtual void Passes_for_compatible_excluded_table_derived() [ConditionalFact] public virtual void Detect_partially_excluded_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id).IsRequired(); modelBuilder.Entity().ToTable("Table", t => t.ExcludeFromMigrations()); @@ -628,7 +628,7 @@ public virtual void Detect_partially_excluded_shared_table() [ConditionalFact] public virtual void Detects_entity_splitting_on_base_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToView("Animal").SplitToView("AnimalDetails", s => s.Property(a => a.Name)); modelBuilder.Entity().ToView("Cat"); @@ -640,7 +640,7 @@ public virtual void Detects_entity_splitting_on_base_type() [ConditionalFact] public virtual void Detects_entity_splitting_on_derived_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animal"); modelBuilder.Entity().ToTable("Cat").SplitToTable("CatDetails", s => s.Property(a => a.Name)); @@ -652,7 +652,7 @@ public virtual void Detects_entity_splitting_on_derived_type() [ConditionalFact] public virtual void Detects_entity_splitting_with_unmapped_main() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().SplitToView("AnimalDetails", s => s.Property(a => a.Name)); VerifyError( @@ -663,7 +663,7 @@ public virtual void Detects_entity_splitting_with_unmapped_main() [ConditionalFact] public virtual void Detects_entity_splitting_to_with_conflicting_main() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animal").SplitToTable("Animal", s => s.Property(a => a.Name)); VerifyError( @@ -674,7 +674,7 @@ public virtual void Detects_entity_splitting_to_with_conflicting_main() [ConditionalFact] public virtual void Detects_entity_splitting_with_unmapped_PK() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().SplitToTable("AnimalDetails", s => s.Property(a => a.Id).HasColumnName(null)); VerifyError( @@ -685,7 +685,7 @@ public virtual void Detects_entity_splitting_with_unmapped_PK() [ConditionalFact] public virtual void Detects_entity_splitting_without_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().SplitToTable("AnimalDetails", s => { }); VerifyError( @@ -696,7 +696,7 @@ public virtual void Detects_entity_splitting_without_properties() [ConditionalFact] public virtual void Detects_entity_splitting_to_table_with_all_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().SplitToTable("AnimalDetails", s => { s.Property(a => a.Name); @@ -711,7 +711,7 @@ public virtual void Detects_entity_splitting_to_table_with_all_properties() [ConditionalFact] public virtual void Detects_entity_splitting_to_view_with_all_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToView("Animal").SplitToView("AnimalDetails", s => { s.Property(a => a.Name); @@ -723,10 +723,131 @@ public virtual void Detects_entity_splitting_to_view_with_all_properties() modelBuilder); } + [ConditionalFact] + public void Detects_entity_splitting_with_partial_table_splitting() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity( + cb => + { + cb.Ignore(c => c.Customer); + + cb.ToTable("Order"); + + cb.SplitToTable( + "OrderDetails", tb => + { + tb.Property(c => c.PartitionId); + }); + + cb.OwnsOne( + c => c.OrderDetails, db => + { + db.ToTable("Details"); + + db.Property("OtherAddress"); + db.SplitToTable( + "Order", tb => + { + tb.Property("OtherAddress"); + }); + }); + cb.Navigation(c => c.OrderDetails).IsRequired(); + }); + + VerifyError( + RelationalStrings.EntitySplittingUnmatchedMainTableSplitting(nameof(OrderDetails), "Order", nameof(Order)), + modelBuilder); + } + + [ConditionalFact] + public void Detects_unnamed_index_properties_mapped_to_different_fragments_in_entity_splitting() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity() + .ToTable("Cats") + .SplitToTable("Animals", s => s.Property(c => c.Name)); + modelBuilder.Entity().HasIndex(nameof(Animal.Name), nameof(Cat.Identity)); + + var definition = RelationalResources + .LogUnnamedIndexPropertiesMappedToNonOverlappingTables( + new TestLogger()); + VerifyWarning( + definition.GenerateMessage( + nameof(Cat), + "{'Name', 'Identity'}", + nameof(Animal.Name), + "{'Animals'}", + nameof(Cat.Identity), + "{'Cats'}"), + modelBuilder, + LogLevel.Error); + } + + [ConditionalFact] + public void Detects_unnamed_key_properties_mapped_to_different_fragments_in_entity_splitting() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Entity() + .ToTable("Cats") + .SplitToTable("Animals", s => s.Property(c => c.Name)); + modelBuilder.Entity().HasAlternateKey(nameof(Animal.Name), nameof(Cat.Identity)); + + var definition = RelationalResources + .LogKeyUnmappedProperties(new TestLogger()); + VerifyWarning( + definition.GenerateMessage( + "{'Name', 'Identity'}", + nameof(Cat), + "Cats"), + modelBuilder, + LogLevel.Error); + } + + [ConditionalFact] + public virtual void Detects_unmapped_foreign_keys_in_entity_splitting() + { + var modelBuilder = CreateConventionModelBuilder(); + modelBuilder.Entity( + pb => + { + pb.HasKey(p => new {p.Id, p.Name}); + }); + modelBuilder.Entity().ToTable("Cat") + .HasOne().WithMany() + .HasForeignKey("FavoritePersonId", "FavoritePersonName"); + + modelBuilder.Entity() + .SplitToTable("Animals", s => s.Property("FavoritePersonName")); + + var definition = + RelationalResources.LogForeignKeyPropertiesMappedToUnrelatedTables(new TestLogger()); + VerifyWarning( + definition.GenerateMessage( + l => l.Log( + definition.Level, + definition.EventId, + definition.MessageFormat, + "{'FavoritePersonId', 'FavoritePersonName'}", + nameof(Cat), + nameof(Person), + "{'FavoritePersonId', 'FavoritePersonName'}", + nameof(Cat), + "{'Id', 'Name'}", + nameof(Person))), + modelBuilder, + LogLevel.Error); + } + + + [ConditionalFact] public virtual void Detects_duplicate_column_names() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(b => b.Id).HasColumnName("Name"); modelBuilder.Entity().Property(d => d.Name).HasColumnName("Name").IsRequired(); @@ -741,7 +862,7 @@ public virtual void Detects_duplicate_column_names() [ConditionalFact] public virtual void Detects_duplicate_columns_in_derived_types_with_different_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Type).HasColumnName("Type").IsRequired(); @@ -756,7 +877,7 @@ public virtual void Detects_duplicate_columns_in_derived_types_with_different_ty [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_MaxLength() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasMaxLength(30); @@ -771,7 +892,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_IsUnicode() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").IsUnicode(); @@ -785,7 +906,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_IsFixedLength() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").IsFixedLength(); @@ -799,7 +920,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_IsConcurrencyToken() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").IsConcurrencyToken(); @@ -816,7 +937,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_ComputedColumnSql() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasComputedColumnSql("1"); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -830,7 +951,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_stored_setting() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasComputedColumnSql("1", true); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasComputedColumnSql("1"); @@ -844,7 +965,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_DefaultValue() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasDefaultValueSql("1"); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasDefaultValue("1"); @@ -858,7 +979,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_DefaultValueSql() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasDefaultValueSql("1"); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -872,7 +993,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_nullability() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity(); modelBuilder.Entity().Property("OtherId").HasColumnName("Id"); @@ -886,7 +1007,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Passes_on_duplicate_column_names_within_hierarchy_with_same_column_nullability() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property("OtherId").HasColumnName("OtherId"); modelBuilder.Entity().Property("OtherId").HasColumnName("OtherId"); @@ -903,7 +1024,7 @@ public virtual void Passes_on_duplicate_column_names_within_hierarchy_with_same_ [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_comments() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasComment("My comment"); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -917,7 +1038,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_collations() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").UseCollation("UTF8"); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -931,7 +1052,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_orders() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasColumnOrder(0); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -945,7 +1066,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_precision() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed"); @@ -959,7 +1080,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_scale() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1, 2); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasPrecision(1); @@ -973,7 +1094,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Passes_for_compatible_duplicate_column_names_within_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( eb => @@ -1000,7 +1121,7 @@ public virtual void Passes_for_compatible_duplicate_column_names_within_hierarch [ConditionalFact] public virtual void Passes_for_shared_columns() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(a => a.Id).HasMaxLength(20).HasPrecision(15, 10).IsUnicode(); modelBuilder.Entity().OwnsOne(a => a.FavoritePerson); modelBuilder.Entity().Ignore(d => d.FavoritePerson); @@ -1011,7 +1132,7 @@ public virtual void Passes_for_shared_columns() [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_on_different_tables() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasOne().WithMany().HasForeignKey("FriendId").HasConstraintName("FK"); modelBuilder.Entity().HasOne().WithMany().HasForeignKey("FriendId").HasConstraintName("FK"); @@ -1036,7 +1157,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_on_diffe [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_principal_tables() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasOne().WithMany().HasForeignKey("FriendId").HasConstraintName("FK"); modelBuilder.Entity().HasOne().WithMany().HasForeignKey("FriendId").HasConstraintName("FK"); @@ -1054,7 +1175,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_dif [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_column_count() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("FriendId"); modelBuilder.Entity().Property("Shadow"); modelBuilder.Entity().HasOne().WithMany().HasForeignKey("FriendId", "Shadow").HasPrincipalKey( @@ -1074,7 +1195,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_dif [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_column_order() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( @@ -1113,7 +1234,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_dif [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_mapped_to_different_columns() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasOne().WithMany().HasForeignKey( c => new { c.Name, c.Breed }).HasPrincipalKey( @@ -1136,7 +1257,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_mapped_t [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_referencing_different_columns() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity() .HasOne().WithMany() @@ -1159,7 +1280,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_referenc [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_uniqueness() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var fk1 = modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .HasConstraintName("FK_Animal_Person_Name").Metadata; @@ -1183,7 +1304,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_dif [ConditionalFact] public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_different_delete_behavior() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .OnDelete(DeleteBehavior.Cascade).HasConstraintName("FK_Animal_Person_Name"); @@ -1202,7 +1323,7 @@ public virtual void Detects_duplicate_foreignKey_names_within_hierarchy_with_dif [ConditionalFact] public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var fk1 = modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .OnDelete(DeleteBehavior.Cascade).Metadata; @@ -1223,7 +1344,7 @@ public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy() [ConditionalFact] public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy_when_one_name_configured_explicitly() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var fk1 = modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .OnDelete(DeleteBehavior.Cascade).HasConstraintName("FK_Animal_Person_Name").Metadata; @@ -1244,7 +1365,7 @@ public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy_when_on [ConditionalFact] public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); IReadOnlyForeignKey fk1 = null; IReadOnlyForeignKey fk2 = null; @@ -1290,7 +1411,7 @@ public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hier [ConditionalFact] public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hierarchy_name_configured_explicitly() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); IReadOnlyForeignKey fk1 = null; IReadOnlyForeignKey fk2 = null; @@ -1338,7 +1459,7 @@ public virtual void Passes_for_compatible_duplicate_foreignKey_names_within_hier [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_column_count() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("Shadow"); modelBuilder.Entity().HasIndex(nameof(Cat.Name), "Shadow").HasDatabaseName("IX"); modelBuilder.Entity().HasIndex(d => d.Name).HasDatabaseName("IX"); @@ -1356,7 +1477,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_column_order() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( @@ -1387,7 +1508,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_mapped_to_different_columns() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex( c => new { c.Name, c.Breed }).HasDatabaseName("IX"); @@ -1408,7 +1529,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_mapped_to_dif [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_uniqueness() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).IsUnique().HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).IsUnique(false).HasDatabaseName("IX_Animal_Name"); @@ -1424,7 +1545,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_sort_orders() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name") .IsDescending(true); @@ -1442,7 +1563,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_filters() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasFilter("Foo").HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).HasFilter("Bar").HasDatabaseName("IX_Animal_Name"); @@ -1459,7 +1580,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public virtual void Passes_for_incompatible_indexes_within_hierarchy_when_one_name_configured_explicitly() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var index1 = modelBuilder.Entity().HasIndex(c => c.Name).IsUnique().HasDatabaseName("IX_Animal_Name").Metadata; var index2 = modelBuilder.Entity().HasIndex(d => d.Name).IsUnique(false).Metadata; @@ -1473,7 +1594,7 @@ public virtual void Passes_for_incompatible_indexes_within_hierarchy_when_one_na [ConditionalFact] public virtual void Passes_for_compatible_duplicate_index_names_within_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); IMutableIndex index1 = null; IMutableIndex index2 = null; modelBuilder.Entity(); @@ -1499,7 +1620,7 @@ public virtual void Passes_for_compatible_duplicate_index_names_within_hierarchy [ConditionalFact] public virtual void Passes_for_indexes_on_related_types_mapped_to_different_tables() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity(); @@ -1567,7 +1688,7 @@ public virtual void Detects_missing_concurrency_token_on_the_sharing_type_withou [ConditionalFact] public virtual void Passes_with_missing_concurrency_token_property_on_the_base_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(nameof(Animal)) .Property("Version").IsRowVersion().HasColumnName("Version"); modelBuilder.Entity().HasOne(a => a.FavoritePerson).WithOne().HasForeignKey(p => p.Id); @@ -1580,7 +1701,7 @@ public virtual void Passes_with_missing_concurrency_token_property_on_the_base_t [ConditionalFact] public virtual void Passes_with_missing_concurrency_token_property_on_the_base_type_when_derived_is_sharing() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(nameof(Animal)) .Property("Version").IsRowVersion().HasColumnName("Version"); modelBuilder.Entity().Ignore(p => p.FavoritePerson); @@ -1597,7 +1718,7 @@ public virtual void Passes_with_missing_concurrency_token_property_on_the_base_t [ConditionalFact] public virtual void Passes_with_missing_concurrency_token_property_on_the_sharing_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(nameof(Animal)); modelBuilder.Entity().HasOne(a => a.FavoritePerson).WithOne().HasForeignKey(p => p.Id); modelBuilder.Entity().Property("Version").IsRowVersion().HasColumnName("Version"); @@ -1608,7 +1729,7 @@ public virtual void Passes_with_missing_concurrency_token_property_on_the_sharin [ConditionalFact] public virtual void Passes_for_explicitly_mapped_concurrency_tokens_with_table_sharing() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(nameof(Animal)) .Property("Version").IsRowVersion(); modelBuilder.Entity() @@ -1624,7 +1745,7 @@ public virtual void Passes_for_explicitly_mapped_concurrency_tokens_with_table_s [ConditionalFact] public virtual void Passes_for_missing_concurrency_token_on_owner() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().OwnsOne( a => a.FavoritePerson, @@ -1637,7 +1758,7 @@ public virtual void Passes_for_missing_concurrency_token_on_owner() [ConditionalFact] public virtual void Passes_for_explicitly_mapped_concurrency_tokens_with_owned() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("Version").IsRowVersion(); modelBuilder.Entity().OwnsOne( a => a.FavoritePerson, @@ -1663,7 +1784,7 @@ public virtual void Passes_for_non_hierarchical_model() [ConditionalFact] public virtual void Passes_for_missing_discriminator_value_for_abstract_class() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasDiscriminator("ClassType") .HasValue(0) @@ -1677,7 +1798,7 @@ public virtual void Passes_for_missing_discriminator_value_for_abstract_class() [ConditionalFact] public virtual void Passes_for_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable("Cat").ToView("Cat"); @@ -1687,7 +1808,7 @@ public virtual void Passes_for_TPT() [ConditionalFact] public virtual void Passes_for_unconfigured_entity_type_in_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable("Cat"); modelBuilder.Entity(); @@ -1698,7 +1819,7 @@ public virtual void Passes_for_unconfigured_entity_type_in_TPT() [ConditionalFact] public virtual void Detects_clashing_entity_types_in_view_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable("Cat").ToView("Cat"); modelBuilder.Entity().ToTable("Dog").ToView("Cat"); @@ -1711,7 +1832,7 @@ public virtual void Detects_clashing_entity_types_in_view_TPT() [ConditionalFact] public virtual void Detects_table_and_view_TPT_mismatch() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animal").ToView("Animal"); modelBuilder.Entity().ToTable("Animal").ToView("Cat"); @@ -1723,7 +1844,7 @@ public virtual void Detects_table_and_view_TPT_mismatch() [ConditionalFact] public virtual void Detects_TPT_with_discriminator() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasDiscriminator("Discriminator"); modelBuilder.Entity().ToTable("Cat"); @@ -1735,7 +1856,7 @@ public virtual void Detects_TPT_with_discriminator() [ConditionalFact] public virtual void Detects_view_TPT_with_discriminator() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToView("Animal").HasDiscriminator("Discriminator"); modelBuilder.Entity().ToView("Cat"); @@ -1747,7 +1868,7 @@ public virtual void Detects_view_TPT_with_discriminator() [ConditionalFact] public virtual void Detects_TPT_with_keyless_entity_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasNoKey(); modelBuilder.Entity().ToTable("Cat"); @@ -1759,7 +1880,7 @@ public virtual void Detects_TPT_with_keyless_entity_type() [ConditionalFact] public virtual void Passes_on_valid_table_sharing_with_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Ignore(a => a.FavoritePerson); @@ -1779,7 +1900,7 @@ public virtual void Passes_on_valid_table_sharing_with_TPT() [ConditionalFact] public virtual void Detects_linking_relationship_on_derived_type_in_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Ignore(a => a.FavoritePerson); @@ -1802,7 +1923,7 @@ public virtual void Detects_linking_relationship_on_derived_type_in_TPT() [ConditionalFact] public virtual void Detects_linking_relationship_on_derived_type_in_TPT_views() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Ignore(a => a.FavoritePerson) @@ -1826,7 +1947,7 @@ public virtual void Detects_linking_relationship_on_derived_type_in_TPT_views() [ConditionalFact] public virtual void Detects_unmapped_foreign_keys_in_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Ignore(a => a.FavoritePerson) .Property("FavoritePersonId"); @@ -1851,7 +1972,7 @@ public virtual void Detects_unmapped_foreign_keys_in_TPT() [ConditionalFact] public virtual void Passes_for_ToTable_for_abstract_class() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Abstract"); modelBuilder.Entity(); modelBuilder.Entity>(); @@ -1862,7 +1983,7 @@ public virtual void Passes_for_ToTable_for_abstract_class() [ConditionalFact] public virtual void Passes_for_abstract_class_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().UseTpcMappingStrategy(); modelBuilder.Entity>().ToTable("G"); @@ -1873,7 +1994,7 @@ public virtual void Passes_for_abstract_class_TPC() [ConditionalFact] public virtual void Passes_for_view_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable((string)null).UseTpcMappingStrategy(); modelBuilder.Entity().ToView("Cat"); @@ -1883,7 +2004,7 @@ public virtual void Passes_for_view_TPC() [ConditionalFact] public virtual void Detects_invalid_MappingStrategy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasAnnotation(RelationalAnnotationNames.MappingStrategy, "TTT"); modelBuilder.Entity(); @@ -1895,7 +2016,7 @@ public virtual void Detects_invalid_MappingStrategy() [ConditionalFact] public virtual void Detects_MappingStrategy_on_derived_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasBaseType((string)null); modelBuilder.Entity(); modelBuilder.Entity().ToTable("Cat").ToView("Cat").UseTpcMappingStrategy().HasBaseType(typeof(Animal)); @@ -1908,7 +2029,7 @@ public virtual void Detects_MappingStrategy_on_derived_types() [ConditionalFact] public virtual void Detects_ToTable_for_abstract_class_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Abstract", "dbo").UseTpcMappingStrategy(); modelBuilder.Entity(); modelBuilder.Entity>(); @@ -1921,7 +2042,7 @@ public virtual void Detects_ToTable_for_abstract_class_TPC() [ConditionalFact] public virtual void Detects_ToView_for_abstract_class_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToView("Abstract").UseTpcMappingStrategy(); modelBuilder.Entity(); modelBuilder.Entity>(); @@ -1934,7 +2055,7 @@ public virtual void Detects_ToView_for_abstract_class_TPC() [ConditionalFact] public virtual void Detects_ToFunction_for_abstract_class_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToFunction("Abstract").UseTpcMappingStrategy(); modelBuilder.Entity(); modelBuilder.Entity>(); @@ -1947,7 +2068,7 @@ public virtual void Detects_ToFunction_for_abstract_class_TPC() [ConditionalFact] public virtual void Detects_clashing_entity_types_in_views_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy(); modelBuilder.Entity().ToTable("Cat").ToView("Cat"); modelBuilder.Entity().ToTable("Dog").ToView("Cat"); @@ -1960,7 +2081,7 @@ public virtual void Detects_clashing_entity_types_in_views_TPC() [ConditionalFact] public virtual void Detects_table_and_view_TPC_mismatch() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy().ToTable("Animal").ToView("Animal"); modelBuilder.Entity().ToTable("Animal").ToView("Cat"); @@ -1972,7 +2093,7 @@ public virtual void Detects_table_and_view_TPC_mismatch() [ConditionalFact] public virtual void Passes_on_TPC_with_keyless_entity_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy().HasNoKey(); modelBuilder.Entity().ToTable("Cat"); @@ -1982,7 +2103,7 @@ public virtual void Passes_on_TPC_with_keyless_entity_type() [ConditionalFact] public virtual void Detects_view_TPC_with_discriminator() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToView("Animal").UseTpcMappingStrategy().HasDiscriminator("Discriminator"); modelBuilder.Entity().ToView("Cat"); @@ -1994,7 +2115,7 @@ public virtual void Detects_view_TPC_with_discriminator() [ConditionalFact] public virtual void Detects_store_generated_PK_in_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy(); modelBuilder.Entity(); @@ -2009,7 +2130,7 @@ public virtual void Detects_store_generated_PK_in_TPC() [ConditionalFact] public virtual void Detects_table_sharing_with_TPC_on_dependent() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .ToTable("Animal") @@ -2025,7 +2146,7 @@ public virtual void Detects_table_sharing_with_TPC_on_dependent() [ConditionalFact] public virtual void Passes_on_valid_view_sharing_with_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .UseTpcMappingStrategy() @@ -2047,7 +2168,7 @@ public virtual void Passes_on_valid_view_sharing_with_TPC() [ConditionalFact] public virtual void Detects_view_sharing_on_base_with_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .UseTpcMappingStrategy() @@ -2065,7 +2186,7 @@ public virtual void Detects_view_sharing_on_base_with_TPC() [ConditionalFact] public virtual void Detects_linking_relationship_on_derived_type_in_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .UseTpcMappingStrategy() @@ -2089,7 +2210,7 @@ public virtual void Detects_linking_relationship_on_derived_type_in_TPC() [ConditionalFact] public virtual void Detects_linking_relationship_on_derived_type_in_TPC_views() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .UseTpcMappingStrategy() @@ -2114,7 +2235,7 @@ public virtual void Detects_linking_relationship_on_derived_type_in_TPC_views() [ConditionalFact] public virtual void Detects_unmapped_foreign_keys_in_TPC() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy() .Property(a => a.Id).ValueGeneratedNever(); modelBuilder.Entity().ToTable("Cat"); @@ -2140,7 +2261,7 @@ public virtual void Detects_unmapped_foreign_keys_in_TPC() [ConditionalFact] public virtual void Passes_for_valid_table_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var property = modelBuilder.Entity().Property(a => a.Identity).GetInfrastructure(); modelBuilder.Entity().ToTable("Dog"); @@ -2152,7 +2273,7 @@ public virtual void Passes_for_valid_table_overrides() [ConditionalFact] public virtual void Detects_invalid_table_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var property = modelBuilder.Entity().Property(a => a.Identity).GetInfrastructure(); property.HasColumnName("DogName", StoreObjectIdentifier.Table("Dog")); @@ -2165,7 +2286,7 @@ public virtual void Detects_invalid_table_overrides() [ConditionalFact] public virtual void Detects_column_override_on_an_inherited_property_with_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Ignore(a => a.FavoritePerson); @@ -2180,7 +2301,7 @@ public virtual void Detects_column_override_on_an_inherited_property_with_TPT() [ConditionalFact] public virtual void Passes_for_valid_view_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var property = modelBuilder.Entity().Property(a => a.Identity).GetInfrastructure(); modelBuilder.Entity().ToView("Dog"); @@ -2192,7 +2313,7 @@ public virtual void Passes_for_valid_view_overrides() [ConditionalFact] public virtual void Detects_invalid_view_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var property = modelBuilder.Entity().Property(a => a.Identity).GetInfrastructure(); property.HasColumnName("DogName", StoreObjectIdentifier.View("Dog")); @@ -2205,7 +2326,7 @@ public virtual void Detects_invalid_view_overrides() [ConditionalFact] public virtual void Detects_invalid_sql_query_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var property = modelBuilder.Entity().Property(a => a.Name).GetInfrastructure(); property.HasColumnName("DogName", StoreObjectIdentifier.SqlQuery("Dog")); @@ -2217,7 +2338,7 @@ public virtual void Detects_invalid_sql_query_overrides() [ConditionalFact] public virtual void Detects_invalid_function_overrides() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var property = modelBuilder.Entity().Property(a => a.Name).GetInfrastructure(); property.HasColumnName("DogName", StoreObjectIdentifier.DbFunction("Dog")); @@ -2229,7 +2350,7 @@ public virtual void Detects_invalid_function_overrides() [ConditionalFact] public void Detects_function_with_invalid_return_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.HasDbFunction(TestMethods.MethodCMi); @@ -2262,7 +2383,7 @@ var methodInfo [ConditionalFact] public void Detects_function_with_invalid_parameter_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.HasDbFunction(TestMethods.MethodDMi); @@ -2277,7 +2398,7 @@ public void Detects_function_with_invalid_parameter_type() [ConditionalFact] public void Passes_for_valid_entity_type_mapped_to_function() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var methodInfo = typeof(TestMethods) @@ -2296,7 +2417,7 @@ var methodInfo [ConditionalFact] public void Detects_entity_type_mapped_to_non_existent_function() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasNoKey().ToFunction("NonExistent"); @@ -2310,7 +2431,7 @@ public void Detects_entity_type_mapped_to_non_existent_function() [ConditionalFact] public void Detects_entity_type_mapped_to_a_scalar_function() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var function = modelBuilder.HasDbFunction(TestMethods.MethodEMi).Metadata; @@ -2328,7 +2449,7 @@ public void Detects_entity_type_mapped_to_a_scalar_function() [ConditionalFact] public void Detects_entity_type_mapped_to_a_different_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasNoKey().ToFunction(TestMethods.MethodAMi); modelBuilder.Entity().HasNoKey(); @@ -2345,7 +2466,7 @@ public void Detects_entity_type_mapped_to_a_different_type() [ConditionalFact] public void Detects_entity_type_mapped_to_a_function_with_parameters() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); ((IConventionEntityType)modelBuilder.Entity().HasNoKey().Metadata) .Builder.ToFunction(TestMethods.MethodBMi); @@ -2361,7 +2482,7 @@ public void Detects_entity_type_mapped_to_a_function_with_parameters() [ConditionalFact] public void Detects_multiple_entity_types_mapped_to_the_same_function() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var function = modelBuilder.HasDbFunction(TestMethods.MethodAMi).Metadata; @@ -2383,7 +2504,7 @@ public void Detects_multiple_entity_types_mapped_to_the_same_function() [ConditionalFact] public void Detects_derived_entity_type_mapped_to_a_function() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var function = modelBuilder.HasDbFunction(TestMethods.MethodAMi).Metadata; @@ -2401,7 +2522,7 @@ public void Detects_derived_entity_type_mapped_to_a_function() [ConditionalFact] public void Passes_for_unnamed_index_with_all_properties_not_mapped_to_any_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable((string)null); modelBuilder.Entity().HasIndex(nameof(Animal.Id), nameof(Animal.Name)); @@ -2419,7 +2540,7 @@ public void Passes_for_unnamed_index_with_all_properties_not_mapped_to_any_table [ConditionalFact] public void Passes_for_named_index_with_all_properties_not_mapped_to_any_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable((string)null); modelBuilder.Entity() @@ -2441,7 +2562,7 @@ public void Passes_for_named_index_with_all_properties_not_mapped_to_any_table() [ConditionalFact] public void Passes_for_mix_of_index_properties_declared_and_inherited_TPT() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable((string)null); modelBuilder.Entity().ToTable("Cats") @@ -2455,7 +2576,7 @@ public void Passes_for_mix_of_index_properties_declared_and_inherited_TPT() [ConditionalFact] public void Detects_mix_of_index_property_mapped_and_not_mapped_to_any_table_mapped_first() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable((string)null) @@ -2476,7 +2597,7 @@ public void Detects_mix_of_index_property_mapped_and_not_mapped_to_any_table_map [ConditionalFact] public void Detects_mix_of_index_property_mapped_and_not_mapped_to_any_table_unmapped_first() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable((string)null) @@ -2500,7 +2621,7 @@ public void Detects_mix_of_index_property_mapped_and_not_mapped_to_any_table_unm [ConditionalFact] public void Passes_for_index_properties_mapped_to_same_table_in_TPT_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animals"); modelBuilder.Entity().ToTable("Cats") @@ -2516,7 +2637,7 @@ public void Passes_for_index_properties_mapped_to_same_table_in_TPT_hierarchy() [ConditionalFact] public void Detects_unnamed_index_properties_mapped_to_different_tables_in_TPT_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animals"); modelBuilder.Entity().ToTable("Cats"); @@ -2540,7 +2661,7 @@ public void Detects_unnamed_index_properties_mapped_to_different_tables_in_TPT_h [ConditionalFact] public void Detects_named_index_properties_mapped_to_different_tables_in_TPT_hierarchy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animals"); modelBuilder.Entity().ToTable("Cats"); @@ -2572,7 +2693,7 @@ public void Detects_named_index_properties_mapped_to_different_tables_in_TPT_hie [ConditionalFact] public virtual void Non_TPH_as_a_result_of_DbFunction_throws() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("A").HasNoDiscriminator(); modelBuilder.Entity().ToTable("C"); @@ -2586,7 +2707,7 @@ public virtual void Non_TPH_as_a_result_of_DbFunction_throws() [ConditionalFact] public virtual void Passes_for_relational_override_without_inheritance() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( e => { @@ -2602,7 +2723,7 @@ public virtual void Passes_for_relational_override_without_inheritance() [ConditionalFact] public virtual void Detects_duplicate_column_orders() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( x => { @@ -2617,7 +2738,7 @@ public virtual void Detects_duplicate_column_orders() [ConditionalFact] public virtual void Detects_triggers_on_unmapped_entity_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( x => { @@ -2631,7 +2752,7 @@ public virtual void Detects_triggers_on_unmapped_entity_types() [ConditionalFact] public virtual void Throws_when_non_tph_entity_type_short_names_are_not_unique() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy(); modelBuilder.Entity().ToTable("TpcDerived1"); modelBuilder.Entity().ToTable("TpcDerived2"); @@ -2647,7 +2768,7 @@ public virtual void Throws_when_non_tph_entity_type_short_names_are_not_unique() [ConditionalFact] public virtual void Throws_when_non_tph_entity_type_discriminator_set_to_non_string() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().UseTpcMappingStrategy(); modelBuilder.Entity().ToTable("TpcDerived1"); modelBuilder.Entity().ToTable("TpcDerived2"); diff --git a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs index 68929a90759..c191e95f703 100644 --- a/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs +++ b/test/EFCore.Relational.Tests/Metadata/RelationalModelTest.cs @@ -165,19 +165,19 @@ private static void AssertDefaultMappings(IRelationalModel model, Mapping mappin Assert.Equal("Microsoft.EntityFrameworkCore.Metadata.RelationalModelTest+Customer", customerTable.Name); Assert.Null(customerTable.Schema); Assert.Equal(4, specialCustomerType.GetDefaultMappings().Count()); - Assert.True(specialCustomerType.GetDefaultMappings().First().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetDefaultMappings().First().IsSplitEntityTypePrincipal); Assert.False(specialCustomerType.GetDefaultMappings().First().IncludesDerivedTypes); - Assert.True(specialCustomerType.GetDefaultMappings().Last().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetDefaultMappings().Last().IsSplitEntityTypePrincipal); Assert.True(specialCustomerType.GetDefaultMappings().Last().IncludesDerivedTypes); var specialCustomerTable = specialCustomerType.GetDefaultMappings().Last().Table; Assert.Null(specialCustomerTable.Schema); Assert.Equal(4, specialCustomerTable.Columns.Count()); - Assert.True(specialCustomerTable.EntityTypeMappings.Single(m => m.EntityType == specialCustomerType).IsSharedTablePrincipal); + Assert.Null(specialCustomerTable.EntityTypeMappings.Single(m => m.EntityType == specialCustomerType).IsSharedTablePrincipal); - var specialityColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); - Assert.False(specialityColumn.IsNullable); + var specialtyColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); + Assert.False(specialtyColumn.IsNullable); Assert.Null(customerType.FindDiscriminatorProperty()); Assert.Equal("Customer", customerType.GetDiscriminatorValue()); @@ -187,9 +187,9 @@ private static void AssertDefaultMappings(IRelationalModel model, Mapping mappin else { var specialCustomerTableMapping = specialCustomerType.GetDefaultMappings().Single(); - Assert.True(specialCustomerTableMapping.IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerTableMapping.IsSplitEntityTypePrincipal); var specialCustomerTable = specialCustomerTableMapping.Table; - var specialityColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); + var specialtyColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); if (mapping == Mapping.TPH) { var baseTable = abstractBaseType.GetDefaultMappings().Single().Table; @@ -200,32 +200,32 @@ private static void AssertDefaultMappings(IRelationalModel model, Mapping mappin Assert.Same(customerTable, specialCustomerTable); Assert.Equal(5, specialCustomerTable.EntityTypeMappings.Count()); - Assert.True(specialCustomerTable.EntityTypeMappings.All(t => t.IsSharedTablePrincipal)); + Assert.All(specialCustomerTable.EntityTypeMappings, t => Assert.Null(t.IsSharedTablePrincipal)); Assert.Equal(10, specialCustomerTable.Columns.Count()); - Assert.True(specialityColumn.IsNullable); + Assert.True(specialtyColumn.IsNullable); } else { Assert.False(specialCustomerTableMapping.IncludesDerivedTypes); Assert.NotSame(customerTable, specialCustomerTable); - Assert.True(customerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); + Assert.Null(customerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); Assert.Equal(5, customerTable.Columns.Count()); - Assert.True(specialCustomerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); + Assert.Null(specialCustomerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); Assert.Equal(9, specialCustomerTable.Columns.Count()); - Assert.False(specialityColumn.IsNullable); + Assert.False(specialtyColumn.IsNullable); } } } private static void AssertViews(IRelationalModel model, Mapping mapping) { - var orderType = model.Model.FindEntityType(typeof(Order)); + var orderType = model.Model.FindEntityType(typeof(Order))!; var orderMapping = orderType.GetViewMappings().Single(); Assert.Equal(orderType.GetViewMappings(), orderType.GetViewOrTableMappings()); Assert.True(orderMapping.IncludesDerivedTypes); @@ -269,9 +269,9 @@ private static void AssertViews(IRelationalModel model, Mapping mapping) var abstractBaseType = model.Model.FindEntityType(typeof(AbstractBase)); var abstractCustomerType = model.Model.FindEntityType(typeof(AbstractCustomer)); - var customerType = model.Model.FindEntityType(typeof(Customer)); - var specialCustomerType = model.Model.FindEntityType(typeof(SpecialCustomer)); - var extraSpecialCustomerType = model.Model.FindEntityType(typeof(ExtraSpecialCustomer)); + var customerType = model.Model.FindEntityType(typeof(Customer))!; + var specialCustomerType = model.Model.FindEntityType(typeof(SpecialCustomer))!; + var extraSpecialCustomerType = model.Model.FindEntityType(typeof(ExtraSpecialCustomer))!; var orderDetailsOwnership = orderType.FindNavigation(nameof(Order.Details)).ForeignKey; var orderDetailsType = orderDetailsOwnership.DeclaringEntityType; Assert.Same(ordersView, orderDetailsType.GetViewMappings().Single().View); @@ -349,16 +349,16 @@ private static void AssertViews(IRelationalModel model, Mapping mapping) Assert.Null(ordersCustomerIndex.GetDefaultDatabaseName( StoreObjectIdentifier.Table(ordersView.Name, ordersView.Schema))); - var specialityCK = specialCustomerType.GetCheckConstraints().Single(); + var specialtyCK = specialCustomerType.GetCheckConstraints().Single(); Assert.Equal(mappedToTable - ? "Speciality" - : null, specialityCK.Name); - Assert.Null(specialityCK.GetName( + ? "Specialty" + : null, specialtyCK.Name); + Assert.Null(specialtyCK.GetName( StoreObjectIdentifier.Table(ordersView.Name, ordersView.Schema))); Assert.Equal(mappedToTable - ? "Speciality" - : null, specialityCK.GetDefaultName()); - Assert.Equal("Speciality", specialityCK.GetDefaultName( + ? "Specialty" + : null, specialtyCK.GetDefaultName()); + Assert.Equal("Specialty", specialtyCK.GetDefaultName( StoreObjectIdentifier.Table(ordersView.Name, ordersView.Schema))); Assert.Equal(mappedToTable @@ -377,20 +377,20 @@ private static void AssertViews(IRelationalModel model, Mapping mapping) Assert.Equal("CustomerView", customerView.Name); Assert.Equal("viewSchema", customerView.Schema); Assert.Equal(3, specialCustomerType.GetViewMappings().Count()); - Assert.True(specialCustomerType.GetViewMappings().First().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetViewMappings().First().IsSplitEntityTypePrincipal); Assert.False(specialCustomerType.GetViewMappings().First().IncludesDerivedTypes); - Assert.True(specialCustomerType.GetViewMappings().Last().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetViewMappings().Last().IsSplitEntityTypePrincipal); Assert.True(specialCustomerType.GetViewMappings().Last().IncludesDerivedTypes); var specialCustomerView = specialCustomerType.GetViewMappings().Select(t => t.Table) .First(t => t.Name == "SpecialCustomerView"); Assert.Null(specialCustomerView.Schema); - Assert.Equal(6, specialCustomerView.Columns.Count()); + Assert.Equal(7, specialCustomerView.Columns.Count()); Assert.True(specialCustomerView.EntityTypeMappings.Single(m => m.EntityType == specialCustomerType).IsSharedTablePrincipal); - var specialityColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); - Assert.False(specialityColumn.IsNullable); + var specialtyColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); + Assert.False(specialtyColumn.IsNullable); Assert.Null(customerType.FindDiscriminatorProperty()); Assert.Equal("Customer", customerType.GetDiscriminatorValue()); @@ -400,9 +400,13 @@ private static void AssertViews(IRelationalModel model, Mapping mapping) else { var specialCustomerViewMapping = specialCustomerType.GetViewMappings().Single(); - Assert.True(specialCustomerViewMapping.IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerViewMapping.IsSplitEntityTypePrincipal); var specialCustomerView = specialCustomerViewMapping.View; - var specialityColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); + var specialtyColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); + + var extraSpecialCustomerViewMapping = extraSpecialCustomerType.GetViewMappings().Single(); + Assert.Null(extraSpecialCustomerViewMapping.IsSplitEntityTypePrincipal); + var extraSpecialCustomerView = extraSpecialCustomerViewMapping.View; if (mapping == Mapping.TPH) { var baseView = abstractBaseType.GetViewMappings().Single().Table; @@ -413,27 +417,38 @@ private static void AssertViews(IRelationalModel model, Mapping mapping) Assert.True(specialCustomerViewMapping.IncludesDerivedTypes); Assert.Same(customerView, specialCustomerView); + Assert.Equal(12, specialCustomerView.Columns.Count()); + Assert.Equal(6, specialCustomerView.EntityTypeMappings.Count()); Assert.True(specialCustomerView.EntityTypeMappings.First().IsSharedTablePrincipal); + Assert.Null(specialCustomerView.EntityTypeMappings.First().IsSplitEntityTypePrincipal); Assert.False(specialCustomerView.EntityTypeMappings.Last().IsSharedTablePrincipal); + Assert.Null(specialCustomerView.EntityTypeMappings.Last().IsSplitEntityTypePrincipal); - Assert.True(specialityColumn.IsNullable); + Assert.True(specialtyColumn.IsNullable); } else { Assert.False(specialCustomerViewMapping.IncludesDerivedTypes); Assert.NotSame(customerView, specialCustomerView); - Assert.True(customerView.EntityTypeMappings.Single().IsSharedTablePrincipal); + Assert.Null(customerView.EntityTypeMappings.Single().IsSharedTablePrincipal); + Assert.Null(customerView.EntityTypeMappings.Single().IsSplitEntityTypePrincipal); Assert.Equal(5, customerView.Columns.Count()); - Assert.Equal(2, specialCustomerView.EntityTypeMappings.Count()); - Assert.True(specialCustomerView.EntityTypeMappings.First().IsSharedTablePrincipal); - Assert.False(specialCustomerView.EntityTypeMappings.Last().IsSharedTablePrincipal); + Assert.Single(specialCustomerView.EntityTypeMappings); + + Assert.Equal(9, specialCustomerView.Columns.Count()); + + Assert.False(specialtyColumn.IsNullable); - Assert.Equal(10, specialCustomerView.Columns.Count()); + Assert.Equal(2, extraSpecialCustomerView.EntityTypeMappings.Count()); + Assert.True(extraSpecialCustomerView.EntityTypeMappings.First().IsSharedTablePrincipal); + Assert.Null(extraSpecialCustomerView.EntityTypeMappings.First().IsSplitEntityTypePrincipal); + Assert.False(extraSpecialCustomerView.EntityTypeMappings.Last().IsSharedTablePrincipal); + Assert.Null(extraSpecialCustomerView.EntityTypeMappings.Last().IsSplitEntityTypePrincipal); - Assert.False(specialityColumn.IsNullable); + Assert.Equal(11, extraSpecialCustomerView.Columns.Count()); } } } @@ -581,9 +596,13 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Equal("OrderId", orderDetailsPkProperty.GetColumnName()); var billingAddressOwnership = orderDetailsType.FindNavigation(nameof(OrderDetails.BillingAddress)).ForeignKey; + Assert.True(billingAddressOwnership.IsRequiredDependent); + var billingAddressType = billingAddressOwnership.DeclaringEntityType; var shippingAddressOwnership = orderDetailsType.FindNavigation(nameof(OrderDetails.ShippingAddress)).ForeignKey; + Assert.True(shippingAddressOwnership.IsRequiredDependent); + var shippingAddressType = shippingAddressOwnership.DeclaringEntityType; Assert.Equal( @@ -622,12 +641,12 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) var specialCustomerTable = specialCustomerType.GetTableMappings().Select(t => t.Table).Last(); - var specialityCK = specialCustomerType.GetCheckConstraints().Single(); - Assert.Equal("Speciality", specialityCK.Name); - Assert.Equal("Speciality", specialityCK.GetName( + var SpecialtyCK = specialCustomerType.GetCheckConstraints().Single(); + Assert.Equal("Specialty", SpecialtyCK.Name); + Assert.Equal("Specialty", SpecialtyCK.GetName( StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema))); - Assert.Equal("Speciality", specialityCK.GetDefaultName()); - Assert.Equal("Speciality", specialityCK.GetDefaultName( + Assert.Equal("Specialty", SpecialtyCK.GetDefaultName()); + Assert.Equal("Specialty", SpecialtyCK.GetDefaultName( StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema))); var customerTable = customerType.GetTableMappings().Last().Table; @@ -660,41 +679,50 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Null(abstractCustomerType.GetTableName()); Assert.Equal(nameof(SpecialCustomer), specialCustomerType.GetTableName()); Assert.Equal(3, specialCustomerType.GetTableMappings().Count()); - Assert.True(specialCustomerType.GetTableMappings().First().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetTableMappings().First().IsSplitEntityTypePrincipal); Assert.False(specialCustomerType.GetTableMappings().First().IncludesDerivedTypes); - Assert.True(specialCustomerType.GetTableMappings().Last().IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerType.GetTableMappings().Last().IsSplitEntityTypePrincipal); Assert.True(specialCustomerType.GetTableMappings().Last().IncludesDerivedTypes); Assert.Equal("SpecialCustomer", specialCustomerTable.Name); - Assert.Equal(6, specialCustomerTable.Columns.Count()); + Assert.Equal(7, specialCustomerTable.Columns.Count()); Assert.True( specialCustomerTable.EntityTypeMappings.Single(m => m.EntityType == specialCustomerType).IsSharedTablePrincipal); - var specialityColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); - Assert.False(specialityColumn.IsNullable); + var specialtyColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); + Assert.False(specialtyColumn.IsNullable); var addressColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Details) + "_" + nameof(CustomerDetails.Address)); Assert.False(addressColumn.IsNullable); - var specialityProperty = specialityColumn.PropertyMappings.First().Property; + var specialtyProperty = specialtyColumn.PropertyMappings.First().Property; Assert.Equal( RelationalStrings.PropertyNotMappedToTable( - nameof(SpecialCustomer.Speciality), nameof(SpecialCustomer), "Customer"), + nameof(SpecialCustomer.Specialty), nameof(SpecialCustomer), "Customer"), Assert.Throws(() => - specialityProperty.IsColumnNullable(StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))) + specialtyProperty.IsColumnNullable(StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))) .Message); var abstractStringColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(AbstractCustomer.AbstractString)); - Assert.False(specialityColumn.IsNullable); - Assert.Equal(2, specialityColumn.PropertyMappings.Count()); + Assert.False(specialtyColumn.IsNullable); + Assert.Equal(2, specialtyColumn.PropertyMappings.Count); + + var abstractStringProperty = abstractStringColumn.PropertyMappings.First().Property; + Assert.Equal(2, abstractStringProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema) + }, + abstractStringProperty.GetMappedStoreObjects(StoreObjectType.Table)); var extraSpecialCustomerTable = extraSpecialCustomerType.GetTableMappings().Select(t => t.Table).First(t => t.Name == "ExtraSpecialCustomer"); Assert.Empty(customerTable.CheckConstraints); - Assert.Same(specialityCK, specialCustomerTable.CheckConstraints.Single()); + Assert.Same(SpecialtyCK, specialCustomerTable.CheckConstraints.Single()); Assert.Empty(extraSpecialCustomerTable.CheckConstraints); Assert.Equal(4, customerPk.GetMappedConstraints().Count()); @@ -704,6 +732,15 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) var idProperty = customerPk.Properties.Single(); Assert.Equal(10, idProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(baseTable.Name, baseTable.Schema), + StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema), + StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema), + StoreObjectIdentifier.Table(extraSpecialCustomerTable.Name, extraSpecialCustomerTable.Schema) + }, + idProperty.GetMappedStoreObjects(StoreObjectType.Table)); var customerFk = customerTable.ForeignKeyConstraints.Single(); Assert.Equal("FK_Customer_AbstractBase_Id", customerFk.Name); @@ -732,7 +769,7 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))); var specialCustomerUniqueConstraint = baseTable.UniqueConstraints.Single(c => !c.GetIsPrimaryKey()); - Assert.Equal("AK_AbstractBase_SpecialityAk", specialCustomerUniqueConstraint.Name); + Assert.Equal("AK_AbstractBase_SpecialtyAk", specialCustomerUniqueConstraint.Name); Assert.NotNull(specialCustomerUniqueConstraint.MappedKeys.Single()); var foreignKeys = specialCustomerTable.ForeignKeyConstraints.ToArray(); @@ -740,7 +777,7 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Equal(3, foreignKeys.Length); var specialCustomerFkConstraint = foreignKeys[0]; - Assert.Equal("FK_SpecialCustomer_AbstractBase_RelatedCustomerSpeciality", specialCustomerFkConstraint.Name); + Assert.Equal("FK_SpecialCustomer_AbstractBase_RelatedCustomerSpecialty", specialCustomerFkConstraint.Name); Assert.NotNull(specialCustomerFkConstraint.MappedForeignKeys.Single()); Assert.Same(baseTable, specialCustomerFkConstraint.PrincipalTable); @@ -757,7 +794,7 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Equal(new[] { orderCustomerFkConstraint, specialCustomerTptFkConstraint }, customerTable.ReferencingForeignKeyConstraints); var specialCustomerDbIndex = specialCustomerTable.Indexes.Last(); - Assert.Equal("IX_SpecialCustomer_RelatedCustomerSpeciality", specialCustomerDbIndex.Name); + Assert.Equal("IX_SpecialCustomer_RelatedCustomerSpecialty", specialCustomerDbIndex.Name); Assert.NotNull(specialCustomerDbIndex.MappedIndexes.Single()); var anotherSpecialCustomerDbIndex = specialCustomerTable.Indexes.First(); @@ -772,11 +809,9 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) else { var specialCustomerTypeMapping = specialCustomerType.GetTableMappings().Single(); - Assert.True(specialCustomerTypeMapping.IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerTypeMapping.IsSplitEntityTypePrincipal); - var specialityColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); - var addressColumn = specialCustomerTable.Columns.Single(c => - c.Name == nameof(SpecialCustomer.Details) + "_" + nameof(CustomerDetails.Address)); + var specialtyColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); var specialCustomerPkConstraint = specialCustomerTable.PrimaryKey; var specialCustomerUniqueConstraint = specialCustomerTable.UniqueConstraints.Single(c => !c.GetIsPrimaryKey()); @@ -801,11 +836,35 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.True(specialCustomerTable.EntityTypeMappings.First().IsSharedTablePrincipal); Assert.False(specialCustomerTable.EntityTypeMappings.Last().IsSharedTablePrincipal); - Assert.Equal(11, specialCustomerTable.Columns.Count()); + Assert.Equal(12, specialCustomerTable.Columns.Count()); - Assert.True(specialityColumn.IsNullable); + var addressColumn = specialCustomerTable.Columns.Single(c => + c.Name == nameof(SpecialCustomer.Details) + "_" + nameof(CustomerDetails.Address)); + + Assert.True(specialtyColumn.IsNullable); Assert.True(addressColumn.IsNullable); + var abstractStringColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(AbstractCustomer.AbstractString)); + Assert.True(specialtyColumn.IsNullable); + Assert.Equal(2, specialtyColumn.PropertyMappings.Count); + + var abstractStringProperty = abstractStringColumn.PropertyMappings.First().Property; + Assert.Equal(3, abstractStringProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema) + }, + abstractStringProperty.GetMappedStoreObjects(StoreObjectType.Table)); + + Assert.Equal(5, idProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema) + }, + idProperty.GetMappedStoreObjects(StoreObjectType.Table)); + var orderCustomerFkConstraint = orderCustomerFk.GetMappedConstraints().Single(); Assert.Equal("FK_Order_" + baseTable.Name + "_CustomerId", orderCustomerFkConstraint.Name); @@ -828,10 +887,10 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))); Assert.Equal("PK_" + baseTable.Name, specialCustomerPkConstraint.Name); - Assert.Equal("AK_AbstractBase_SpecialityAk", specialCustomerUniqueConstraint.Name); + Assert.Equal("AK_AbstractBase_SpecialtyAk", specialCustomerUniqueConstraint.Name); var specialCustomerFkConstraint = specialCustomerTable.ForeignKeyConstraints.Last(); - Assert.Equal("FK_AbstractBase_AbstractBase_RelatedCustomerSpeciality", specialCustomerFkConstraint.Name); + Assert.Equal("FK_AbstractBase_AbstractBase_RelatedCustomerSpecialty", specialCustomerFkConstraint.Name); Assert.NotNull(specialCustomerFkConstraint.MappedForeignKeys.Single()); var anotherSpecialCustomerFkConstraint = specialCustomerTable.ForeignKeyConstraints.First(); @@ -841,7 +900,7 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Equal(new[] { anotherSpecialCustomerFkConstraint, specialCustomerFkConstraint, orderCustomerFkConstraint }, customerTable.ReferencingForeignKeyConstraints); - Assert.Equal("IX_AbstractBase_RelatedCustomerSpeciality", specialCustomerDbIndex.Name); + Assert.Equal("IX_AbstractBase_RelatedCustomerSpecialty", specialCustomerDbIndex.Name); Assert.Equal("IX_AbstractBase_AnotherRelatedCustomerId", anotherSpecialCustomerDbIndex.Name); Assert.Equal(5, idProperty.GetTableColumnMappings().Count()); @@ -859,18 +918,43 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) Assert.Empty(ordersTable.ReferencingForeignKeyConstraints); Assert.Empty(customerTable.ReferencingForeignKeyConstraints); - Assert.True(customerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); + Assert.Null(customerTable.EntityTypeMappings.Single().IsSharedTablePrincipal); Assert.Equal(5, customerTable.Columns.Count()); - Assert.Equal(2, specialCustomerTable.EntityTypeMappings.Count()); - Assert.True(specialCustomerTable.EntityTypeMappings.First().IsSharedTablePrincipal); - Assert.False(specialCustomerTable.EntityTypeMappings.Last().IsSharedTablePrincipal); + Assert.Single(specialCustomerTable.EntityTypeMappings); - Assert.Equal(10, specialCustomerTable.Columns.Count()); + var abstractStringColumn = specialCustomerTable.Columns.Single(c => c.Name == nameof(AbstractCustomer.AbstractString)); + Assert.False(specialtyColumn.IsNullable); - Assert.False(specialityColumn.IsNullable); + var extraSpecialCustomerTable = + extraSpecialCustomerType.GetTableMappings().Select(t => t.Table).First(t => t.Name == "ExtraSpecialCustomer"); + + Assert.Equal(2, extraSpecialCustomerTable.EntityTypeMappings.Count()); + + var addressColumn = extraSpecialCustomerTable.Columns.Single(c => + c.Name == nameof(SpecialCustomer.Details) + "_" + nameof(CustomerDetails.Address)); Assert.False(addressColumn.IsNullable); + var abstractStringProperty = abstractStringColumn.PropertyMappings.Single().Property; + Assert.Equal(2, abstractStringProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema), + StoreObjectIdentifier.Table(extraSpecialCustomerTable.Name, extraSpecialCustomerTable.Schema) + }, + abstractStringProperty.GetMappedStoreObjects(StoreObjectType.Table)); + + Assert.Equal(3, idProperty.GetTableColumnMappings().Count()); + Assert.Equal( + new[] + { + StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema), + StoreObjectIdentifier.Table(specialCustomerTable.Name, specialCustomerTable.Schema), + StoreObjectIdentifier.Table(extraSpecialCustomerTable.Name, extraSpecialCustomerTable.Schema) + }, + idProperty.GetMappedStoreObjects(StoreObjectType.Table)); + // Derived principal entity types are mapped to different tables, so the constraint is not enforceable Assert.Empty(orderCustomerFk.GetMappedConstraints()); @@ -884,11 +968,11 @@ private static void AssertTables(IRelationalModel model, Mapping mapping) StoreObjectIdentifier.Table(customerTable.Name, customerTable.Schema))); Assert.Equal("PK_SpecialCustomer", specialCustomerPkConstraint.Name); - Assert.Equal("AK_SpecialCustomer_SpecialityAk", specialCustomerUniqueConstraint.Name); + Assert.Equal("AK_SpecialCustomer_SpecialtyAk", specialCustomerUniqueConstraint.Name); Assert.Empty(specialCustomerTable.ForeignKeyConstraints); - Assert.Equal("IX_SpecialCustomer_RelatedCustomerSpeciality", specialCustomerDbIndex.Name); + Assert.Equal("IX_SpecialCustomer_RelatedCustomerSpecialty", specialCustomerDbIndex.Name); Assert.Equal("IX_SpecialCustomer_AnotherRelatedCustomerId", anotherSpecialCustomerDbIndex.Name); Assert.Equal(3, idProperty.GetTableColumnMappings().Count()); @@ -919,7 +1003,7 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie if (mapToTables) { - cb.ToTable(t => { }); + cb.ToTable(_ => { }); } } @@ -928,13 +1012,14 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie cb.UseTpcMappingStrategy(); } else if (mapping == Mapping.TPT - && (!mapToTables && !mapToViews)) + && !mapToTables + && !mapToViews) { cb.UseTptMappingStrategy(); } // TODO: Don't map it on the base #19811 - cb.Property("SpecialityAk"); + cb.Property("SpecialtyAk"); }); modelBuilder.Entity( @@ -979,19 +1064,39 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie cb.ToTable("SpecialCustomer", "SpecialSchema"); } } - cb.HasCheckConstraint($"Speciality", $"[Speciality] IN ('Specialist', 'Generalist')"); + cb.HasCheckConstraint("Specialty", "[Specialty] IN ('Specialist', 'Generalist')"); - cb.Property(s => s.Speciality).IsRequired(); + cb.Property(s => s.Specialty).IsRequired(); cb.HasOne(c => c.RelatedCustomer).WithOne() - .HasForeignKey(c => c.RelatedCustomerSpeciality) - .HasPrincipalKey("SpecialityAk"); // TODO: Use the derived one, #2611 + .HasForeignKey(c => c.RelatedCustomerSpecialty) + .HasPrincipalKey("SpecialtyAk"); // TODO: Use the derived one, #2611 cb.HasOne().WithOne() .HasForeignKey("AnotherRelatedCustomerId"); - cb.OwnsOne(c => c.Details).Property(d => d.Address).IsRequired(); - cb.Navigation(c => c.Details).IsRequired(); + if (mapping == Mapping.TPC) + { + cb.Ignore(c => c.Details); + } + else + { + cb.OwnsOne(c => c.Details).Property(d => d.Address).IsRequired(); + cb.Navigation(c => c.Details).IsRequired(); + + if (mapping == Mapping.TPT) + { + if (mapToViews) + { + cb.OwnsOne(c => c.Details, cdb => cdb.ToView("SpecialCustomerView")); + } + + if (mapToTables) + { + cb.OwnsOne(c => c.Details, cdb => cdb.ToTable("SpecialCustomer", "SpecialSchema")); + } + } + } }); modelBuilder.Entity( @@ -1009,6 +1114,22 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie cb.ToTable("ExtraSpecialCustomer", "ExtraSpecialSchema"); } } + + if (mapping == Mapping.TPC) + { + cb.OwnsOne(c => c.Details).Property(d => d.Address).IsRequired(); + cb.Navigation(c => c.Details).IsRequired(); + + if (mapToViews) + { + cb.OwnsOne(c => c.Details, cdb => cdb.ToView("ExtraSpecialCustomerView")); + } + + if (mapToTables) + { + cb.OwnsOne(c => c.Details, cdb => cdb.ToTable("ExtraSpecialCustomer", "ExtraSpecialSchema")); + } + } }); modelBuilder.Entity( @@ -1040,6 +1161,8 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie odb.OwnsOne(od => od.BillingAddress); odb.OwnsOne(od => od.ShippingAddress); + odb.Navigation(od => od.BillingAddress).IsRequired(); + odb.Navigation(od => od.ShippingAddress).IsRequired(); }); if (mapToViews) @@ -1077,6 +1200,433 @@ private IRelationalModel CreateTestModel(bool mapToTables = false, bool mapToVie return Finalize(modelBuilder); } + [ConditionalTheory] + [InlineData(true)] + [InlineData(false)] + public void Can_use_relational_model_with_entity_splitting_and_table_splitting_on_both_fragments(bool mapToViews) + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Ignore(); + modelBuilder.Ignore(); + + modelBuilder.Entity( + cb => + { + cb.Ignore(c => c.Orders); + cb.Ignore(c => c.RelatedCustomer); + + if (mapToViews) + { + cb.ToView("CustomerView", tb => + { + tb.Property(c => c.AbstractString); + }); + + cb.SplitToView("CustomerDetailsView", tb => + { + tb.Property(c => c.AbstractString); + tb.Property(c => c.Specialty); + tb.Property(c => c.RelatedCustomerSpecialty); + }); + } + else + { + cb.ToTable("Customer", tb => + { + tb.Property(c => c.AbstractString); + }); + + cb.SplitToTable("CustomerDetails", tb => + { + tb.Property(c => c.AbstractString); + tb.Property(c => c.Specialty); + tb.Property(c => c.RelatedCustomerSpecialty); + }); + } + + cb.OwnsOne(c => c.Details, db => + { + if (mapToViews) + { + db.ToView("CustomerView"); + + db.SplitToView("CustomerDetailsView", tb => + { + tb.Property(d => d.BirthDay); + }); + } + else + { + db.SplitToTable("CustomerDetails", tb => + { + tb.Property(d => d.BirthDay); + }); + } + db.Property("SpecialCustomerId").HasColumnName("Id"); + }); + cb.Navigation(c => c.Details).IsRequired(); + }); + + var model = Finalize(modelBuilder); + var customerType = model.Model.FindEntityType(typeof(SpecialCustomer)); + + var detailsNavigation = customerType.FindNavigation(nameof(SpecialCustomer.Details)); + var detailsType = detailsNavigation.TargetEntityType; + + Assert.Equal(2, model.Model.GetEntityTypes().Count()); + if (mapToViews) + { + Assert.Empty(model.Tables); + Assert.Equal(2, model.Views.Count()); + + var customerView = model.Views.Single(t => t.Name == "CustomerView"); + + Assert.Equal(2, customerView.EntityTypeMappings.Count()); + + var customerMapping = customerView.EntityTypeMappings.First(); + Assert.True(customerMapping.IsSharedTablePrincipal); + Assert.True(customerMapping.IsSplitEntityTypePrincipal); + var detailsMapping = customerView.EntityTypeMappings.Last(); + Assert.False(detailsMapping.IsSharedTablePrincipal); + Assert.True(detailsMapping.IsSplitEntityTypePrincipal); + + var customerDetailsView = model.Views.Single(t => t.Name == "CustomerDetailsView"); + + Assert.Equal(new[] { customerView, customerDetailsView }, + customerType.GetViewMappings().Select(m => m.View)); + + Assert.Equal(2, customerDetailsView.EntityTypeMappings.Count()); + + var customerSplitMapping = customerDetailsView.EntityTypeMappings.First(); + Assert.True(customerSplitMapping.IsSharedTablePrincipal); + Assert.False(customerSplitMapping.IsSplitEntityTypePrincipal); + var detailsSplitMapping = customerDetailsView.EntityTypeMappings.Last(); + Assert.False(detailsSplitMapping.IsSharedTablePrincipal); + Assert.False(detailsSplitMapping.IsSplitEntityTypePrincipal); + + Assert.Equal(new[] { customerView, customerDetailsView }, + detailsType.GetViewMappings().Select(m => m.View)); + + Assert.Equal(new[] { "AbstractString", "Details_Address", "EnumValue", "Id", "Name", "SomeShort" }, + customerView.Columns.Select(t => t.Name)); + + Assert.Equal(new[] { "AbstractString", "Details_BirthDay", "Id", "RelatedCustomerSpecialty", "Specialty" }, + customerDetailsView.Columns.Select(t => t.Name)); + } + else + { + Assert.Empty(model.Views); + Assert.Equal(2, model.Tables.Count()); + + var customerTable = model.Tables.Single(t => t.Name == "Customer"); + + Assert.Equal(2, customerTable.EntityTypeMappings.Count()); + + var customerMapping = customerTable.EntityTypeMappings.First(); + Assert.True(customerMapping.IsSharedTablePrincipal); + Assert.True(customerMapping.IsSplitEntityTypePrincipal); + var detailsMapping = customerTable.EntityTypeMappings.Last(); + Assert.False(detailsMapping.IsSharedTablePrincipal); + Assert.True(detailsMapping.IsSplitEntityTypePrincipal); + + var customerDetailsTable = model.Tables.Single(t => t.Name == "CustomerDetails"); + + Assert.Equal(new[] { customerTable, customerDetailsTable }, + customerType.GetTableMappings().Select(m => m.Table)); + + Assert.Equal(2, customerDetailsTable.EntityTypeMappings.Count()); + + var customerSplitMapping = customerDetailsTable.EntityTypeMappings.First(); + Assert.True(customerSplitMapping.IsSharedTablePrincipal); + Assert.False(customerSplitMapping.IsSplitEntityTypePrincipal); + var detailsSplitMapping = customerDetailsTable.EntityTypeMappings.Last(); + Assert.False(detailsSplitMapping.IsSharedTablePrincipal); + Assert.False(detailsSplitMapping.IsSplitEntityTypePrincipal); + + Assert.Equal(new[] { customerTable, customerDetailsTable }, + detailsType.GetTableMappings().Select(m => m.Table)); + + Assert.Single(customerTable.UniqueConstraints); + Assert.Empty(customerTable.ForeignKeyConstraints); + Assert.Empty(customerTable.Indexes); + Assert.Empty(customerTable.GetRowInternalForeignKeys(customerType)); + Assert.Single(customerTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal(new[] { "Id", "AbstractString", "Details_Address", "EnumValue", "Name", "SomeShort" }, + customerTable.Columns.Select(t => t.Name)); + + Assert.Single(customerDetailsTable.UniqueConstraints); + var fkConstraint = customerDetailsTable.ForeignKeyConstraints.Single(); + Assert.Empty(customerDetailsTable.Indexes); + Assert.Empty(customerDetailsTable.GetRowInternalForeignKeys(customerType)); + Assert.Single(customerDetailsTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal(new[] { "Id", "AbstractString", "Details_BirthDay", "RelatedCustomerSpecialty", "Specialty" }, + customerDetailsTable.Columns.Select(t => t.Name)); + + Assert.Equal(2, fkConstraint.MappedForeignKeys.Count()); + Assert.All(fkConstraint.MappedForeignKeys, + fk => + { + Assert.True(fk.IsUnique); + Assert.True(fk.IsRequired); + Assert.True(fk.IsRequiredDependent); + }); + } + } + + [ConditionalFact] + public void Can_use_relational_model_with_entity_splitting_and_table_splitting_on_main_fragments() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Ignore(); + modelBuilder.Ignore(); + + modelBuilder.Entity( + cb => + { + cb.Ignore(c => c.Orders); + cb.Ignore(c => c.RelatedCustomer); + + cb.ToTable( + "Customer", tb => + { + tb.Property(c => c.AbstractString); + }); + + cb.SplitToTable( + "CustomerSpecialty", tb => + { + tb.Property(c => c.AbstractString); + tb.Property(c => c.Specialty); + tb.Property(c => c.RelatedCustomerSpecialty); + }); + + cb.OwnsOne( + c => c.Details, db => + { + db.SplitToTable( + "CustomerDetails", tb => + { + tb.Property(d => d.BirthDay); + }); + db.Property("SpecialCustomerId").HasColumnName("Id"); + }); + cb.Navigation(c => c.Details).IsRequired(); + }); + + var model = Finalize(modelBuilder); + var customerType = model.Model.FindEntityType(typeof(SpecialCustomer)); + + var detailsNavigation = customerType.FindNavigation(nameof(SpecialCustomer.Details)); + var detailsType = detailsNavigation.TargetEntityType; + + Assert.Equal(2, model.Model.GetEntityTypes().Count()); + Assert.Empty(model.Views); + Assert.Equal(3, model.Tables.Count()); + + var customerTable = model.Tables.Single(t => t.Name == "Customer"); + + Assert.Equal(2, customerTable.EntityTypeMappings.Count()); + + var customerMapping = customerTable.EntityTypeMappings.First(); + Assert.True(customerMapping.IsSharedTablePrincipal); + Assert.True(customerMapping.IsSplitEntityTypePrincipal); + var detailsMapping = customerTable.EntityTypeMappings.Last(); + Assert.False(detailsMapping.IsSharedTablePrincipal); + Assert.True(detailsMapping.IsSplitEntityTypePrincipal); + + var customerDetailsTable = model.Tables.Single(t => t.Name == "CustomerDetails"); + + Assert.Equal( + new[] { customerTable, customerDetailsTable }, + detailsType.GetTableMappings().Select(m => m.Table)); + + var detailsSplitMapping = customerDetailsTable.EntityTypeMappings.Single(); + Assert.Null(detailsSplitMapping.IsSharedTablePrincipal); + Assert.False(detailsSplitMapping.IsSplitEntityTypePrincipal); + + var customerSpecialtyTable = model.Tables.Single(t => t.Name == "CustomerSpecialty"); + + Assert.Equal( + new[] { customerTable, customerSpecialtyTable }, + customerType.GetTableMappings().Select(m => m.Table)); + + var customerSplitMapping = customerSpecialtyTable.EntityTypeMappings.Single(); + Assert.Null(customerSplitMapping.IsSharedTablePrincipal); + Assert.False(customerSplitMapping.IsSplitEntityTypePrincipal); + + Assert.Single(customerTable.UniqueConstraints); + Assert.Empty(customerTable.ForeignKeyConstraints); + Assert.Empty(customerTable.Indexes); + Assert.Empty(customerTable.GetRowInternalForeignKeys(customerType)); + Assert.Single(customerTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal( + new[] { "Id", "AbstractString", "Details_Address", "EnumValue", "Name", "SomeShort" }, + customerTable.Columns.Select(t => t.Name)); + + Assert.Single(customerDetailsTable.UniqueConstraints); + var detailsFkConstraint = customerDetailsTable.ForeignKeyConstraints.Single(); + Assert.Empty(customerDetailsTable.Indexes); + Assert.Empty(customerDetailsTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal( + new[] { "Id", "BirthDay" }, + customerDetailsTable.Columns.Select(t => t.Name)); + + var detailsFk = detailsFkConstraint.MappedForeignKeys.Single(); + + Assert.True(detailsFk.IsUnique); + Assert.True(detailsFk.IsRequired); + Assert.True(detailsFk.IsRequiredDependent); + + Assert.Single(customerSpecialtyTable.UniqueConstraints); + var specialtyFkConstraint = customerSpecialtyTable.ForeignKeyConstraints.Single(); + Assert.Empty(customerSpecialtyTable.Indexes); + Assert.Empty(customerSpecialtyTable.GetRowInternalForeignKeys(customerType)); + Assert.Equal( + new[] { "Id", "AbstractString", "RelatedCustomerSpecialty", "Specialty" }, + customerSpecialtyTable.Columns.Select(t => t.Name)); + + var specialtyFk = specialtyFkConstraint.MappedForeignKeys.Single(); + + Assert.True(specialtyFk.IsUnique); + Assert.True(specialtyFk.IsRequired); + Assert.True(specialtyFk.IsRequiredDependent); + } + + [ConditionalFact] + public void Can_use_relational_model_with_entity_splitting_and_table_splitting_on_leaf_and_main_fragments() + { + var modelBuilder = CreateConventionModelBuilder(); + + modelBuilder.Ignore(); + modelBuilder.Ignore(); + + modelBuilder.Entity( + cb => + { + cb.Ignore(c => c.Orders); + cb.Ignore(c => c.RelatedCustomer); + + cb.ToTable( + "Customer", tb => + { + tb.Property(c => c.AbstractString); + }); + + cb.SplitToTable( + "CustomerDetails", tb => + { + tb.Property(c => c.AbstractString); + tb.Property(c => c.Specialty); + tb.Property(c => c.RelatedCustomerSpecialty); + }); + + cb.OwnsOne( + c => c.Details, db => + { + db.ToTable("CustomerDetails"); + + db.SplitToTable( + "Details", tb => + { + tb.Property(d => d.BirthDay); + }); + db.Property("SpecialCustomerId").HasColumnName("Id"); + }); + cb.Navigation(c => c.Details).IsRequired(); + }); + + var model = Finalize(modelBuilder); + var customerType = model.Model.FindEntityType(typeof(SpecialCustomer)); + + var detailsNavigation = customerType.FindNavigation(nameof(SpecialCustomer.Details)); + var detailsType = detailsNavigation.TargetEntityType; + + Assert.Equal(2, model.Model.GetEntityTypes().Count()); + Assert.Empty(model.Views); + Assert.Equal(3, model.Tables.Count()); + + var customerTable = model.Tables.Single(t => t.Name == "Customer"); + + var customerMapping = customerTable.EntityTypeMappings.Single(); + Assert.Null(customerMapping.IsSharedTablePrincipal); + Assert.True(customerMapping.IsSplitEntityTypePrincipal); + + var customerDetailsTable = model.Tables.Single(t => t.Name == "CustomerDetails"); + + Assert.Equal( + new[] { customerTable, customerDetailsTable }, + customerType.GetTableMappings().Select(m => m.Table)); + + Assert.Equal(2, customerDetailsTable.EntityTypeMappings.Count()); + + var customerSplitMapping = customerDetailsTable.EntityTypeMappings.First(); + Assert.True(customerSplitMapping.IsSharedTablePrincipal); + Assert.False(customerSplitMapping.IsSplitEntityTypePrincipal); + var detailsMapping = customerDetailsTable.EntityTypeMappings.Last(); + Assert.False(detailsMapping.IsSharedTablePrincipal); + Assert.True(detailsMapping.IsSplitEntityTypePrincipal); + + var detailsTable = model.Tables.Single(t => t.Name == "Details"); + + Assert.Equal( + new[] { customerDetailsTable, detailsTable }, + detailsType.GetTableMappings().Select(m => m.Table)); + + var detailsSplitMapping = detailsTable.EntityTypeMappings.Single(); + Assert.Null(detailsSplitMapping.IsSharedTablePrincipal); + Assert.False(detailsSplitMapping.IsSplitEntityTypePrincipal); + + Assert.Single(customerTable.UniqueConstraints); + Assert.Empty(customerTable.ForeignKeyConstraints); + Assert.Empty(customerTable.Indexes); + Assert.Empty(customerTable.GetRowInternalForeignKeys(customerType)); + Assert.Equal( + new[] { "Id", "AbstractString", "EnumValue", "Name", "SomeShort" }, + customerTable.Columns.Select(t => t.Name)); + + Assert.Single(customerDetailsTable.UniqueConstraints); + var customerDetailsFkConstraint = customerDetailsTable.ForeignKeyConstraints.Single(); + Assert.Empty(customerDetailsTable.Indexes); + Assert.Empty(customerDetailsTable.GetRowInternalForeignKeys(customerType)); + Assert.Single(customerDetailsTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal( + new[] { "Id", "AbstractString", "Details_Address", "RelatedCustomerSpecialty", "Specialty" }, + customerDetailsTable.Columns.Select(t => t.Name)); + + Assert.Equal(2, customerDetailsFkConstraint.MappedForeignKeys.Count()); + + var customerFk = customerDetailsFkConstraint.MappedForeignKeys.First(); + + Assert.True(customerFk.IsUnique); + Assert.True(customerFk.IsRequired); + Assert.True(customerFk.IsRequiredDependent); + Assert.Same(customerType, customerFk.DeclaringEntityType); + + var customerDetailsFk = customerDetailsFkConstraint.MappedForeignKeys.Last(); + + Assert.True(customerDetailsFk.IsUnique); + Assert.True(customerDetailsFk.IsRequired); + Assert.True(customerDetailsFk.IsRequiredDependent); + Assert.Same(detailsType, customerDetailsFk.DeclaringEntityType); + + Assert.Single(detailsTable.UniqueConstraints); + var detailsFkConstraint = detailsTable.ForeignKeyConstraints.Single(); + Assert.Empty(detailsTable.Indexes); + Assert.Empty(detailsTable.GetRowInternalForeignKeys(detailsType)); + Assert.Equal( + new[] { "Id", "BirthDay" }, + detailsTable.Columns.Select(t => t.Name)); + + var detailsFk = detailsFkConstraint.MappedForeignKeys.Last(); + + Assert.True(detailsFk.IsUnique); + Assert.True(detailsFk.IsRequired); + Assert.True(detailsFk.IsRequiredDependent); + Assert.Same(detailsType, detailsFk.DeclaringEntityType); + } + [ConditionalFact] public void Can_use_relational_model_with_keyless_TPH() { @@ -1093,7 +1643,7 @@ public void Can_use_relational_model_with_keyless_TPH() cb => { cb.Ignore(c => c.Details); - cb.Property(s => s.Speciality).IsRequired(); + cb.Property(s => s.Specialty).IsRequired(); }); var model = Finalize(modelBuilder); @@ -1111,7 +1661,7 @@ public void Can_use_relational_model_with_keyless_TPH() var specialCustomerType = model.Model.FindEntityType(typeof(SpecialCustomer)); var specialCustomerTypeMapping = specialCustomerType.GetViewMappings().Single(); - Assert.True(specialCustomerTypeMapping.IsSplitEntityTypePrincipal); + Assert.Null(specialCustomerTypeMapping.IsSplitEntityTypePrincipal); var specialCustomerView = specialCustomerTypeMapping.View; Assert.Same(customerView, specialCustomerView); @@ -1120,8 +1670,8 @@ public void Can_use_relational_model_with_keyless_TPH() Assert.True(specialCustomerView.EntityTypeMappings.First().IsSharedTablePrincipal); Assert.False(specialCustomerView.EntityTypeMappings.Last().IsSharedTablePrincipal); - var specialityColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Speciality)); - Assert.True(specialityColumn.IsNullable); + var specialtyColumn = specialCustomerView.Columns.Single(c => c.Name == nameof(SpecialCustomer.Specialty)); + Assert.True(specialtyColumn.IsNullable); } [ConditionalFact] @@ -1152,8 +1702,8 @@ public void Can_use_relational_model_with_SQL_queries() Assert.Null(orderType.FindPrimaryKey()); var orderMapping = orderType.GetSqlQueryMappings().Single(); - Assert.True(orderMapping.IsSharedTablePrincipal); - Assert.True(orderMapping.IsSplitEntityTypePrincipal); + Assert.Null(orderMapping.IsSharedTablePrincipal); + Assert.Null(orderMapping.IsSplitEntityTypePrincipal); Assert.True(orderMapping.IncludesDerivedTypes); Assert.Equal( @@ -1227,13 +1777,13 @@ public void Can_use_relational_model_with_functions() Assert.Equal(2, orderType.GetFunctionMappings().Count()); var orderMapping = orderType.GetFunctionMappings().First(); - Assert.True(orderMapping.IsSharedTablePrincipal); - Assert.True(orderMapping.IsSplitEntityTypePrincipal); + Assert.Null(orderMapping.IsSharedTablePrincipal); + Assert.Null(orderMapping.IsSplitEntityTypePrincipal); Assert.True(orderMapping.IsDefaultFunctionMapping); var tvfMapping = orderType.GetFunctionMappings().Last(); - Assert.True(tvfMapping.IsSharedTablePrincipal); - Assert.True(tvfMapping.IsSplitEntityTypePrincipal); + Assert.Null(tvfMapping.IsSharedTablePrincipal); + Assert.Null(tvfMapping.IsSplitEntityTypePrincipal); Assert.False(tvfMapping.IsDefaultFunctionMapping); Assert.True(orderMapping.IncludesDerivedTypes); @@ -1321,7 +1871,12 @@ private static IRelationalModel Finalize(TestHelpers.TestModelBuilder modelBuild => modelBuilder.FinalizeModel(designTime: true).GetRelationalModel(); protected virtual TestHelpers.TestModelBuilder CreateConventionModelBuilder() - => RelationalTestHelpers.Instance.CreateConventionBuilder(); + => RelationalTestHelpers.Instance.CreateConventionBuilder( + configureContext: b => + b.ConfigureWarnings(w => + w.Default(WarningBehavior.Throw) + .Ignore(RelationalEventId.ForeignKeyTpcPrincipalWarning) + .Ignore(RelationalEventId.AllIndexPropertiesNotToMappedToAnyTable))); public enum Mapping { @@ -1362,8 +1917,8 @@ private abstract class AbstractCustomer : Customer private class SpecialCustomer : AbstractCustomer { - public string Speciality { get; set; } - public string RelatedCustomerSpeciality { get; set; } + public string Specialty { get; set; } + public string RelatedCustomerSpecialty { get; set; } public SpecialCustomer RelatedCustomer { get; set; } public CustomerDetails Details { get; set; } } @@ -1371,6 +1926,7 @@ private class SpecialCustomer : AbstractCustomer private class CustomerDetails { public string Address { get; set; } + public DateTime BirthDay { get; set; } } private class ExtraSpecialCustomer : SpecialCustomer diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTestBase.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTestBase.cs index f9de90f00ce..c29f28e996b 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTestBase.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTestBase.cs @@ -116,7 +116,7 @@ protected static T[][] ToJaggedArray(T[,] twoDimensionalArray, bool firstDime protected abstract TestHelpers TestHelpers { get; } protected virtual TestHelpers.TestModelBuilder CreateModelBuilder(bool skipConventions) - => TestHelpers.CreateConventionBuilder(configure: skipConventions ? c => c.RemoveAllConventions() : null); + => TestHelpers.CreateConventionBuilder(configureModel: skipConventions ? c => c.RemoveAllConventions() : null); protected virtual MigrationsModelDiffer CreateModelDiffer(DbContextOptions options) => new MigrationsModelDiffer( diff --git a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs index dcfad442eb5..2ea5e22e0d4 100644 --- a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs +++ b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs @@ -230,9 +230,6 @@ public override typeof(RelationalIndexBuilderExtensions).GetMethod( nameof(RelationalIndexBuilderExtensions.HasName), new[] { typeof(IndexBuilder), typeof(string) }), - typeof(RelationalEntityTypeExtensions).GetMethod( - nameof(RelationalEntityTypeExtensions.GetMappingFragments), - new[] { typeof(IReadOnlyEntityType) }), typeof(RelationalPropertyExtensions).GetMethod( nameof(RelationalPropertyExtensions.FindOverrides), new[] { typeof(IReadOnlyProperty), typeof(StoreObjectIdentifier).MakeByRefType() }), diff --git a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs index d69c906204d..46ad3f4e069 100644 --- a/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs +++ b/test/EFCore.Relational.Tests/Storage/RelationalTypeMapperTest.cs @@ -345,5 +345,5 @@ public static RelationalTypeMapping GetMapping( => typeMappingSource.FindMapping(property); protected override ModelBuilder CreateModelBuilder(Action configure = null) - => RelationalTestHelpers.Instance.CreateConventionBuilder(configure: configure); + => RelationalTestHelpers.Instance.CreateConventionBuilder(configureModel: configure); } diff --git a/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs b/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs index 2eb26cd66e8..400de7b963e 100644 --- a/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs +++ b/test/EFCore.Relational.Tests/TestUtilities/RelationalTestHelpers.cs @@ -20,7 +20,7 @@ protected override EntityFrameworkDesignServicesBuilder CreateEntityFrameworkDes public override IServiceCollection AddProviderServices(IServiceCollection services) => FakeRelationalOptionsExtension.AddEntityFrameworkRelationalDatabase(services); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseFakeRelational(); public override LoggingDefinitions LoggingDefinitions { get; } = new TestRelationalLoggingDefinitions(); diff --git a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs index 535b8402c78..75a320006b4 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/TestHelpers.cs @@ -127,7 +127,7 @@ public DbContextOptionsBuilder AddProviderOptions(DbContextOptionsBuilder option return optionsBuilder; } - public abstract void UseProviderOptions(DbContextOptionsBuilder optionsBuilder); + public abstract DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder); public DbContext CreateContext(IServiceProvider serviceProvider, IModel model) => new(CreateOptions(model, serviceProvider)); @@ -187,7 +187,8 @@ public IServiceProvider CreateContextServices(IServiceCollection customServices) public TestModelBuilder CreateConventionBuilder( IDiagnosticsLogger modelLogger = null, IDiagnosticsLogger validationLogger = null, - Action configure = null, + Action configureModel = null, + Func configureContext = null, IServiceCollection customServices = null) { customServices ??= new ServiceCollection(); @@ -196,11 +197,18 @@ public TestModelBuilder CreateConventionBuilder( customServices.AddScoped(_ => modelLogger); } - validationLogger ??= new TestLogger(LoggingDefinitions); + if (validationLogger != null) + { + customServices.AddScoped(_ => validationLogger); + } - customServices.AddScoped(_ => validationLogger); + var services = configureContext == null + ? CreateContextServices(customServices) + : CreateContextServices( + customServices, + configureContext(UseProviderOptions(new DbContextOptionsBuilder())).Options); - return CreateConventionBuilder(CreateContextServices(customServices), configure, validationLogger); + return CreateConventionBuilder(services, configureModel, validationLogger); } public TestModelBuilder CreateConventionBuilder( diff --git a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs index c88dcae0eba..2f4d2624ed9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs +++ b/test/EFCore.SqlServer.FunctionalTests/TestUtilities/SqlServerTestHelpers.cs @@ -17,7 +17,7 @@ protected SqlServerTestHelpers() public override IServiceCollection AddProviderServices(IServiceCollection services) => services.AddEntityFrameworkSqlServer(); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(new SqlConnection("Database=DummyDatabase")); public override LoggingDefinitions LoggingDefinitions { get; } = new SqlServerLoggingDefinitions(); diff --git a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs index 211a2353f82..36d578920d2 100644 --- a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs +++ b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs @@ -13,7 +13,7 @@ public class SqlServerModelValidatorTest : RelationalModelValidatorTest { public override void Detects_duplicate_column_names() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(b => b.Id).HasColumnName("Name"); modelBuilder.Entity().Property(d => d.Name).IsRequired().HasColumnName("Name"); @@ -27,7 +27,7 @@ public override void Detects_duplicate_column_names() public override void Detects_duplicate_columns_in_derived_types_with_different_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Type).HasColumnName("Type").IsRequired(); @@ -41,7 +41,7 @@ public override void Detects_duplicate_columns_in_derived_types_with_different_t public override void Passes_for_ForeignKey_on_inherited_generated_composite_key_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("SomeId").ValueGeneratedOnAdd(); modelBuilder.Entity().Property("SomeOtherId").ValueGeneratedOnAdd() .Metadata.SetValueGenerationStrategy(SqlServerValueGenerationStrategy.None); @@ -55,7 +55,7 @@ public override void Passes_for_ForeignKey_on_inherited_generated_composite_key_ [ConditionalFact] public virtual void Passes_for_duplicate_column_names_within_hierarchy_with_identity() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(a => a.Id).ValueGeneratedNever(); modelBuilder.Entity( cb => @@ -74,7 +74,7 @@ public virtual void Passes_for_duplicate_column_names_within_hierarchy_with_iden [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_identity_seed() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( cb => @@ -96,7 +96,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_identity_increment() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( cb => @@ -118,7 +118,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Passes_for_identity_seed_and_increment_on_owner() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(a => a.Id).UseIdentityColumn(2, 3); modelBuilder.Entity().OwnsOne(a => a.FavoritePerson); modelBuilder.Entity().Ignore(d => d.FavoritePerson); @@ -129,7 +129,7 @@ public virtual void Passes_for_identity_seed_and_increment_on_owner() [ConditionalFact] public virtual void Passes_for_duplicate_column_names_with_HiLoSequence() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( cb => { @@ -150,7 +150,7 @@ public virtual void Passes_for_duplicate_column_names_with_HiLoSequence() [ConditionalFact] public virtual void Detects_duplicate_column_names_with_different_HiLoSequence_name() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( cb => { @@ -174,7 +174,7 @@ public virtual void Detects_duplicate_column_names_with_different_HiLoSequence_n [ConditionalFact] public virtual void Detects_duplicate_column_name_with_different_HiLoSequence_schema() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( cb => { @@ -198,7 +198,7 @@ public virtual void Detects_duplicate_column_name_with_different_HiLoSequence_sc [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_value_generation_strategy() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( cb => @@ -221,7 +221,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_sparseness() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity( cb => @@ -245,7 +245,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy_when_one_name_configured_explicitly_for_sqlServer() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); var fk1 = modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .OnDelete(DeleteBehavior.Cascade).HasConstraintName("FK_Animal_Person_Name").Metadata; @@ -261,7 +261,7 @@ public virtual void Passes_for_incompatible_foreignKeys_within_hierarchy_when_on [ConditionalFact] public virtual void Passes_for_compatible_duplicate_convention_indexes_for_foreign_keys() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasOne().WithMany().HasForeignKey(c => c.Name).HasPrincipalKey(p => p.Name) .HasConstraintName("FK_Animal_Person_Name"); @@ -277,7 +277,7 @@ public virtual void Passes_for_compatible_duplicate_convention_indexes_for_forei [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_differently_clustered() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).IsClustered().HasDatabaseName("IX_Animal_Name"); @@ -293,7 +293,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_differently_c [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_different_fill_factor() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).HasDatabaseName("IX_Animal_Name").HasFillFactor(30); @@ -309,7 +309,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_different_fil [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_differently_online() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).IsCreatedOnline().HasDatabaseName("IX_Animal_Name"); @@ -325,7 +325,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_differently_o [ConditionalFact] public virtual void Detects_duplicate_index_names_within_hierarchy_with_different_different_include() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasIndex(c => c.Name).HasDatabaseName("IX_Animal_Name"); modelBuilder.Entity().HasIndex(d => d.Name).HasDatabaseName("IX_Animal_Name").IncludeProperties(nameof(Dog.Identity)); @@ -342,7 +342,7 @@ public virtual void Detects_duplicate_index_names_within_hierarchy_with_differen [ConditionalFact] public void Detects_missing_include_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(c => c.Type); modelBuilder.Entity().HasIndex(nameof(Dog.Name)).IncludeProperties(nameof(Dog.Type), "Tag"); @@ -352,7 +352,7 @@ public void Detects_missing_include_properties() [ConditionalFact] public void Detects_duplicate_include_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(c => c.Type); modelBuilder.Entity().HasIndex(nameof(Dog.Name)).IncludeProperties(nameof(Dog.Type), nameof(Dog.Type)); @@ -362,7 +362,7 @@ public void Detects_duplicate_include_properties() [ConditionalFact] public void Detects_indexed_include_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(c => c.Type); modelBuilder.Entity().HasIndex(nameof(Dog.Name)).IncludeProperties(nameof(Dog.Name)); @@ -372,7 +372,7 @@ public void Detects_indexed_include_properties() [ConditionalFact] public virtual void Detects_incompatible_memory_optimized_shared_table() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); @@ -388,7 +388,7 @@ public virtual void Detects_incompatible_memory_optimized_shared_table() [ConditionalFact] public virtual void Detects_incompatible_non_clustered_shared_key() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id).IsRequired(); @@ -405,7 +405,7 @@ public virtual void Detects_incompatible_non_clustered_shared_key() [ConditionalFact] public virtual void Detects_decimal_keys() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Property("Price").HasPrecision(18, 2); modelBuilder.Entity().HasKey("Price"); @@ -418,7 +418,7 @@ public virtual void Detects_decimal_keys() [ConditionalFact] public virtual void Detects_default_decimal_mapping() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("Price"); VerifyWarning( @@ -429,7 +429,7 @@ public virtual void Detects_default_decimal_mapping() [ConditionalFact] public virtual void Detects_default_nullable_decimal_mapping() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("Price"); VerifyWarning( @@ -440,7 +440,7 @@ public virtual void Detects_default_nullable_decimal_mapping() [ConditionalFact] public virtual void Does_not_warn_if_decimal_column_has_precision_and_scale() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Property("Price").HasPrecision(18, 2); @@ -452,7 +452,7 @@ public virtual void Does_not_warn_if_decimal_column_has_precision_and_scale() [ConditionalFact] public virtual void Does_not_warn_if_default_decimal_mapping_has_non_decimal_to_decimal_value_converter() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Property("Price") .HasConversion(new TestDecimalToLongConverter()); @@ -465,7 +465,7 @@ public virtual void Does_not_warn_if_default_decimal_mapping_has_non_decimal_to_ [ConditionalFact] public virtual void Warn_if_default_decimal_mapping_has_decimal_to_decimal_value_converter() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity() .Property("Price") .HasConversion(new TestDecimalToDecimalConverter()); @@ -478,7 +478,7 @@ public virtual void Warn_if_default_decimal_mapping_has_decimal_to_decimal_value [ConditionalFact] public void Detects_byte_identity_column() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(d => d.Id).ValueGeneratedNever(); modelBuilder.Entity().Property("Bite").UseIdentityColumn(); @@ -490,7 +490,7 @@ public void Detects_byte_identity_column() [ConditionalFact] public void Detects_nullable_byte_identity_column() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(d => d.Id).ValueGeneratedNever(); modelBuilder.Entity().Property("Bite").UseIdentityColumn(); @@ -502,7 +502,7 @@ public void Detects_nullable_byte_identity_column() [ConditionalFact] public void Detects_multiple_identity_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(d => d.Id).ValueGeneratedNever(); modelBuilder.Entity().Property(c => c.Type).UseIdentityColumn(); @@ -514,7 +514,7 @@ public void Detects_multiple_identity_properties() [ConditionalFact] public void Passes_for_non_key_identity() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(d => d.Id).ValueGeneratedNever(); modelBuilder.Entity().Property(c => c.Type).UseIdentityColumn(); @@ -524,7 +524,7 @@ public void Passes_for_non_key_identity() [ConditionalFact] public void Passes_for_non_key_identity_on_model() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.UseIdentityColumns(); @@ -537,7 +537,7 @@ public void Passes_for_non_key_identity_on_model() [ConditionalFact] public void Detects_non_key_SequenceHiLo() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(c => c.Type).UseHiLo(); VerifyError(SqlServerStrings.NonKeyValueGeneration(nameof(Dog.Type), nameof(Dog)), modelBuilder); @@ -546,7 +546,7 @@ public void Detects_non_key_SequenceHiLo() [ConditionalFact] public void Passes_for_non_key_SequenceHiLo_on_model() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.UseHiLo(); @@ -561,7 +561,7 @@ public void Passes_for_non_key_SequenceHiLo_on_model() [InlineData("DefaultValueSql", "ComputedColumnSql")] public void Metadata_throws_when_setting_conflicting_serverGenerated_values(string firstConfiguration, string secondConfiguration) { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var propertyBuilder = modelBuilder.Entity().Property("NullableInt"); @@ -582,7 +582,7 @@ public void SqlServerValueGenerationStrategy_warns_when_setting_conflicting_valu SqlServerValueGenerationStrategy sqlServerValueGenerationStrategy, string conflictingValueGenerationStrategy) { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var propertyBuilder = modelBuilder.Entity().Property("Id"); @@ -601,7 +601,7 @@ public void SqlServerValueGenerationStrategy_warns_when_setting_conflicting_valu public void SqlServerValueGenerationStrategy_warns_when_setting_conflicting_DefaultValue( SqlServerValueGenerationStrategy sqlServerValueGenerationStrategy) { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var propertyBuilder = modelBuilder.Entity().Property("Id"); @@ -643,7 +643,7 @@ protected virtual void ConfigureProperty(IMutableProperty property, string confi [ConditionalFact] public void Temporal_can_only_be_specified_on_root_entities() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal()); @@ -653,7 +653,7 @@ public void Temporal_can_only_be_specified_on_root_entities() [ConditionalFact] public void Temporal_enitty_must_have_period_start() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal()); modelBuilder.Entity().Metadata.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodStartPropertyName); @@ -663,7 +663,7 @@ public void Temporal_enitty_must_have_period_start() [ConditionalFact] public void Temporal_enitty_must_have_period_end() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal()); modelBuilder.Entity().Metadata.RemoveAnnotation(SqlServerAnnotationNames.TemporalPeriodEndPropertyName); @@ -673,7 +673,7 @@ public void Temporal_enitty_must_have_period_end() [ConditionalFact] public void Temporal_enitty_without_expected_period_start_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start"))); modelBuilder.Entity().Metadata.RemoveProperty("Start"); @@ -683,7 +683,7 @@ public void Temporal_enitty_without_expected_period_start_property() [ConditionalFact] public void Temporal_period_property_must_be_in_shadow_state() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("DateOfBirth"))); VerifyError(SqlServerStrings.TemporalPeriodPropertyMustBeInShadowState(nameof(Human), "DateOfBirth"), modelBuilder); @@ -692,7 +692,7 @@ public void Temporal_period_property_must_be_in_shadow_state() [ConditionalFact] public void Temporal_period_property_must_be_non_nullable_datetime() { - var modelBuilder1 = CreateConventionalModelBuilder(); + var modelBuilder1 = CreateConventionModelBuilder(); modelBuilder1.Entity().Property(typeof(DateTime?), "Start"); modelBuilder1.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start"))); @@ -700,7 +700,7 @@ public void Temporal_period_property_must_be_non_nullable_datetime() VerifyError( SqlServerStrings.TemporalPeriodPropertyMustBeNonNullableDateTime(nameof(Dog), "Start", nameof(DateTime)), modelBuilder1); - var modelBuilder2 = CreateConventionalModelBuilder(); + var modelBuilder2 = CreateConventionModelBuilder(); modelBuilder2.Entity().Property(typeof(int), "Start"); modelBuilder2.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start"))); @@ -712,7 +712,7 @@ public void Temporal_period_property_must_be_non_nullable_datetime() [ConditionalFact] public void Temporal_period_property_must_be_mapped_to_datetime2() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(typeof(DateTime), "Start").HasColumnType("datetime"); modelBuilder.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start"))); @@ -722,7 +722,7 @@ public void Temporal_period_property_must_be_mapped_to_datetime2() [ConditionalFact] public void Temporal_all_properties_mapped_to_period_column_must_have_value_generated_OnAddOrUpdate() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(typeof(DateTime), "Start2").HasColumnName("StartColumn").ValueGeneratedOnAddOrUpdate(); modelBuilder.Entity().Property(typeof(DateTime), "Start3").HasColumnName("StartColumn"); modelBuilder.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start").HasColumnName("StartColumn"))); @@ -735,7 +735,7 @@ public void Temporal_all_properties_mapped_to_period_column_must_have_value_gene [ConditionalFact] public void Temporal_all_properties_mapped_to_period_column_cant_have_default_values() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(typeof(DateTime), "Start2").HasColumnName("StartColumn").ValueGeneratedOnAddOrUpdate(); modelBuilder.Entity().Property(typeof(DateTime), "Start3").HasColumnName("StartColumn").ValueGeneratedOnAddOrUpdate() .HasDefaultValue(DateTime.MinValue); @@ -749,7 +749,7 @@ public void Temporal_all_properties_mapped_to_period_column_cant_have_default_va [ConditionalFact] public void Temporal_period_property_cant_have_default_value() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(typeof(DateTime), "Start").HasDefaultValue(new DateTime(2000, 1, 1)); modelBuilder.Entity().ToTable(tb => tb.IsTemporal(ttb => ttb.HasPeriodStart("Start"))); @@ -759,7 +759,7 @@ public void Temporal_period_property_cant_have_default_value() [ConditionalFact] public void Temporal_doesnt_work_on_TPH() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable(tb => tb.IsTemporal()); modelBuilder.Entity().ToTable("Dogs"); modelBuilder.Entity().ToTable("Cats"); @@ -770,7 +770,7 @@ public void Temporal_doesnt_work_on_TPH() [ConditionalFact] public void Temporal_doesnt_work_on_table_splitting_with_inconsistent_period_mappings() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Splitting", tb => tb.IsTemporal()); modelBuilder.Entity().ToTable("Splitting", tb => tb.IsTemporal()); modelBuilder.Entity().HasOne(x => x.Details).WithOne().HasForeignKey(x => x.Id); @@ -781,7 +781,7 @@ public void Temporal_doesnt_work_on_table_splitting_with_inconsistent_period_map [ConditionalFact] public void Temporal_doesnt_work_on_table_splitting_when_some_types_are_temporal_and_some_are_not() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Splitting"); modelBuilder.Entity().ToTable("Splitting", tb => tb.IsTemporal()); modelBuilder.Entity().HasOne(x => x.Details).WithOne().HasForeignKey(x => x.Id); diff --git a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs index 6b12f4f598c..64e6bf0912b 100644 --- a/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs +++ b/test/EFCore.SqlServer.Tests/Storage/SqlServerTypeMappingSourceTest.cs @@ -1426,7 +1426,7 @@ private enum ByteEnum : byte } protected override ModelBuilder CreateModelBuilder(Action configure = null) - => SqlServerTestHelpers.Instance.CreateConventionBuilder(configure: configure); + => SqlServerTestHelpers.Instance.CreateConventionBuilder(configureModel: configure); private class TestParameter : DbParameter { diff --git a/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs b/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs index d94a6e0abff..1b0386ab51b 100644 --- a/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs +++ b/test/EFCore.Sqlite.FunctionalTests/TestUtilities/SqliteTestHelpers.cs @@ -17,7 +17,7 @@ protected SqliteTestHelpers() public override IServiceCollection AddProviderServices(IServiceCollection services) => services.AddEntityFrameworkSqlite(); - public override void UseProviderOptions(DbContextOptionsBuilder optionsBuilder) + public override DbContextOptionsBuilder UseProviderOptions(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlite(new SqliteConnection("Data Source=:memory:")); public override LoggingDefinitions LoggingDefinitions { get; } = new SqliteLoggingDefinitions(); diff --git a/test/EFCore.Sqlite.Tests/Infrastructure/SqliteModelValidatorTest.cs b/test/EFCore.Sqlite.Tests/Infrastructure/SqliteModelValidatorTest.cs index cbcd5ffff04..0ca1505fe31 100644 --- a/test/EFCore.Sqlite.Tests/Infrastructure/SqliteModelValidatorTest.cs +++ b/test/EFCore.Sqlite.Tests/Infrastructure/SqliteModelValidatorTest.cs @@ -11,7 +11,7 @@ public class SqliteModelValidatorTest : RelationalModelValidatorTest { public override void Detects_duplicate_column_names() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(b => b.Id).HasColumnName("Name"); modelBuilder.Entity().Property(d => d.Name).IsRequired().HasColumnName("Name"); @@ -25,7 +25,7 @@ public override void Detects_duplicate_column_names() public override void Detects_duplicate_columns_in_derived_types_with_different_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Type).IsRequired().HasColumnName("Type"); @@ -39,7 +39,7 @@ public override void Detects_duplicate_columns_in_derived_types_with_different_t [ConditionalFact] public virtual void Detects_duplicate_column_names_within_hierarchy_with_different_srid() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().Property(c => c.Breed).HasColumnName("Breed").HasSrid(30); @@ -53,7 +53,7 @@ public virtual void Detects_duplicate_column_names_within_hierarchy_with_differe [ConditionalFact] public void Detects_schemas() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().ToTable("Animals", "pet").Ignore(a => a.FavoritePerson); VerifyWarning( @@ -64,7 +64,7 @@ public void Detects_schemas() [ConditionalFact] public void Detects_sequences() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.HasSequence("Fibonacci"); VerifyWarning( diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.PropertyMapping.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.PropertyMapping.cs index 4b2831dd453..f11ab5785c2 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.PropertyMapping.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.PropertyMapping.cs @@ -119,7 +119,7 @@ public virtual void Throws_when_navigation_is_not_added_or_ignored() [ConditionalFact] public virtual void Throws_when_navigation_to_owned_type_is_not_added_or_ignored() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(a => a.Id); modelBuilder.Entity().OwnsOne(a => a.FavoritePerson); modelBuilder.Entity(); diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs index 60eefb6bbfa..a284fb03765 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs @@ -14,7 +14,7 @@ public partial class ModelValidatorTest : ModelValidatorTestBase [ConditionalFact] public virtual void Detects_key_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -36,7 +36,7 @@ protected class WithNonComparableKey [ConditionalFact] public virtual void Detects_unique_index_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -59,7 +59,7 @@ protected class WithNonComparableUniqueIndex [ConditionalFact] public virtual void Ignores_normal_property_which_cannot_be_compared() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => @@ -85,7 +85,7 @@ protected struct NotComparable [ConditionalFact] public virtual void Detects_custom_converter_for_collection_type_without_comparer() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); IMutableProperty convertedProperty = null; modelBuilder.Entity( @@ -108,7 +108,7 @@ public virtual void Detects_custom_converter_for_collection_type_without_compare [ConditionalFact] public virtual void Ignores_custom_converter_for_collection_type_with_comparer() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); IMutableProperty convertedProperty = null; modelBuilder.Entity( @@ -170,7 +170,7 @@ protected class WithStringAndBinaryKey [ConditionalFact] public virtual void Detects_filter_on_derived_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var entityTypeA = modelBuilder.Entity().Metadata; var entityTypeD = modelBuilder.Entity().Metadata; @@ -184,7 +184,7 @@ public virtual void Detects_filter_on_derived_type() [ConditionalFact] public virtual void Detects_filter_on_owned_type() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var queryFilter = (Expression>)(_ => true); modelBuilder.Entity() .OwnsOne( @@ -430,7 +430,7 @@ public virtual void Detects_key_property_with_value_generated_on_add_or_update() [ConditionalFact] public virtual void Detects_relationship_cycle() { - var modelBuilder = base.CreateConventionalModelBuilder(); + var modelBuilder = base.CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity(); @@ -447,7 +447,7 @@ public virtual void Detects_relationship_cycle() [ConditionalFact] public virtual void Passes_on_multiple_relationship_paths() { - var modelBuilder = base.CreateConventionalModelBuilder(); + var modelBuilder = base.CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity(); @@ -462,7 +462,7 @@ public virtual void Passes_on_multiple_relationship_paths() [ConditionalFact] public virtual void Passes_on_redundant_foreign_key() { - var modelBuilder = base.CreateConventionalModelBuilder(); + var modelBuilder = base.CreateConventionModelBuilder(); modelBuilder.Entity().HasOne().WithOne().IsRequired().HasForeignKey(a => a.Id).HasPrincipalKey(b => b.Id); @@ -630,7 +630,7 @@ public virtual void Passes_on_diamond_path_to_root_principal_property() [ConditionalFact] public virtual void Passes_on_correct_inheritance() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity(); @@ -691,7 +691,7 @@ public virtual void Detects_generic_leaf_type() [ConditionalFact] public virtual void Passes_on_valid_many_to_many_navigations() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var model = modelBuilder.Model; var orderProductEntity = model.AddEntityType(typeof(OrderProduct)); @@ -721,7 +721,7 @@ public virtual void Passes_on_valid_many_to_many_navigations() [ConditionalFact] public virtual void Detects_missing_foreign_key_for_skip_navigations() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var model = modelBuilder.Model; var orderProductEntity = model.AddEntityType(typeof(OrderProduct)); @@ -745,7 +745,7 @@ public virtual void Detects_missing_foreign_key_for_skip_navigations() [ConditionalFact] public virtual void Detects_missing_inverse_skip_navigations() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var model = modelBuilder.Model; var orderProductEntity = model.AddEntityType(typeof(OrderProduct)); @@ -770,7 +770,7 @@ public virtual void Detects_missing_inverse_skip_navigations() [ConditionalFact] public virtual void Detects_nonCollection_skip_navigations() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); var model = modelBuilder.Model; var customerEntity = model.AddEntityType(typeof(Customer)); @@ -966,7 +966,7 @@ public virtual void Detects_owned_entity_type_without_ownership() [ConditionalFact] public virtual void Detects_ForeignKey_on_inherited_generated_key_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("SomeId").ValueGeneratedOnAdd(); modelBuilder.Entity().HasAlternateKey("SomeId"); modelBuilder.Entity>().HasOne().WithOne().HasForeignKey>("SomeId"); @@ -983,7 +983,7 @@ public virtual void Detects_ForeignKey_on_inherited_generated_key_property() [ConditionalFact] public virtual void Passes_for_ForeignKey_on_inherited_generated_key_property_abstract_base() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property(e => e.Id).ValueGeneratedOnAdd(); modelBuilder.Entity>().HasOne().WithOne().HasForeignKey>(e => e.Id); @@ -993,7 +993,7 @@ public virtual void Passes_for_ForeignKey_on_inherited_generated_key_property_ab [ConditionalFact] public virtual void Passes_for_ForeignKey_on_inherited_generated_composite_key_property() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().Property("SomeId").ValueGeneratedOnAdd(); modelBuilder.Entity().Property("SomeOtherId").ValueGeneratedOnAdd(); modelBuilder.Entity().HasAlternateKey("SomeId", "SomeOtherId"); @@ -1097,7 +1097,7 @@ public virtual void Passes_for_non_notifying_entities_with_snapshot_tracking() [ConditionalFact] public virtual void Passes_for_valid_seeds() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasData( new A { Id = 1 }); modelBuilder.Entity().HasData( @@ -1109,7 +1109,7 @@ public virtual void Passes_for_valid_seeds() [ConditionalFact] public virtual void Passes_for_ignored_invalid_seeded_properties() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( eb => { @@ -1140,7 +1140,7 @@ public virtual void Passes_for_ignored_invalid_seeded_properties() [ConditionalFact] public virtual void Detects_derived_seeds() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); Assert.Equal( CoreStrings.SeedDatumDerivedType(nameof(A), nameof(D)), @@ -1152,7 +1152,7 @@ public virtual void Detects_derived_seeds() [ConditionalFact] public virtual void Detects_derived_seeds_for_owned_types() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); Assert.Equal( CoreStrings.SeedDatumDerivedType(nameof(A), nameof(D)), @@ -1167,7 +1167,7 @@ public virtual void Detects_derived_seeds_for_owned_types() [ConditionalFact] public virtual void Detects_missing_required_values_in_seeds() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( e => { @@ -1184,7 +1184,7 @@ public virtual void Detects_missing_required_values_in_seeds() [ConditionalFact] public virtual void Passes_on_missing_required_store_generated_values_in_seeds() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( e => { @@ -1200,7 +1200,7 @@ public virtual void Passes_on_missing_required_store_generated_values_in_seeds() public virtual void Detects_missing_key_values_in_seeds() { var entity = new NonSignedIntegerKeyEntity(); - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(e => e.HasData(entity)); Assert.Equal( @@ -1215,7 +1215,7 @@ public virtual void Detects_missing_key_values_in_seeds() [ConditionalFact] public virtual void Detects_missing_signed_integer_key_values_in_seeds() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(e => e.HasData(new A())); VerifyError( @@ -1228,7 +1228,7 @@ public virtual void Detects_missing_signed_integer_key_values_in_seeds() [InlineData(false)] public virtual void Detects_duplicate_seeds(bool sensitiveDataLoggingEnabled) { - var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); + var modelBuilder = CreateConventionModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); modelBuilder.Entity().HasData( new A { Id = 1 }); modelBuilder.Entity().HasData( @@ -1247,7 +1247,7 @@ public virtual void Detects_duplicate_seeds(bool sensitiveDataLoggingEnabled) [InlineData(false)] public virtual void Detects_incompatible_values(bool sensitiveDataLoggingEnabled) { - var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); + var modelBuilder = CreateConventionModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); modelBuilder.Entity( e => { @@ -1268,7 +1268,7 @@ public virtual void Detects_incompatible_values(bool sensitiveDataLoggingEnabled [InlineData(false)] public virtual void Detects_reference_navigations_in_seeds(bool sensitiveDataLoggingEnabled) { - var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); + var modelBuilder = CreateConventionModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); modelBuilder.Entity( e => { @@ -1298,7 +1298,7 @@ public virtual void Detects_reference_navigations_in_seeds(bool sensitiveDataLog [InlineData(false)] public virtual void Detects_reference_navigations_in_seeds2(bool sensitiveDataLoggingEnabled) { - var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); + var modelBuilder = CreateConventionModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); modelBuilder.Entity( e => { @@ -1330,7 +1330,7 @@ public virtual void Detects_reference_navigations_in_seeds2(bool sensitiveDataLo [InlineData(false)] public virtual void Detects_collection_navigations_in_seeds(bool sensitiveDataLoggingEnabled) { - var modelBuilder = CreateConventionalModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); + var modelBuilder = CreateConventionModelBuilder(sensitiveDataLoggingEnabled: sensitiveDataLoggingEnabled); modelBuilder.Entity( e => { @@ -1417,7 +1417,7 @@ public virtual void Detects_missing_discriminator_value_on_leaf() [ConditionalFact] public virtual void Detects_missing_non_string_discriminator_values() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity(); modelBuilder.Entity().HasDiscriminator("ClassType") .HasValue(0) @@ -1429,7 +1429,7 @@ public virtual void Detects_missing_non_string_discriminator_values() [ConditionalFact] public virtual void Detects_duplicate_discriminator_values() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasDiscriminator("ClassType") .HasValue(1) .HasValue(1) @@ -1441,7 +1441,7 @@ public virtual void Detects_duplicate_discriminator_values() [ConditionalFact] public virtual void Required_navigation_with_query_filter_on_one_side_issues_a_warning() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasMany(x => x.Orders).WithOne(x => x.Customer).IsRequired(); modelBuilder.Entity().HasQueryFilter(x => x.Id > 5); modelBuilder.Ignore(); @@ -1455,7 +1455,7 @@ public virtual void Required_navigation_with_query_filter_on_one_side_issues_a_w [ConditionalFact] public virtual void Optional_navigation_with_query_filter_on_one_side_doesnt_issue_a_warning() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasMany(x => x.Orders).WithOne(x => x.Customer).IsRequired(false); modelBuilder.Entity().HasQueryFilter(x => x.Id > 5); @@ -1468,7 +1468,7 @@ public virtual void Optional_navigation_with_query_filter_on_one_side_doesnt_iss [ConditionalFact] public virtual void Required_navigation_with_query_filter_on_both_sides_doesnt_issue_a_warning() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity().HasMany(x => x.Orders).WithOne(x => x.Customer).IsRequired(); modelBuilder.Entity().HasQueryFilter(x => x.Id > 5); modelBuilder.Entity().HasQueryFilter(x => x.Customer.Id > 5); @@ -1482,7 +1482,7 @@ public virtual void Required_navigation_with_query_filter_on_both_sides_doesnt_i [ConditionalFact] public virtual void Shared_type_inheritance_throws() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.SharedTypeEntity("Shared1"); modelBuilder.SharedTypeEntity("Shared2").HasBaseType("Shared1"); @@ -1492,7 +1492,7 @@ public virtual void Shared_type_inheritance_throws() [ConditionalFact] public virtual void Seeding_keyless_entity_throws() { - var modelBuilder = CreateConventionalModelBuilder(); + var modelBuilder = CreateConventionModelBuilder(); modelBuilder.Entity( e => { diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs index bd970e5f7ad..75d7a2a33f6 100644 --- a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs +++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs @@ -391,7 +391,7 @@ protected virtual IModel Validate(TestHelpers.TestModelBuilder modelBuilder, boo new NullDbContextLogger()); } - protected virtual TestHelpers.TestModelBuilder CreateConventionalModelBuilder( + protected virtual TestHelpers.TestModelBuilder CreateConventionModelBuilder( Action configure = null, bool sensitiveDataLoggingEnabled = false) => TestHelpers.CreateConventionBuilder(