diff --git a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
index 655013a1408..6c5d41210fb 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalEventId.cs
@@ -86,6 +86,7 @@ private enum Id
AllIndexPropertiesNotToMappedToAnyTable,
IndexPropertiesBothMappedAndNotMappedToTable,
IndexPropertiesMappedToNonOverlappingTables,
+ ForeignKeyPropertiesMappedToUnrelatedTables,
// Update events
BatchReadyForExecution = CoreEventId.RelationalBaseId + 700,
@@ -709,6 +710,19 @@ private static EventId MakeValidationId(Id id)
public static readonly EventId IndexPropertiesMappedToNonOverlappingTables =
MakeValidationId(Id.IndexPropertiesMappedToNonOverlappingTables);
+ ///
+ ///
+ /// A foreign key specifies properties which don't map to the related tables.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ ///
+ /// This event uses the payload when used with a .
+ ///
+ ///
+ public static readonly EventId ForeignKeyPropertiesMappedToUnrelatedTables = MakeValidationId(Id.ForeignKeyPropertiesMappedToUnrelatedTables);
+
private static readonly string _updatePrefix = DbLoggerCategory.Update.Name + ".";
private static EventId MakeUpdateId(Id id)
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
index 1648b1828f4..a9b724e6cb3 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggerExtensions.cs
@@ -4650,6 +4650,62 @@ private static string NamedIndexPropertiesMappedToNonOverlappingTables(EventDefi
p.TablesMappedToProperty2.FormatTables()));
}
+ ///
+ /// Logs the event.
+ ///
+ /// The diagnostics logger to use.
+ /// The foreign key.
+ public static void ForeignKeyPropertiesMappedToUnrelatedTables(
+ [NotNull] this IDiagnosticsLogger diagnostics,
+ [NotNull] IForeignKey foreignKey)
+ {
+ var definition = RelationalResources.LogForeignKeyPropertiesMappedToUnrelatedTables(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics,
+ l => l.Log(
+ definition.Level,
+ definition.EventId,
+ definition.MessageFormat,
+ foreignKey.Properties.Format(),
+ foreignKey.DeclaringEntityType.DisplayName(),
+ foreignKey.PrincipalEntityType.DisplayName(),
+ foreignKey.Properties.Format(),
+ foreignKey.DeclaringEntityType.GetSchemaQualifiedTableName(),
+ foreignKey.PrincipalKey.Properties.Format(),
+ foreignKey.PrincipalEntityType.GetSchemaQualifiedTableName()));
+ }
+
+ if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
+ {
+ var eventData = new ForeignKeyEventData(
+ definition,
+ ForeignKeyPropertiesMappedToUnrelatedTables,
+ foreignKey);
+
+ diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
+ }
+ }
+
+ private static string ForeignKeyPropertiesMappedToUnrelatedTables(EventDefinitionBase definition, EventData payload)
+ {
+ var d = (FallbackEventDefinition)definition;
+ var p = (ForeignKeyEventData)payload;
+ return d.GenerateMessage(
+ l => l.Log(
+ d.Level,
+ d.EventId,
+ d.MessageFormat,
+ p.ForeignKey.Properties.Format(),
+ p.ForeignKey.DeclaringEntityType.DisplayName(),
+ p.ForeignKey.PrincipalEntityType.DisplayName(),
+ p.ForeignKey.Properties.Format(),
+ p.ForeignKey.DeclaringEntityType.GetSchemaQualifiedTableName(),
+ p.ForeignKey.PrincipalKey.Properties.Format(),
+ p.ForeignKey.PrincipalEntityType.GetSchemaQualifiedTableName()));
+ }
+
///
/// Logs for the event.
///
diff --git a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
index 6a2cc467c8e..d050a25d329 100644
--- a/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
+++ b/src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs
@@ -468,6 +468,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 LogForeignKeyPropertiesMappedToUnrelatedTables;
+
///
/// 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/RelationalForeignKeyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
index ae648e4d96a..461bbac569e 100644
--- a/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalForeignKeyExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
@@ -23,13 +23,12 @@ public static class RelationalForeignKeyExtensions
/// The foreign key.
/// The foreign key constraint name.
public static string GetConstraintName([NotNull] this IForeignKey foreignKey)
- => foreignKey.GetConstraintName(
- StoreObjectIdentifier.Create(foreignKey.DeclaringEntityType, StoreObjectType.Table).Value,
- StoreObjectIdentifier.Create(
- foreignKey.PrincipalKey.IsPrimaryKey()
- ? foreignKey.PrincipalEntityType
- : foreignKey.PrincipalKey.DeclaringEntityType,
- StoreObjectType.Table).Value);
+ {
+ var annotation = foreignKey.FindAnnotation(RelationalAnnotationNames.Name);
+ return annotation != null
+ ? (string)annotation.Value
+ : foreignKey.GetDefaultName();
+ }
///
/// Returns the foreign key constraint name.
diff --git a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
index 68806ec67e7..f04d4ec91cc 100644
--- a/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
+++ b/src/EFCore.Relational/Extensions/RelationalPropertyExtensions.cs
@@ -50,12 +50,30 @@ public static string GetColumnName([NotNull] this IProperty property, in StoreOb
return overrides.ColumnName;
}
- if (!property.IsPrimaryKey()
- && storeObject.StoreObjectType != StoreObjectType.Function
- && storeObject.StoreObjectType != StoreObjectType.SqlQuery
- && StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObject.StoreObjectType) != storeObject)
+ if (storeObject.StoreObjectType != StoreObjectType.Function
+ && storeObject.StoreObjectType != StoreObjectType.SqlQuery)
{
- return null;
+ if (property.IsPrimaryKey())
+ {
+ var tableFound = false;
+ foreach (var containingType in property.DeclaringEntityType.GetDerivedTypesInclusive())
+ {
+ if (StoreObjectIdentifier.Create(containingType, storeObject.StoreObjectType) == storeObject)
+ {
+ tableFound = true;
+ break;
+ }
+ }
+
+ if (!tableFound)
+ {
+ return null;
+ }
+ }
+ else if (StoreObjectIdentifier.Create(property.DeclaringEntityType, storeObject.StoreObjectType) != storeObject)
+ {
+ return null;
+ }
}
var columnAnnotation = property.FindAnnotation(RelationalAnnotationNames.ColumnName);
diff --git a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
index 7f2039cf8e0..b8c6c3b4dc5 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalModelValidator.cs
@@ -924,6 +924,17 @@ protected virtual void ValidateSharedForeignKeysCompatibility(
var foreignKeyName = foreignKey.GetConstraintName(storeObject, principalTable.Value);
if (foreignKeyName == null)
{
+ 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;
}
@@ -1254,8 +1265,7 @@ protected virtual void ValidateIndexProperties(
if (firstPropertyTables != null)
{
- // Property is not mapped but we already found
- // a property that is mapped.
+ // Property is not mapped but we already found a property that is mapped.
break;
}
@@ -1264,21 +1274,18 @@ protected virtual void ValidateIndexProperties(
if (firstPropertyTables == null)
{
- // store off which tables the first member maps to
firstPropertyTables =
new Tuple>(property.Name, tablesMappedToProperty);
}
else
{
- // store off which tables the last member we encountered maps to
lastPropertyTables =
new Tuple>(property.Name, tablesMappedToProperty);
}
if (propertyNotMappedToAnyTable != null)
{
- // Property is mapped but we already found
- // a property that is not mapped.
+ // Property is mapped but we already found a property that is not mapped.
overlappingTables = null;
break;
}
diff --git a/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs b/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
index 4b62a75f515..d9337c9080f 100644
--- a/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
+++ b/src/EFCore.Relational/Infrastructure/RelationalOptionsExtension.cs
@@ -388,6 +388,18 @@ public virtual void Validate(IDbContextOptions options)
{
}
+ ///
+ /// Adds default for relational events.
+ ///
+ /// The core options extension.
+ /// The new core options extension.
+ public static CoreOptionsExtension WithDefaultWarningConfiguration([NotNull] CoreOptionsExtension coreOptionsExtension)
+ => coreOptionsExtension.WithWarningsConfiguration(coreOptionsExtension.WarningsConfiguration
+ .TryWithExplicit(RelationalEventId.AmbientTransactionWarning, WarningBehavior.Throw)
+ .TryWithExplicit(RelationalEventId.IndexPropertiesBothMappedAndNotMappedToTable, WarningBehavior.Throw)
+ .TryWithExplicit(RelationalEventId.IndexPropertiesMappedToNonOverlappingTables, WarningBehavior.Throw)
+ .TryWithExplicit(RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables, WarningBehavior.Throw));
+
///
/// Information/metadata for a .
///
@@ -396,7 +408,7 @@ protected abstract class RelationalExtensionInfo : DbContextOptionsExtensionInfo
private string _logFragment;
///
- /// Creates a new instance containing
+ /// Creates a new instance containing
/// info/metadata for the given extension.
///
/// The extension.
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index f10f6119e58..025f8ecb9f9 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -830,9 +830,17 @@ private static void PopulateConstraints(Table table)
break;
}
+ if (entityTypeMapping.IncludesDerivedTypes
+ && foreignKey.DeclaringEntityType != entityType
+ && foreignKey.Properties.SequenceEqual(entityType.FindPrimaryKey().Properties))
+ {
+ // The identifying FK constraint is needed to be created only on the table that corresponds
+ // to the declaring entity type
+ break;
+ }
+
constraint = new ForeignKeyConstraint(
- name, table, principalTable, columns, principalColumns,
- ToReferentialAction(foreignKey.DeleteBehavior));
+ name, table, principalTable, columns, principalColumns, ToReferentialAction(foreignKey.DeleteBehavior));
constraint.MappedForeignKeys.Add(foreignKey);
if (foreignKeyConstraints == null)
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index f1a9ffe8fd4..4587186a00a 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -1771,6 +1771,27 @@ public static EventDefinition LogExplicitTransactionEnlisted([NotNull] I
return (EventDefinition)definition;
}
+ ///
+ /// The foreign key {foreignKeyProperties} on the entity type '{entityType}' targeting '{principalEntityType}' cannot be represented in the database. Either the properties {foreignKeyProperties} aren't mapped to table '{table}' or the principal properties {principalProperties} aren't mapped to table '{principalTable}'. All foreign key properties must map to the table that the dependent type is mapped to and all principal properties must map to a single table that the principal type is mapped to.
+ ///
+ public static FallbackEventDefinition LogForeignKeyPropertiesMappedToUnrelatedTables([NotNull] IDiagnosticsLogger logger)
+ {
+ var definition = ((RelationalLoggingDefinitions)logger.Definitions).LogForeignKeyPropertiesMappedToUnrelatedTables;
+ if (definition == null)
+ {
+ definition = LazyInitializer.EnsureInitialized(
+ ref ((RelationalLoggingDefinitions)logger.Definitions).LogForeignKeyPropertiesMappedToUnrelatedTables,
+ () => new FallbackEventDefinition(
+ logger.Options,
+ RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables,
+ LogLevel.Error,
+ "RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables",
+ _resourceManager.GetString("LogForeignKeyPropertiesMappedToUnrelatedTables")));
+ }
+
+ return (FallbackEventDefinition)definition;
+ }
+
///
/// Generating down script for migration '{migration}'.
///
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index 6cbc84a97b9..1704e062e8f 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -468,6 +468,10 @@
Enlisted in an explicit transaction with isolation level '{isolationLevel}'.
Debug RelationalEventId.ExplicitTransactionEnlisted string
+
+ The foreign key {foreignKeyProperties} on the entity type '{entityType}' targeting '{principalEntityType}' cannot be represented in the database. Either the properties {foreignKeyProperties} aren't mapped to table '{table}' or the principal properties {principalProperties} aren't mapped to table '{principalTable}'. All foreign key properties must map to the table that the dependent type is mapped to and all principal properties must map to a single table that the principal type is mapped to.
+ Error RelationalEventId.ForeignKeyPropertiesMappedToUnrelatedTables string string string string string string string
+
Generating down script for migration '{migration}'.
Debug RelationalEventId.MigrationGeneratingDownScript string
diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs
index 285b5bd9f18..4e2e24b99f9 100644
--- a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -169,10 +169,8 @@ var coreOptionsExtension
= optionsBuilder.Options.FindExtension()
?? new CoreOptionsExtension();
- coreOptionsExtension = coreOptionsExtension.WithWarningsConfiguration(
- coreOptionsExtension.WarningsConfiguration.TryWithExplicit(
- RelationalEventId.AmbientTransactionWarning, WarningBehavior.Throw)).WithWarningsConfiguration(
- coreOptionsExtension.WarningsConfiguration.TryWithExplicit(
+ coreOptionsExtension = RelationalOptionsExtension.WithDefaultWarningConfiguration(coreOptionsExtension)
+ .WithWarningsConfiguration(coreOptionsExtension.WarningsConfiguration.TryWithExplicit(
SqlServerEventId.ConflictingValueGenerationStrategiesWarning, WarningBehavior.Throw));
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(coreOptionsExtension);
diff --git a/src/EFCore.Sqlite.Core/Extensions/SqliteDbContextOptionsBuilderExtensions.cs b/src/EFCore.Sqlite.Core/Extensions/SqliteDbContextOptionsBuilderExtensions.cs
index 16785d124e8..c92935c026e 100644
--- a/src/EFCore.Sqlite.Core/Extensions/SqliteDbContextOptionsBuilderExtensions.cs
+++ b/src/EFCore.Sqlite.Core/Extensions/SqliteDbContextOptionsBuilderExtensions.cs
@@ -167,9 +167,7 @@ var coreOptionsExtension
= optionsBuilder.Options.FindExtension()
?? new CoreOptionsExtension();
- coreOptionsExtension = coreOptionsExtension.WithWarningsConfiguration(
- coreOptionsExtension.WarningsConfiguration.TryWithExplicit(
- RelationalEventId.AmbientTransactionWarning, WarningBehavior.Throw));
+ coreOptionsExtension = RelationalOptionsExtension.WithDefaultWarningConfiguration(coreOptionsExtension);
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(coreOptionsExtension);
}
diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
index b4cc522ddd0..bc0d2879f53 100644
--- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
+++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs
@@ -1353,6 +1353,27 @@ public virtual void Detects_linking_relationship_on_derived_type_in_TPT_views()
modelBuilder.Model);
}
+ [ConditionalFact]
+ public virtual void Detects_unmapped_foreign_keys_in_TPT()
+ {
+ var modelBuilder = CreateConventionalModelBuilder();
+ modelBuilder.Entity()
+ .Ignore(a => a.FavoritePerson)
+ .Property("FavoritePersonId");
+ modelBuilder.Entity().ToTable("Cat")
+ .HasOne().WithMany()
+ .HasForeignKey("FavoritePersonId");
+
+ var definition = RelationalResources.LogForeignKeyPropertiesMappedToUnrelatedTables(new TestLogger());
+ VerifyWarning(definition.GenerateMessage(l => l.Log(
+ definition.Level,
+ definition.EventId,
+ definition.MessageFormat,
+ "{'FavoritePersonId'}", nameof(Cat), nameof(Person), "{'FavoritePersonId'}", nameof(Cat), "{'Id'}", nameof(Person))),
+ modelBuilder.Model,
+ LogLevel.Error);
+ }
+
[ConditionalFact]
public virtual void Passes_for_valid_table_overrides()
{
diff --git a/test/EFCore.Relational.Tests/RelationalEventIdTest.cs b/test/EFCore.Relational.Tests/RelationalEventIdTest.cs
index c86d7730355..ab80a7fc823 100644
--- a/test/EFCore.Relational.Tests/RelationalEventIdTest.cs
+++ b/test/EFCore.Relational.Tests/RelationalEventIdTest.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@@ -40,10 +40,11 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled()
var constantExpression = Expression.Constant("A");
var model = new Model();
var entityType = new EntityType(typeof(object), model, ConfigurationSource.Convention);
- var property = new Property(
- "A", typeof(int), null, null, entityType, ConfigurationSource.Convention, ConfigurationSource.Convention);
+ var property = entityType.AddProperty("A", typeof(int), ConfigurationSource.Convention, ConfigurationSource.Convention);
+ var key = entityType.AddKey(property, ConfigurationSource.Convention);
+ var foreignKey = new ForeignKey(new List { property }, key, entityType, entityType, ConfigurationSource.Convention);
+ var index = new Metadata.Internal.Index(new List { property }, "IndexName", entityType, ConfigurationSource.Convention);
var contextServices = RelationalTestHelpers.Instance.CreateContextServices(model.FinalizeModel());
- var index = new Index(new List { property }, "IndexName", entityType, ConfigurationSource.Convention);
var fakeFactories = new Dictionary>
{
@@ -71,6 +72,8 @@ public void Every_eventId_has_a_logger_method_and_logs_when_level_enabled()
{ typeof(Expression), () => constantExpression },
{ typeof(IEntityType), () => entityType },
{ typeof(IProperty), () => property },
+ { typeof(IKey), () => key },
+ { typeof(IForeignKey), () => foreignKey },
{ typeof(IIndex), () => index },
{ typeof(TypeInfo), () => typeof(object).GetTypeInfo() },
{ typeof(Type), () => typeof(object) },
diff --git a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
index 8f7f95f598f..658981e6208 100644
--- a/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
+++ b/test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
@@ -2230,6 +2230,48 @@ protected class ConflictingFKAttributes
public A As { get; set; }
}
+ [ConditionalFact]
+ public virtual void Attribute_set_shadow_FK_name_is_preserved_with_HasPrincipalKey()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity(
+ m =>
+ {
+ m.Property("_email");
+
+ m.HasMany("_profiles")
+ .WithOne("User")
+ .HasPrincipalKey("_email");
+ });
+
+ modelBuilder.Entity().Property("Email");
+
+ var model = modelBuilder.FinalizeModel();
+
+ var fk = model.FindEntityType(typeof(Profile13694)).GetForeignKeys().Single();
+ Assert.Equal("_profiles", fk.PrincipalToDependent.Name);
+ Assert.Equal("User", fk.DependentToPrincipal.Name);
+ Assert.Equal("Email", fk.Properties[0].Name);
+ Assert.Equal(typeof(string), fk.Properties[0].ClrType);
+ Assert.Equal("_email", fk.PrincipalKey.Properties[0].Name);
+ }
+
+ protected class User13694
+ {
+ public Guid Id { get; set; }
+ private readonly string _email = string.Empty;
+ private readonly List _profiles = new List();
+ }
+
+ protected class Profile13694
+ {
+ public Guid Id { get; set; }
+
+ [ForeignKey("Email")]
+ public User13694 User { get; set; }
+ }
+
[ConditionalFact]
public virtual void RequiredAttribute_for_navigation_throws_while_inserting_null_value()
{
diff --git a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs
index c4e072023b7..da1b52beb30 100644
--- a/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs
+++ b/test/EFCore.SqlServer.Tests/Infrastructure/SqlServerModelValidatorTest.cs
@@ -11,6 +11,7 @@
using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.Extensions.Logging;
using Xunit;
// ReSharper disable InconsistentNaming
diff --git a/test/EFCore.SqlServer.Tests/ModelBuilding/ModelBuilderSqlServerTest.Other.cs b/test/EFCore.SqlServer.Tests/ModelBuilding/ModelBuilderSqlServerTest.Other.cs
deleted file mode 100644
index 9606a5f70cc..00000000000
--- a/test/EFCore.SqlServer.Tests/ModelBuilding/ModelBuilderSqlServerTest.Other.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.ModelBuilding
-{
- public class ModelBuilderSqlServerTest : ModelBuilderOtherTest
- {
- protected override DbContextOptions Configure()
- => new DbContextOptionsBuilder()
- .UseInternalServiceProvider(
- new ServiceCollection()
- .AddEntityFrameworkSqlServer()
- .AddSingleton()
- .BuildServiceProvider())
- .UseSqlServer("Database = None")
- .Options;
-
- protected override void RunThroughDifferPipeline(DbContext context)
- => context.GetService().GetDifferences(null, context.Model.GetRelationalModel());
- }
-}
diff --git a/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs b/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
index 574c3478157..ca414e118ee 100644
--- a/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
+++ b/test/EFCore.SqlServer.Tests/ModelBuilding/SqlServerModelBuilderGenericTest.cs
@@ -149,6 +149,63 @@ public void Index_convention_sets_filter_for_unique_index_when_base_type_changed
Assert.Null(index.GetFilter());
}
+ [ConditionalFact]
+ public virtual void TPT_identifying_FK_are_created_only_on_declaring_type()
+ {
+ var modelBuilder = CreateModelBuilder();
+ modelBuilder.Entity()
+ .Ignore(b => b.Bun)
+ .Ignore(b => b.Pickles);
+ modelBuilder.Entity(b =>
+ {
+ b.ToTable("Ingredients");
+ b.Ignore(i => i.BigMak);
+ });
+ modelBuilder.Entity(b =>
+ {
+ b.ToTable("Buns");
+ b.HasOne(i => i.BigMak).WithOne().HasForeignKey(i => i.Id);
+ });
+ modelBuilder.Entity(b =>
+ {
+ b.ToTable("SesameBuns");
+ });
+
+ var model = modelBuilder.FinalizeModel();
+
+ var principalType = model.FindEntityType(typeof(BigMak));
+ Assert.Empty(principalType.GetForeignKeys());
+ Assert.Empty(principalType.GetIndexes());
+
+ var ingredientType = model.FindEntityType(typeof(Ingredient));
+
+ var bunType = model.FindEntityType(typeof(Bun));
+ Assert.Empty(bunType.GetIndexes());
+ var bunFk = bunType.GetDeclaredForeignKeys().Single(fk => !fk.IsBaseLinking());
+ Assert.Equal("FK_Buns_BigMak_Id", bunFk.GetConstraintName());
+ Assert.Equal("FK_Buns_BigMak_Id", bunFk.GetConstraintName(
+ StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value,
+ StoreObjectIdentifier.Create(principalType, StoreObjectType.Table).Value));
+ Assert.Single(bunFk.GetMappedConstraints());
+
+ var bunLinkingFk = bunType.GetDeclaredForeignKeys().Single(fk => fk.IsBaseLinking());
+ Assert.Equal("FK_Buns_Ingredients_Id", bunLinkingFk.GetConstraintName());
+ Assert.Equal("FK_Buns_Ingredients_Id", bunLinkingFk.GetConstraintName(
+ StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value,
+ StoreObjectIdentifier.Create(ingredientType, StoreObjectType.Table).Value));
+ Assert.Single(bunLinkingFk.GetMappedConstraints());
+
+ var sesameBunType = model.FindEntityType(typeof(SesameBun));
+ Assert.Empty(sesameBunType.GetIndexes());
+ var sesameBunFk = sesameBunType.GetDeclaredForeignKeys().Single();
+ Assert.True(sesameBunFk.IsBaseLinking());
+ Assert.Equal("FK_SesameBuns_Buns_Id", sesameBunFk.GetConstraintName());
+ Assert.Equal("FK_SesameBuns_Buns_Id", sesameBunFk.GetConstraintName(
+ StoreObjectIdentifier.Create(sesameBunType, StoreObjectType.Table).Value,
+ StoreObjectIdentifier.Create(bunType, StoreObjectType.Table).Value));
+ Assert.Single(sesameBunFk.GetMappedConstraints());
+ }
+
public class Parent
{
public int Id { get; set; }
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
index 9e047662cf6..787eeefac86 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTestBase.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
diff --git a/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs b/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs
index 4fac3d28b05..92466686788 100644
--- a/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/ManyToOneTestBase.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -1164,6 +1165,47 @@ public virtual void Can_have_principal_key_by_convention_specified_with_explicit
Assert.Empty(principalType.GetIndexes());
}
+ [ConditionalFact] //Issue#13300
+ public virtual void Explicitly_set_shadow_FK_name_is_preserved_with_HasPrincipalKey()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity(
+ m =>
+ {
+ m.Property("_email");
+
+ m.HasMany("_profiles")
+ .WithOne("User")
+ .HasForeignKey("Email")
+ .HasPrincipalKey("_email");
+ });
+
+ modelBuilder.Entity().Property("Email");
+
+ var model = modelBuilder.FinalizeModel();
+
+ var fk = model.FindEntityType(typeof(Profile13300)).GetForeignKeys().Single();
+ Assert.Equal("_profiles", fk.PrincipalToDependent.Name);
+ Assert.Equal("User", fk.DependentToPrincipal.Name);
+ Assert.Equal("Email", fk.Properties[0].Name);
+ Assert.Equal(typeof(string), fk.Properties[0].ClrType);
+ Assert.Equal("_email", fk.PrincipalKey.Properties[0].Name);
+ }
+
+ protected class User13300
+ {
+ public Guid Id { get; set; }
+ private readonly string _email = string.Empty;
+ private readonly List _profiles = new List();
+ }
+
+ protected class Profile13300
+ {
+ public Guid Id { get; set; }
+ public User13300 User { get; set; }
+ }
+
[ConditionalFact]
public virtual void Creates_both_navigations_and_finds_existing_composite_FK()
{
diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs
deleted file mode 100644
index 4cf1296b409..00000000000
--- a/test/EFCore.Tests/ModelBuilding/ModelBuilder.Other.cs
+++ /dev/null
@@ -1,382 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations.Schema;
-using System.Linq;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
-using Microsoft.Extensions.DependencyInjection;
-using Xunit;
-
-namespace Microsoft.EntityFrameworkCore.ModelBuilding
-{
- public class ModelBuilderOtherTest
- {
- [ConditionalFact]
- public virtual void HasOne_with_just_string_navigation_for_non_CLR_property_throws()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity().HasOne("Snoop");
- });
- Assert.Equal(
- CoreStrings.NoClrNavigation("Snoop", nameof(Dr)),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- public virtual void HasMany_with_just_string_navigation_for_non_CLR_property_throws()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity().HasMany("Snoop");
- });
- Assert.Equal(
- CoreStrings.NoClrNavigation("Snoop", nameof(Dr)),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- public virtual void HasMany_with_a_non_collection_just_string_navigation_CLR_property_throws()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity().HasMany("Dre");
- });
- Assert.Equal(
- CoreStrings.NavigationCollectionWrongClrType("Dre", nameof(Dr), nameof(Dre), "T"),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- public virtual void HasMany_with_a_collection_navigation_CLR_property_to_derived_type_throws()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity().HasMany(d => d.Jrs);
- });
- Assert.Equal(
- CoreStrings.NavigationCollectionWrongClrType(nameof(Dr.Jrs), nameof(Dr), "ICollection", nameof(Dre)),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- public virtual void OwnsOne_HasOne_with_just_string_navigation_for_non_CLR_property_throws()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity().OwnsOne(e => e.Dre).HasOne("Snoop");
- });
- Assert.Equal(
- CoreStrings.NoClrNavigation("Snoop", nameof(Dre)),
- Assert.Throws(() => context.Model).Message);
- }
-
- protected class Dr
- {
- public int Id { get; set; }
-
- public Dre Dre { get; set; }
-
- public ICollection Jrs { get; set; }
- }
-
- protected class Dre
- {
- }
-
- protected class DreJr : Dre
- {
- }
-
- [ConditionalFact] //Issue#13108
- public virtual void HasForeignKey_infers_type_for_shadow_property_when_not_specified()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity(
- e =>
- {
- e.HasKey(c => c.Key);
- e.Property("ParentKey");
- e.HasOne(c => c.Parent).WithMany(c => c.Children).HasForeignKey("ParentKey");
- });
-
- b.Entity().HasKey(c => c.Key);
- });
- var model = (IConventionModel)context.Model;
- var property = model
- .FindEntityType(typeof(ComplexCaseChild13108)).GetProperties().Single(p => p.Name == "ParentKey");
- Assert.Equal(typeof(int), property.ClrType);
- Assert.Equal(ConfigurationSource.Explicit, property.GetTypeConfigurationSource());
- }
-
- protected class ComplexCaseChild13108
- {
- public int Key { get; set; }
- public string Id { get; set; }
- private int ParentKey { get; set; }
- public ComplexCaseParent13108 Parent { get; set; }
- }
-
- protected class ComplexCaseParent13108
- {
- public int Key { get; set; }
- public string Id { get; set; }
- public ICollection Children { get; set; }
- }
-
- [ConditionalFact] //Issue#12617
- [UseCulture("de-DE")]
- public virtual void EntityType_name_is_stored_culture_invariantly()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity();
- b.Entity();
- });
- Assert.Equal(2, context.Model.GetEntityTypes().Count());
- Assert.Equal(2, context.Model.FindEntityType(typeof(Entityss)).GetNavigations().Count());
- }
-
- protected class Entityß
- {
- public int Id { get; set; }
- }
-
- protected class Entityss
- {
- public int Id { get; set; }
- public Entityß Navigationß { get; set; }
- public Entityß Navigationss { get; set; }
- }
-
- [ConditionalFact] //Issue#13300
- public virtual void Explicitly_set_shadow_FK_name_is_preserved_with_HasPrincipalKey()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity(
- m =>
- {
- m.Property("_email");
-
- m.HasMany("_profiles")
- .WithOne("User")
- .HasForeignKey("Email")
- .HasPrincipalKey("_email");
- });
-
- b.Entity().Property("Email");
- });
- var model = context.Model;
-
- var fk = model.FindEntityType(typeof(Profile13300)).GetForeignKeys().Single();
- Assert.Equal("_profiles", fk.PrincipalToDependent.Name);
- Assert.Equal("User", fk.DependentToPrincipal.Name);
- Assert.Equal("Email", fk.Properties[0].Name);
- Assert.Equal(typeof(string), fk.Properties[0].ClrType);
- Assert.Equal("_email", fk.PrincipalKey.Properties[0].Name);
- }
-
- protected class User13300
- {
- public Guid Id { get; set; }
- private readonly string _email = string.Empty;
- private readonly List _profiles = new List();
- }
-
- protected class Profile13300
- {
- public Guid Id { get; set; }
- public User13300 User { get; set; }
- }
-
- [ConditionalFact]
- public virtual void Attribute_set_shadow_FK_name_is_preserved_with_HasPrincipalKey()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b =>
- {
- b.Entity(
- m =>
- {
- m.Property("_email");
-
- m.HasMany("_profiles")
- .WithOne("User")
- .HasPrincipalKey("_email");
- });
-
- b.Entity().Property("Email");
- });
- var model = context.Model;
-
- var fk = model.FindEntityType(typeof(Profile13694)).GetForeignKeys().Single();
- Assert.Equal("_profiles", fk.PrincipalToDependent.Name);
- Assert.Equal("User", fk.DependentToPrincipal.Name);
- Assert.Equal("Email", fk.Properties[0].Name);
- Assert.Equal(typeof(string), fk.Properties[0].ClrType);
- Assert.Equal("_email", fk.PrincipalKey.Properties[0].Name);
- }
-
- protected class User13694
- {
- public Guid Id { get; set; }
- private readonly string _email = string.Empty;
- private readonly List _profiles = new List();
- }
-
- protected class Profile13694
- {
- public Guid Id { get; set; }
-
- [ForeignKey("Email")]
- public User13694 User { get; set; }
- }
-
- [ConditionalFact]
- protected virtual void Mapping_throws_for_non_ignored_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity());
- Assert.Equal(
- CoreStrings.PropertyNotAdded(
- typeof(OneDee).ShortDisplayName(), "One", typeof(int[]).ShortDisplayName()),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- protected virtual void Mapping_ignores_ignored_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity().Ignore(e => e.One));
- Assert.Null(context.Model.FindEntityType(typeof(OneDee)).FindProperty("One"));
-
- RunThroughDifferPipeline(context);
- }
-
- [ConditionalFact]
- protected virtual void Mapping_throws_for_non_ignored_two_dimensional_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity());
- Assert.Equal(
- CoreStrings.PropertyNotAdded(
- typeof(TwoDee).ShortDisplayName(), "Two", typeof(int[,]).ShortDisplayName()),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- protected virtual void Mapping_ignores_ignored_two_dimensional_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity().Ignore(e => e.Two));
- Assert.Null(context.Model.FindEntityType(typeof(TwoDee)).FindProperty("Two"));
-
- RunThroughDifferPipeline(context);
- }
-
- [ConditionalFact]
- protected virtual void Mapping_throws_for_non_ignored_three_dimensional_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity());
- Assert.Equal(
- CoreStrings.PropertyNotAdded(
- typeof(ThreeDee).ShortDisplayName(), "Three", typeof(int[,,]).ShortDisplayName()),
- Assert.Throws(() => context.Model).Message);
- }
-
- [ConditionalFact]
- protected virtual void Mapping_ignores_ignored_three_dimensional_array()
- {
- using var context = new CustomModelBuildingContext(
- Configure(),
- b => b.Entity().Ignore(e => e.Three));
- Assert.Null(context.Model.FindEntityType(typeof(ThreeDee)).FindProperty("Three"));
-
- RunThroughDifferPipeline(context);
- }
-
- protected class CustomModelBuildingContext : DbContext
- {
- private readonly Action _builder;
-
- public CustomModelBuildingContext(DbContextOptions options, Action builder)
- : base(options)
- {
- _builder = builder;
- }
-
- protected internal override void OnModelCreating(ModelBuilder modelBuilder)
- => _builder(modelBuilder);
- }
-
- protected virtual void RunThroughDifferPipeline(DbContext context)
- {
- }
-
- protected class TestModelCacheKeyFactory : IModelCacheKeyFactory
- {
- public object Create(DbContext context)
- => new object();
- }
-
- protected class OneDee
- {
- public int Id { get; set; }
-
- public int[] One { get; set; }
- }
-
- protected class TwoDee
- {
- public int Id { get; set; }
-
- public int[,] Two { get; set; }
- }
-
- protected class ThreeDee
- {
- public int Id { get; set; }
-
- public int[,,] Three { get; set; }
- }
-
- protected virtual DbContextOptions Configure()
- => new DbContextOptionsBuilder()
- .UseInternalServiceProvider(
- InMemoryFixture.BuildServiceProvider(
- new ServiceCollection()
- .AddSingleton()))
- .UseInMemoryDatabase(nameof(CustomModelBuildingContext))
- .Options;
- }
-}
diff --git a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs
index e6659c09d77..5190b341dde 100644
--- a/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs
+++ b/test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs
@@ -6,12 +6,14 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.EntityFrameworkCore.ValueGeneration;
+using Xunit;
namespace Microsoft.EntityFrameworkCore.ModelBuilding
{
@@ -31,6 +33,18 @@ protected override TestModelBuilder CreateTestModelBuilder(TestHelpers testHelpe
public class NonGenericOwnedTypes : OwnedTypesTestBase
{
+ [ConditionalFact]
+ public virtual void OwnsOne_HasOne_with_just_string_navigation_for_non_CLR_property_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ Assert.Equal(
+ CoreStrings.NoClrNavigation("Snoop", nameof(Dre)),
+ Assert.Throws(() =>
+ ((NonGenericTestOwnedNavigationBuilder)modelBuilder.Entity().OwnsOne(e => e.Dre)).GetInfrastructure()
+ .HasOne("Snoop")).Message);
+ }
+
protected override TestModelBuilder CreateTestModelBuilder(TestHelpers testHelpers)
=> new NonGenericTestModelBuilder(testHelpers);
}
@@ -43,6 +57,80 @@ protected override TestModelBuilder CreateTestModelBuilder(TestHelpers testHelpe
public class NonGenericOneToMany : OneToManyTestBase
{
+
+ [ConditionalFact]
+ public virtual void HasOne_with_just_string_navigation_for_non_CLR_property_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ Assert.Equal(
+ CoreStrings.NoClrNavigation("Snoop", nameof(Dr)),
+ Assert.Throws(() =>
+ ((NonGenericTestEntityTypeBuilder)modelBuilder.Entity()).GetInfrastructure()
+ .HasOne("Snoop")).Message);
+ }
+
+ [ConditionalFact]
+ public virtual void HasMany_with_just_string_navigation_for_non_CLR_property_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ Assert.Equal(
+ CoreStrings.NoClrNavigation("Snoop", nameof(Dr)),
+ Assert.Throws(() =>
+ ((NonGenericTestEntityTypeBuilder)modelBuilder.Entity()).GetInfrastructure()
+ .HasMany("Snoop")).Message);
+ }
+
+ [ConditionalFact]
+ public virtual void HasMany_with_a_non_collection_just_string_navigation_CLR_property_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ Assert.Equal(
+ CoreStrings.NavigationCollectionWrongClrType("Dre", nameof(Dr), nameof(Dre), "T"),
+ Assert.Throws(() =>
+ ((NonGenericTestEntityTypeBuilder)modelBuilder.Entity()).GetInfrastructure()
+ .HasMany("Dre")).Message);
+ }
+
+ [ConditionalFact] //Issue#13108
+ public virtual void HasForeignKey_infers_type_for_shadow_property_when_not_specified()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity(
+ e =>
+ {
+ e.HasKey(c => c.Key);
+ ((NonGenericTestEntityTypeBuilder)e).GetInfrastructure().Property("ParentKey");
+ e.HasOne(c => c.Parent).WithMany(c => c.Children).HasForeignKey("ParentKey");
+ });
+
+ modelBuilder.Entity().HasKey(c => c.Key);
+
+ var model = (IConventionModel)modelBuilder.FinalizeModel();
+ var property = model
+ .FindEntityType(typeof(ComplexCaseChild13108)).GetProperties().Single(p => p.Name == "ParentKey");
+ Assert.Equal(typeof(int), property.ClrType);
+ Assert.Equal(ConfigurationSource.Explicit, property.GetTypeConfigurationSource());
+ }
+
+ protected class ComplexCaseChild13108
+ {
+ public int Key { get; set; }
+ public string Id { get; set; }
+ private int ParentKey { get; set; }
+ public ComplexCaseParent13108 Parent { get; set; }
+ }
+
+ protected class ComplexCaseParent13108
+ {
+ public int Key { get; set; }
+ public string Id { get; set; }
+ public ICollection Children { get; set; }
+ }
+
protected override TestModelBuilder CreateTestModelBuilder(TestHelpers testHelpers)
=> new NonGenericTestModelBuilder(testHelpers);
}
diff --git a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs
index 97899ca39fd..485065f1522 100644
--- a/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/NonRelationshipTestBase.cs
@@ -8,10 +8,12 @@
using System.Text;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.EntityFrameworkCore.TestUtilities.Xunit;
using Microsoft.EntityFrameworkCore.ValueGeneration;
using Xunit;
@@ -1081,6 +1083,103 @@ protected class StringCollectionEntity
public ICollection Property { get; set; }
}
+ [ConditionalFact]
+ protected virtual void Mapping_throws_for_non_ignored_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ Assert.Equal(
+ CoreStrings.PropertyNotAdded(
+ typeof(OneDee).ShortDisplayName(), "One", typeof(int[]).ShortDisplayName()),
+ Assert.Throws(() => modelBuilder.FinalizeModel()).Message);
+ }
+
+ [ConditionalFact]
+ protected virtual void Mapping_ignores_ignored_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().Ignore(e => e.One);
+
+ var model = modelBuilder.FinalizeModel();
+
+ Assert.Null(model.FindEntityType(typeof(OneDee)).FindProperty("One"));
+ }
+
+ [ConditionalFact]
+ protected virtual void Mapping_throws_for_non_ignored_two_dimensional_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ Assert.Equal(
+ CoreStrings.PropertyNotAdded(
+ typeof(TwoDee).ShortDisplayName(), "Two", typeof(int[,]).ShortDisplayName()),
+ Assert.Throws(() => modelBuilder.FinalizeModel()).Message);
+ }
+
+ [ConditionalFact]
+ protected virtual void Mapping_ignores_ignored_two_dimensional_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().Ignore(e => e.Two);
+
+ var model = modelBuilder.FinalizeModel();
+
+ Assert.Null(model.FindEntityType(typeof(TwoDee)).FindProperty("Two"));
+ }
+
+ [ConditionalFact]
+ protected virtual void Mapping_throws_for_non_ignored_three_dimensional_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+
+ Assert.Equal(
+ CoreStrings.PropertyNotAdded(
+ typeof(ThreeDee).ShortDisplayName(), "Three", typeof(int[,,]).ShortDisplayName()),
+ Assert.Throws(() => modelBuilder.FinalizeModel()).Message);
+ }
+
+ [ConditionalFact]
+ protected virtual void Mapping_ignores_ignored_three_dimensional_array()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().Ignore(e => e.Three);
+
+ var model = modelBuilder.FinalizeModel();
+
+ Assert.Null(model.FindEntityType(typeof(ThreeDee)).FindProperty("Three"));
+ }
+
+ protected class OneDee
+ {
+ public int Id { get; set; }
+
+ public int[] One { get; set; }
+ }
+
+ protected class TwoDee
+ {
+ public int Id { get; set; }
+
+ public int[,] Two { get; set; }
+ }
+
+ protected class ThreeDee
+ {
+ public int Id { get; set; }
+
+ public int[,,] Three { get; set; }
+ }
+
+
[ConditionalFact]
public virtual void Can_set_unicode_for_properties()
{
@@ -1527,7 +1626,6 @@ public virtual void Can_add_seed_data_objects_indexed_property_dictionary()
public virtual void Can_add_seed_data_anonymous_objects_indexed_property_dictionary()
{
var modelBuilder = CreateModelBuilder();
- var model = modelBuilder.Model;
modelBuilder.Entity(
b =>
{
@@ -1536,7 +1634,7 @@ public virtual void Can_add_seed_data_anonymous_objects_indexed_property_diction
b.HasData(new { Id = -1, Required = 2 });
});
- modelBuilder.FinalizeModel();
+ var model = modelBuilder.FinalizeModel();
var entityType = model.FindEntityType(typeof(IndexedClassByDictionary));
var data = Assert.Single(entityType.GetSeedData());
@@ -1545,6 +1643,33 @@ public virtual void Can_add_seed_data_anonymous_objects_indexed_property_diction
Assert.False(data.ContainsKey("Optional"));
}
+ [ConditionalFact] //Issue#12617
+ [UseCulture("de-DE")]
+ public virtual void EntityType_name_is_stored_culture_invariantly()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity();
+ modelBuilder.Entity();
+
+ var model = modelBuilder.FinalizeModel();
+
+ Assert.Equal(2, model.GetEntityTypes().Count());
+ Assert.Equal(2, model.FindEntityType(typeof(Entityss)).GetNavigations().Count());
+ }
+
+ protected class Entityß
+ {
+ public int Id { get; set; }
+ }
+
+ protected class Entityss
+ {
+ public int Id { get; set; }
+ public Entityß Navigationß { get; set; }
+ public Entityß Navigationss { get; set; }
+ }
+
[ConditionalFact]
public virtual void Can_add_shared_type_entity_type()
{
diff --git a/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs b/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs
index 73b6b743041..03fbf4cbd13 100644
--- a/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/OneToManyTestBase.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
@@ -1882,6 +1883,16 @@ public virtual void Throws_on_existing_one_to_one_relationship()
modelBuilder.Entity().HasMany(e => e.Hobs).WithOne(e => e.Nob)).Message);
}
+ [ConditionalFact]
+ public virtual void HasMany_with_a_collection_navigation_CLR_property_to_derived_type_throws()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ Assert.Equal(
+ CoreStrings.NavigationCollectionWrongClrType(nameof(Dr.Jrs), nameof(Dr), "ICollection", nameof(Dre)),
+ Assert.Throws(() => modelBuilder.Entity().HasMany(d => d.Jrs)).Message);
+ }
+
[ConditionalFact]
public virtual void Removes_existing_unidirectional_one_to_one_relationship()
{
diff --git a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
index 89f8d8f2120..9d8d1ad26b6 100644
--- a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
@@ -511,24 +511,25 @@ public virtual void Creates_both_navigations_and_uses_specified_FK_even_if_PK()
public virtual void Creates_both_navigations_and_uses_existing_FK_not_found_by_convention()
{
var modelBuilder = CreateModelBuilder();
- var model = modelBuilder.Model;
modelBuilder.Entity();
modelBuilder.Entity().HasOne().WithOne()
.HasForeignKey(e => e.BurgerId);
modelBuilder.Ignore();
+ modelBuilder
+ .Entity().HasOne(e => e.Bun).WithOne(e => e.BigMak)
+ .HasForeignKey(e => e.BurgerId);
+
+ var model = modelBuilder.FinalizeModel();
+
var dependentType = model.FindEntityType(typeof(Bun));
var principalType = model.FindEntityType(typeof(BigMak));
var fk = dependentType.GetForeignKeys().Single(foreignKey => foreignKey.Properties.All(p => p.Name == "BurgerId"));
- fk.IsUnique = true;
+ Assert.True(fk.IsUnique);
var principalKey = principalType.FindPrimaryKey();
var dependentKey = dependentType.FindPrimaryKey();
- modelBuilder
- .Entity().HasOne(e => e.Bun).WithOne(e => e.BigMak)
- .HasForeignKey(e => e.BurgerId);
-
Assert.Same(fk, dependentType.GetForeignKeys().Single());
Assert.Equal("BigMak", dependentType.GetNavigations().Single().Name);
Assert.Equal("Bun", principalType.GetNavigations().Single().Name);
diff --git a/test/EFCore.Tests/ModelBuilding/TestModel.cs b/test/EFCore.Tests/ModelBuilding/TestModel.cs
index 0941082bac8..624fa856e0d 100644
--- a/test/EFCore.Tests/ModelBuilding/TestModel.cs
+++ b/test/EFCore.Tests/ModelBuilding/TestModel.cs
@@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.ModelBuilding
{
public abstract partial class ModelBuilderTest
{
- private class BigMak
+ protected class BigMak
{
public int Id { get; set; }
public int AlternateKey { get; set; }
@@ -25,7 +25,7 @@ private class BigMak
public Bun Bun { get; set; }
}
- private class Ingredient
+ protected class Ingredient
{
public static readonly PropertyInfo BurgerIdProperty = typeof(Ingredient).GetProperty("BurgerId");
@@ -34,15 +34,19 @@ private class Ingredient
public BigMak BigMak { get; set; }
}
- private class Pickle : Ingredient
+ protected class Pickle : Ingredient
{
}
- private class Bun : Ingredient
+ protected class Bun : Ingredient
{
}
- private class Whoopper
+ protected class SesameBun : Bun
+ {
+ }
+
+ protected class Whoopper
{
public int Id1 { get; set; }
public int Id2 { get; set; }
@@ -56,7 +60,7 @@ private class Whoopper
public Mustard Mustard { get; set; }
}
- private class Tomato
+ protected class Tomato
{
public int Id { get; set; }
@@ -65,7 +69,7 @@ private class Tomato
public Whoopper Whoopper { get; set; }
}
- private class ToastedBun
+ protected class ToastedBun
{
public int Id { get; set; }
@@ -74,7 +78,7 @@ private class ToastedBun
public Whoopper Whoopper { get; set; }
}
- private class Mustard
+ protected class Mustard
{
public int Id1 { get; set; }
public int Id2 { get; set; }
@@ -1126,5 +1130,22 @@ protected class NestedOwnerOfSharedType
public Dictionary Reference { get; set; }
public List> Collection { get; set; }
}
+
+ protected class Dr
+ {
+ public int Id { get; set; }
+
+ public Dre Dre { get; set; }
+
+ public ICollection Jrs { get; set; }
+ }
+
+ protected class Dre
+ {
+ }
+
+ protected class DreJr : Dre
+ {
+ }
}
}