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(