From 9d53615345111447fcd47fb8875fe1a543771512 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Fri, 22 Apr 2016 11:31:03 -0700 Subject: [PATCH] Metadata: Allow creating shadow navigation on shadow entity types --- .../Design/CSharpSnapshotGenerator.cs | 33 ++- .../Metadata/Internal/EntityType.cs | 34 ++- .../Metadata/Internal/Navigation.cs | 17 +- .../Properties/CoreStrings.Designer.cs | 25 +- .../Properties/CoreStrings.resx | 3 - .../Migrations/ModelSnapshotTest.cs | 224 +++++++++++++----- .../Metadata/Internal/EntityTypeTest.cs | 26 +- .../ModelBuilderNonGenericStringTest.cs | 209 +++++++++++++++- 8 files changed, 464 insertions(+), 107 deletions(-) diff --git a/src/Microsoft.EntityFrameworkCore.Commands/Migrations/Design/CSharpSnapshotGenerator.cs b/src/Microsoft.EntityFrameworkCore.Commands/Migrations/Design/CSharpSnapshotGenerator.cs index 3f6b051e157..4da7010a315 100644 --- a/src/Microsoft.EntityFrameworkCore.Commands/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/Microsoft.EntityFrameworkCore.Commands/Migrations/Design/CSharpSnapshotGenerator.cs @@ -467,7 +467,16 @@ protected virtual void GenerateForeignKey( stringBuilder .AppendLine() .Append("b.HasOne(") - .Append(_code.Literal(foreignKey.PrincipalEntityType.Name)) + .Append(_code.Literal(foreignKey.PrincipalEntityType.Name)); + + if (foreignKey.DependentToPrincipal != null) + { + stringBuilder + .Append(", ") + .Append(_code.Literal(foreignKey.DependentToPrincipal.Name)); + } + + stringBuilder .Append(")") .AppendLine(); @@ -476,7 +485,16 @@ protected virtual void GenerateForeignKey( if (foreignKey.IsUnique) { stringBuilder - .AppendLine(".WithOne()") + .Append(".WithOne("); + + if (foreignKey.PrincipalToDependent != null) + { + stringBuilder + .Append(_code.Literal(foreignKey.PrincipalToDependent.Name)); + } + + stringBuilder + .AppendLine(")") .Append(".HasForeignKey(") .Append(_code.Literal(foreignKey.DeclaringEntityType.Name)) .Append(", ") @@ -499,7 +517,16 @@ protected virtual void GenerateForeignKey( else { stringBuilder - .AppendLine(".WithMany()") + .Append(".WithMany("); + + if (foreignKey.PrincipalToDependent != null) + { + stringBuilder + .Append(_code.Literal(foreignKey.PrincipalToDependent.Name)); + } + + stringBuilder + .AppendLine(")") .Append(".HasForeignKey(") .Append(string.Join(", ", foreignKey.Properties.Select(p => _code.Literal(p.Name)))) .Append(")"); diff --git a/src/Microsoft.EntityFrameworkCore/Metadata/Internal/EntityType.cs b/src/Microsoft.EntityFrameworkCore/Metadata/Internal/EntityType.cs index fdd6623ba81..e43ba477265 100644 --- a/src/Microsoft.EntityFrameworkCore/Metadata/Internal/EntityType.cs +++ b/src/Microsoft.EntityFrameworkCore/Metadata/Internal/EntityType.cs @@ -848,18 +848,28 @@ public virtual Navigation AddNavigation( Debug.Assert((pointsToPrincipal ? foreignKey.DeclaringEntityType : foreignKey.PrincipalEntityType) == this, "EntityType mismatch"); - Navigation.IsCompatible( - name, - this, - pointsToPrincipal ? foreignKey.PrincipalEntityType : foreignKey.DeclaringEntityType, - !pointsToPrincipal && !foreignKey.IsUnique, - shouldThrow: true); - - // TODO: use this value for IsCompatible call - var navigationProperty = ClrType.GetPropertiesInHierarchy(name).FirstOrDefault(); - Debug.Assert(navigationProperty != null); - - var navigation = new Navigation(navigationProperty, foreignKey); + Navigation navigation = null; + + if (ClrType != null) + { + Navigation.IsCompatible( + name, + this, + pointsToPrincipal ? foreignKey.PrincipalEntityType : foreignKey.DeclaringEntityType, + !pointsToPrincipal && !foreignKey.IsUnique, + shouldThrow: true); + + // TODO: use this value for IsCompatible call + var navigationProperty = ClrType.GetPropertiesInHierarchy(name).FirstOrDefault(); + Debug.Assert(navigationProperty != null); + + navigation = new Navigation(navigationProperty, foreignKey); + } + else + { + navigation = new Navigation(name, foreignKey); + } + _navigations.Add(name, navigation); PropertyMetadataChanged(); diff --git a/src/Microsoft.EntityFrameworkCore/Metadata/Internal/Navigation.cs b/src/Microsoft.EntityFrameworkCore/Metadata/Internal/Navigation.cs index 612f5051f79..bf370529ca5 100644 --- a/src/Microsoft.EntityFrameworkCore/Metadata/Internal/Navigation.cs +++ b/src/Microsoft.EntityFrameworkCore/Metadata/Internal/Navigation.cs @@ -36,6 +36,15 @@ public Navigation([NotNull] PropertyInfo navigationProperty, [NotNull] ForeignKe ForeignKey = foreignKey; } + public Navigation([NotNull] string navigationName, [NotNull] ForeignKey foreignKey) + { + Check.NotEmpty(navigationName, nameof(navigationName)); + Check.NotNull(foreignKey, nameof(foreignKey)); + + Name = navigationName; + ForeignKey = foreignKey; + } + public virtual string Name { get; } public virtual ForeignKey ForeignKey { get; } @@ -96,12 +105,8 @@ public static bool IsCompatible( var sourceClrType = sourceType.ClrType; if (sourceClrType == null) { - if (shouldThrow) - { - throw new InvalidOperationException( - CoreStrings.NavigationOnShadowEntity(navigationPropertyName, sourceType.DisplayName())); - } - return false; + // Navigation defined on shadow entity type. + return true; } var targetClrType = targetType.ClrType; diff --git a/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.Designer.cs b/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.Designer.cs index f54ad3d5bc9..239f19e0635 100644 --- a/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.Designer.cs +++ b/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.Designer.cs @@ -228,15 +228,6 @@ public static string NoValueGenerator([CanBeNull] object property, [CanBeNull] o return string.Format(CultureInfo.CurrentCulture, GetString("NoValueGenerator", "property", "entityType", "propertyType"), property, entityType, propertyType); } - /// - /// The property '{property}' on entity type '{entityType}' has a temporary value. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property. - /// - public static string TempValue([CanBeNull] object property, [CanBeNull] object entityType) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TempValue", "property", "entityType"), property, entityType); - } - - /// /// The property '{property}' on entity type '{entityType}' has a temporary value while attempting to change the entity's state to '{state}'. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property. /// @@ -325,14 +316,6 @@ public static string DuplicateNavigation([CanBeNull] object navigation, [CanBeNu return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateNavigation", "navigation", "entityType", "duplicateEntityType"), navigation, entityType, duplicateEntityType); } - /// - /// The navigation property '{navigation}' cannot be added to the entity type '{entityType}' because the entity type is defined in shadow state and navigations properties cannot be added to shadow state. - /// - public static string NavigationOnShadowEntity([CanBeNull] object navigation, [CanBeNull] object entityType) - { - return string.Format(CultureInfo.CurrentCulture, GetString("NavigationOnShadowEntity", "navigation", "entityType"), navigation, entityType); - } - /// /// The navigation property '{navigation}' cannot be added to the entity type '{entityType}' because there is no corresponding CLR property on the underlying type and navigations properties cannot be added to shadow state. /// @@ -1157,6 +1140,14 @@ public static string NoPropertyType([CanBeNull] object property, [CanBeNull] obj return string.Format(CultureInfo.CurrentCulture, GetString("NoPropertyType", "property", "entityType"), property, entityType); } + /// + /// The property '{property}' on entity type '{entityType}' has a temporary value. Either set a permanent value explicitly or ensure that the database is configured to generate values for this property. + /// + public static string TempValue([CanBeNull] object property, [CanBeNull] object entityType) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TempValue", "property", "entityType"), property, entityType); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx b/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx index fb382c25a1b..a6d69bfd1e6 100644 --- a/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx +++ b/src/Microsoft.EntityFrameworkCore/Properties/CoreStrings.resx @@ -231,9 +231,6 @@ The navigation property '{navigation}' cannot be added to the entity type '{entityType}' because a navigation property with the same name already exists on entity type '{duplicateEntityType}'. - - The navigation property '{navigation}' cannot be added to the entity type '{entityType}' because the entity type is defined in shadow state and navigations properties cannot be added to shadow state. - The navigation property '{navigation}' cannot be added to the entity type '{entityType}' because there is no corresponding CLR property on the underlying type and navigations properties cannot be added to shadow state. diff --git a/test/Microsoft.EntityFrameworkCore.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs b/test/Microsoft.EntityFrameworkCore.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs index f201a244800..32977a763b8 100644 --- a/test/Microsoft.EntityFrameworkCore.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs +++ b/test/Microsoft.EntityFrameworkCore.Commands.FunctionalTests/Migrations/ModelSnapshotTest.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 System.Reflection; using Microsoft.EntityFrameworkCore.Specification.Tests; @@ -21,12 +22,14 @@ public class ModelSnapshotTest private class EntityWithOneProperty { public int Id { get; set; } + public EntityWithTwoProperties EntityWithTwoProperties { get; set; } } private class EntityWithTwoProperties { public int Id { get; set; } public int AlternateId { get; set; } + public EntityWithOneProperty EntityWithOneProperty { get; set; } } private class EntityWithStringProperty @@ -38,6 +41,7 @@ private class EntityWithStringProperty private class EntityWithStringKey { public string Id { get; set; } + public ICollection Properties { get; set; } } private class EntityWithGenericKey @@ -140,8 +144,8 @@ public void Entities_are_stored_in_model_snapshot() Test( builder => { - builder.Entity(); - builder.Entity(); + builder.Entity().Ignore(e => e.EntityWithTwoProperties); + builder.Entity().Ignore(e => e.EntityWithOneProperty); }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => @@ -184,7 +188,11 @@ public void Entities_are_stored_in_model_snapshot() public void EntityType_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { @@ -322,7 +330,11 @@ public void Discriminator_annotations_are_stored_in_snapshot() public void Properties_are_stored_in_snapshot() { Test( - builder => { builder.Entity(); }, + builder => + { + builder.Entity(); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -351,7 +363,11 @@ public void Properties_are_stored_in_snapshot() public void Primary_key_is_stored_in_snapshot() { Test( - builder => { builder.Entity().HasKey(t => new { t.Id, t.AlternateId }); }, + builder => + { + builder.Entity().HasKey(t => new { t.Id, t.AlternateId }); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -379,7 +395,11 @@ public void Primary_key_is_stored_in_snapshot() public void Alternate_keys_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasAlternateKey(t => new { t.Id, t.AlternateId }); }, + builder => + { + builder.Entity().HasAlternateKey(t => new { t.Id, t.AlternateId }); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -409,7 +429,11 @@ public void Alternate_keys_are_stored_in_snapshot() public void Indexes_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasIndex(t => t.AlternateId); }, + builder => + { + builder.Entity().HasIndex(t => t.AlternateId); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -436,7 +460,11 @@ public void Indexes_are_stored_in_snapshot() public void Indexes_are_stored_in_snapshot_including_composite_index() { Test( - builder => { builder.Entity().HasIndex(t => new { t.Id, t.AlternateId }); }, + builder => + { + builder.Entity().HasIndex(t => new { t.Id, t.AlternateId }); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -466,7 +494,14 @@ public void Indexes_are_stored_in_snapshot_including_composite_index() public void Foreign_keys_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasOne().WithOne().HasForeignKey(e => e.AlternateId); }, + builder => + { + builder + .Entity() + .HasOne(e => e.EntityWithOneProperty) + .WithOne(e => e.EntityWithTwoProperties) + .HasForeignKey(e => e.AlternateId); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { @@ -494,16 +529,18 @@ public void Foreign_keys_are_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""EntityWithOneProperty"") + .WithOne(""EntityWithTwoProperties"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""AlternateId"") .OnDelete(DeleteBehavior.Cascade); }); ", o => { - Assert.Equal(1, o.FindEntityType(typeof(EntityWithTwoProperties)).GetForeignKeys().Count()); - Assert.Equal("AlternateId", o.FindEntityType(typeof(EntityWithTwoProperties)).GetForeignKeys().First().Properties[0].Name); + var foreignKey = o.FindEntityType(typeof(EntityWithTwoProperties)).GetForeignKeys().Single(); + Assert.Equal("AlternateId", foreignKey.Properties[0].Name); + Assert.Equal("EntityWithTwoProperties", foreignKey.PrincipalToDependent.Name); + Assert.Equal("EntityWithOneProperty", foreignKey.DependentToPrincipal.Name); }); } @@ -513,9 +550,11 @@ public void Relationship_principal_key_is_stored_in_snapshot() Test( builder => { - builder.Entity().HasOne().WithOne() - .HasForeignKey(e => e.Id). - HasPrincipalKey(e => e.AlternateId); + builder.Entity() + .HasOne(e => e.EntityWithTwoProperties) + .WithOne(e => e.EntityWithOneProperty) + .HasForeignKey(e => e.Id) + .HasPrincipalKey(e => e.AlternateId); }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => @@ -543,8 +582,8 @@ public void Relationship_principal_key_is_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""EntityWithTwoProperties"") + .WithOne(""EntityWithOneProperty"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""Id"") .HasPrincipalKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""AlternateId"") .OnDelete(DeleteBehavior.Cascade); @@ -553,7 +592,7 @@ public void Relationship_principal_key_is_stored_in_snapshot() o => { Assert.Equal(2, o.FindEntityType(typeof(EntityWithTwoProperties)).GetKeys().Count()); - Assert.True(o.FindEntityType(typeof(EntityWithTwoProperties)).GetKeys().Any(k => k.Properties.Any(p => p.Name == "AlternateId"))); + Assert.True(o.FindEntityType(typeof(EntityWithTwoProperties)).FindProperty("AlternateId").IsKey()); }); } @@ -674,7 +713,11 @@ public void AlternateKey_name_preserved_when_generic() public void Property_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().Property("Id").HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().Property("Id").HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { @@ -717,7 +760,11 @@ public void Property_isNullable_is_stored_in_snapshot() public void Property_ValueGenerated_value_is_stored_in_snapshot() { Test( - builder => { builder.Entity().Property("AlternateId").ValueGeneratedOnAdd(); }, + builder => + { + builder.Entity().Property("AlternateId").ValueGeneratedOnAdd(); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -761,7 +808,11 @@ public void Property_maxLength_is_stored_in_snapshot() public void Property_RequiresValueGenerator_is_not_stored_in_snapshot() { Test( - builder => { builder.Entity().Property("AlternateId").Metadata.RequiresValueGenerator = true; }, + builder => + { + builder.Entity().Property("AlternateId").Metadata.RequiresValueGenerator = true; + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -782,7 +833,11 @@ public void Property_RequiresValueGenerator_is_not_stored_in_snapshot() public void Property_concurrencyToken_is_stored_in_snapshot() { Test( - builder => { builder.Entity().Property("AlternateId").IsConcurrencyToken(); }, + builder => + { + builder.Entity().Property("AlternateId").IsConcurrencyToken(); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -804,7 +859,11 @@ public void Property_concurrencyToken_is_stored_in_snapshot() public void Property_column_name_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().Property("AlternateId").HasColumnName("CName"); }, + builder => + { + builder.Entity().Property("AlternateId").HasColumnName("CName"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -826,7 +885,11 @@ public void Property_column_name_annotation_is_stored_in_snapshot_as_fluent_api( public void Property_column_type_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().Property("AlternateId").HasColumnType("CType"); }, + builder => + { + builder.Entity().Property("AlternateId").HasColumnType("CType"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -848,7 +911,11 @@ public void Property_column_type_annotation_is_stored_in_snapshot_as_fluent_api( public void Property_default_value_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().Property("AlternateId").HasDefaultValue(1); }, + builder => + { + builder.Entity().Property("AlternateId").HasDefaultValue(1); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -871,7 +938,11 @@ public void Property_default_value_annotation_is_stored_in_snapshot_as_fluent_ap public void Property_default_value_sql_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().Property("AlternateId").HasDefaultValueSql("SQL"); }, + builder => + { + builder.Entity().Property("AlternateId").HasDefaultValueSql("SQL"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -894,7 +965,11 @@ public void Property_default_value_sql_annotation_is_stored_in_snapshot_as_fluen public void Property_computed_column_sql_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().Property("AlternateId").HasComputedColumnSql("SQL"); }, + builder => + { + builder.Entity().Property("AlternateId").HasComputedColumnSql("SQL"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -940,7 +1015,11 @@ public void Property_default_value_of_enum_type_is_stored_in_snapshot_without_ac public void Property_multiple_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().Property("AlternateId").HasColumnName("CName").HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().Property("AlternateId").HasColumnName("CName").HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -973,7 +1052,11 @@ public void Property_multiple_annotations_are_stored_in_snapshot() public void Key_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasAlternateKey(t => t.AlternateId).HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().HasAlternateKey(t => t.AlternateId).HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -997,7 +1080,11 @@ public void Key_annotations_are_stored_in_snapshot() public void Key_name_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().HasAlternateKey(t => t.AlternateId).HasName("KeyName"); }, + builder => + { + builder.Entity().HasAlternateKey(t => t.AlternateId).HasName("KeyName"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1021,7 +1108,11 @@ public void Key_name_annotation_is_stored_in_snapshot_as_fluent_api() public void Key_multiple_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasAlternateKey(t => t.AlternateId).HasName("IndexName").HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().HasAlternateKey(t => t.AlternateId).HasName("IndexName").HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1056,7 +1147,11 @@ public void Key_multiple_annotations_are_stored_in_snapshot() public void Index_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasIndex(t => t.AlternateId).HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().HasIndex(t => t.AlternateId).HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1080,7 +1175,11 @@ public void Index_annotations_are_stored_in_snapshot() public void Index_isUnique_is_stored_in_snapshot() { Test( - builder => { builder.Entity().HasIndex(t => t.AlternateId).IsUnique(); }, + builder => + { + builder.Entity().HasIndex(t => t.AlternateId).IsUnique(); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1104,7 +1203,11 @@ public void Index_isUnique_is_stored_in_snapshot() public void Index_name_annotation_is_stored_in_snapshot_as_fluent_api() { Test( - builder => { builder.Entity().HasIndex(t => t.AlternateId).HasName("IndexName"); }, + builder => + { + builder.Entity().HasIndex(t => t.AlternateId).HasName("IndexName"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1128,7 +1231,11 @@ public void Index_name_annotation_is_stored_in_snapshot_as_fluent_api() public void Index_multiple_annotations_are_stored_in_snapshot() { Test( - builder => { builder.Entity().HasIndex(t => t.AlternateId).HasName("IndexName").HasAnnotation("AnnotationName", "AnnotationValue"); }, + builder => + { + builder.Entity().HasIndex(t => t.AlternateId).HasName("IndexName").HasAnnotation("AnnotationName", "AnnotationValue"); + builder.Ignore(); + }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { @@ -1166,8 +1273,8 @@ public void ForeignKey_annotations_are_stored_in_snapshot() builder => { builder.Entity() - .HasOne() - .WithOne() + .HasOne(e => e.EntityWithOneProperty) + .WithOne(e => e.EntityWithTwoProperties) .HasForeignKey(e => e.AlternateId) .HasAnnotation("AnnotationName", "AnnotationValue"); }, @@ -1198,8 +1305,8 @@ public void ForeignKey_annotations_are_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""EntityWithOneProperty"") + .WithOne(""EntityWithTwoProperties"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""AlternateId"") .HasAnnotation(""AnnotationName"", ""AnnotationValue"") .OnDelete(DeleteBehavior.Cascade); @@ -1214,6 +1321,7 @@ public void ForeignKey_isRequired_is_stored_in_snapshot() Test( builder => { + builder.Entity().Ignore(e => e.Properties); builder.Entity() .HasOne() .WithOne() @@ -1264,7 +1372,7 @@ public void ForeignKey_isUnique_is_stored_in_snapshot() { builder.Entity() .HasOne() - .WithMany() + .WithMany(e => e.Properties) .HasForeignKey(e => e.Name); }, @" @@ -1294,7 +1402,7 @@ public void ForeignKey_isUnique_is_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithStringProperty"", b => { b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithStringKey"") - .WithMany() + .WithMany(""Properties"") .HasForeignKey(""Name""); }); ", @@ -1308,9 +1416,10 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot() builder => { builder.Entity() - .HasOne() + .HasOne(e => e.EntityWithTwoProperties) .WithMany() .HasForeignKey(e => e.Id); + builder.Entity().Ignore(e => e.EntityWithOneProperty); }, @" builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => @@ -1338,7 +1447,7 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"") + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""EntityWithTwoProperties"") .WithMany() .HasForeignKey(""Id"") .OnDelete(DeleteBehavior.Cascade); @@ -1354,8 +1463,8 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot_for_one_to_one() builder => { builder.Entity() - .HasOne() - .WithOne() + .HasOne(e => e.EntityWithTwoProperties) + .WithOne(e => e.EntityWithOneProperty) .HasForeignKey(e => e.Id); }, @" @@ -1384,8 +1493,8 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot_for_one_to_one() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""EntityWithTwoProperties"") + .WithOne(""EntityWithOneProperty"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""Id"") .OnDelete(DeleteBehavior.Cascade); }); @@ -1474,8 +1583,8 @@ public void ForeignKey_constraint_name_is_stored_in_snapshot_as_fluent_api() builder => { builder.Entity() - .HasOne() - .WithOne() + .HasOne(e => e.EntityWithOneProperty) + .WithOne(e => e.EntityWithTwoProperties) .HasForeignKey(e => e.AlternateId) .HasConstraintName("Constraint"); }, @@ -1506,8 +1615,8 @@ public void ForeignKey_constraint_name_is_stored_in_snapshot_as_fluent_api() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""EntityWithOneProperty"") + .WithOne(""EntityWithTwoProperties"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""AlternateId"") .HasConstraintName(""Constraint"") .OnDelete(DeleteBehavior.Cascade); @@ -1523,8 +1632,8 @@ public void ForeignKey_multiple_annotations_are_stored_in_snapshot() builder => { builder.Entity() - .HasOne() - .WithOne() + .HasOne(e => e.EntityWithOneProperty) + .WithOne(e => e.EntityWithTwoProperties) .HasForeignKey(e => e.AlternateId) .HasAnnotation("AnnotationName", "AnnotationValue") .HasConstraintName("Constraint"); @@ -1556,8 +1665,8 @@ public void ForeignKey_multiple_annotations_are_stored_in_snapshot() builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"") - .WithOne() + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""EntityWithOneProperty"") + .WithOne(""EntityWithTwoProperties"") .HasForeignKey(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", ""AlternateId"") .HasConstraintName(""Constraint"") .HasAnnotation(""AnnotationName"", ""AnnotationValue"") @@ -1580,6 +1689,7 @@ public void Do_not_generate_entity_type_builder_again_if_no_foreign_key_is_defin builder => { builder.Entity(); + builder.Ignore(); builder.Entity(); }, @" @@ -1617,7 +1727,7 @@ public void Do_not_generate_entity_type_builder_again_if_no_foreign_key_is_defin builder.Entity(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+BaseType"", b => { - b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"") + b.HasOne(""Microsoft.EntityFrameworkCore.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", ""Navigation"") .WithMany() .HasForeignKey(""NavigationId""); }); diff --git a/test/Microsoft.EntityFrameworkCore.Tests/Metadata/Internal/EntityTypeTest.cs b/test/Microsoft.EntityFrameworkCore.Tests/Metadata/Internal/EntityTypeTest.cs index b9c4cc9e3fe..2e414cd1061 100644 --- a/test/Microsoft.EntityFrameworkCore.Tests/Metadata/Internal/EntityTypeTest.cs +++ b/test/Microsoft.EntityFrameworkCore.Tests/Metadata/Internal/EntityTypeTest.cs @@ -1972,7 +1972,7 @@ public void Adding_a_new_navigation_with_a_name_that_conflicts_with_a_property_t } [Fact] - public void Adding_a_navigation_to_a_shadow_entity_type_throws() + public void Can_add_a_navigation_to_shadow_entity() { var model = new Model(); var customerType = model.AddEntityType(typeof(Customer)); @@ -1982,14 +1982,11 @@ public void Adding_a_navigation_to_a_shadow_entity_type_throws() var foreignKeyProperty = orderType.AddProperty("CustomerId", typeof(int)); var customerForeignKey = orderType.GetOrAddForeignKey(foreignKeyProperty, customerKey, customerType); - Assert.Equal( - CoreStrings.NavigationOnShadowEntity("Customer", "Order"), - Assert.Throws( - () => customerForeignKey.HasDependentToPrincipal("Customer")).Message); + Assert.NotNull(customerForeignKey.HasDependentToPrincipal("Customer")); } [Fact] - public void Adding_a_navigation_pointing_to_a_shadow_entity_type_throws() + public void Adding_a_navigation_on_non_shadow_entity_type_pointing_to_a_shadow_entity_type_throws() { var model = new Model(); var customerType = model.AddEntityType("Customer"); @@ -2005,6 +2002,23 @@ public void Adding_a_navigation_pointing_to_a_shadow_entity_type_throws() () => customerForeignKey.HasDependentToPrincipal("Customer")).Message); } + [Fact] + public void Adding_a_shadow_navigation_on_a_non_shadow_entity_type_throws() + { + var model = new Model(); + var customerType = model.AddEntityType(typeof(Customer)); + var customerKey = customerType.GetOrAddKey(customerType.AddProperty("Id", typeof(int))); + + var orderType = model.AddEntityType(typeof(Order)); + var foreignKeyProperty = orderType.AddProperty("CustomerId", typeof(int)); + var customerForeignKey = orderType.GetOrAddForeignKey(foreignKeyProperty, customerKey, customerType); + + Assert.Equal( + CoreStrings.NoClrNavigation("Navigation", typeof(Order).Name), + Assert.Throws( + () => customerForeignKey.HasDependentToPrincipal("Navigation")).Message); + } + [Fact] public void Adding_a_navigation_that_doesnt_match_a_CLR_property_throws() { diff --git a/test/Microsoft.EntityFrameworkCore.Tests/ModelBuilderTest/ModelBuilderNonGenericStringTest.cs b/test/Microsoft.EntityFrameworkCore.Tests/ModelBuilderTest/ModelBuilderNonGenericStringTest.cs index 9edded211f9..50a6bc1d3f9 100644 --- a/test/Microsoft.EntityFrameworkCore.Tests/ModelBuilderTest/ModelBuilderNonGenericStringTest.cs +++ b/test/Microsoft.EntityFrameworkCore.Tests/ModelBuilderTest/ModelBuilderNonGenericStringTest.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Xunit; @@ -15,18 +16,141 @@ namespace Microsoft.EntityFrameworkCore.Tests { public class ModelBuilderNonGenericStringTest : ModelBuilderNonGenericTest { - public class NonGenericOneToManyType : OneToManyTestBase + public class NonGenericStringOneToManyType : OneToManyTestBase { + [Fact] + public virtual void Can_create_shadow_navigations_between_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var foreignKey = modelBuilder.Entity("Order") + .HasOne("Customer", "Customer") + .WithMany("Orders") + .Metadata; + + Assert.Equal("Customer", modelBuilder.Model.FindEntityType("Order")?.GetNavigations().Single().Name); + Assert.Equal("Orders", modelBuilder.Model.FindEntityType("Customer")?.GetNavigations().Single().Name); + Assert.False(foreignKey.IsUnique); + + // TODO: Issue#4016 - The Exception message here should be about shadow entity type instead of shadow key. + Assert.Equal( + CoreStrings.ReferencedShadowKey("{'TempId'}", "Customer.Orders", "Order.Customer"), + Assert.Throws(() => modelBuilder.Validate()).Message); + } + + [Fact] + public virtual void Cannot_create_navigation_on_non_shadow_entity_targeting_shadow_entity() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var orderEntityType = modelBuilder.Entity(typeof(Order)); + + Assert.Equal( + CoreStrings.NavigationToShadowEntity("Customer", typeof(Order).DisplayName(fullName: false), "Customer"), + Assert.Throws(() => orderEntityType.HasOne("Customer", "Customer")).Message); + } + + [Fact] + public virtual void Cannot_create_shadow_navigation_between_non_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var orderEntityType = modelBuilder.Entity(typeof(Order)); + + Assert.Equal( + CoreStrings.NoClrNavigation("CustomerNavigation", typeof(Order).DisplayName(fullName: false)), + Assert.Throws(() => orderEntityType.HasOne(typeof(Customer), "CustomerNavigation")).Message); + } + protected override TestModelBuilder CreateTestModelBuilder(ModelBuilder modelBuilder) => new NonGenericStringTestModelBuilder(modelBuilder); } - public class NonGenericManyToOneType : ManyToOneTestBase + public class NonGenericStringManyToOneType : ManyToOneTestBase { + [Fact] + public virtual void Can_create_shadow_navigations_between_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var foreignKey = modelBuilder.Entity("Customer") + .HasMany("Order", "Orders") + .WithOne("Customer") + .Metadata; + + Assert.Equal("Customer", modelBuilder.Model.FindEntityType("Order")?.GetNavigations().Single().Name); + Assert.Equal("Orders", modelBuilder.Model.FindEntityType("Customer")?.GetNavigations().Single().Name); + Assert.False(foreignKey.IsUnique); + + // TODO: Issue#4016 - The Exception message here should be about shadow entity type instead of shadow key. + Assert.Equal( + CoreStrings.ReferencedShadowKey("{'TempId'}", "Customer.Orders", "Order.Customer"), + Assert.Throws(() => modelBuilder.Validate()).Message); + } + + [Fact] + public virtual void Cannot_create_navigation_on_non_shadow_entity_targeting_shadow_entity() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var customerEntityType = modelBuilder.Entity(typeof(Customer)); + + Assert.Equal( + CoreStrings.NavigationToShadowEntity("Orders", typeof(Customer).DisplayName(fullName: false), "Order"), + Assert.Throws(() => customerEntityType.HasMany("Order", "Orders")).Message); + } + + [Fact] + public virtual void Cannot_create_shadow_navigation_between_non_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var customerEntityType = modelBuilder.Entity(typeof(Customer)); + + Assert.Equal( + CoreStrings.NoClrNavigation("OrdersNavigation", typeof(Customer).DisplayName(fullName: false)), + Assert.Throws(() => customerEntityType.HasMany(typeof(Order), "OrdersNavigation")).Message); + } + protected override TestModelBuilder CreateTestModelBuilder(ModelBuilder modelBuilder) => new NonGenericStringTestModelBuilder(modelBuilder); } - public class NonGenericOneToOneType : OneToOneTestBase + public class NonGenericStringOneToOneType : OneToOneTestBase { + [Fact] + public virtual void Can_create_shadow_navigations_between_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var foreignKey = modelBuilder.Entity("Order") + .HasOne("OrderDetails", "OrderDetails") + .WithOne("Order") + .Metadata; + + Assert.Equal("OrderDetails", modelBuilder.Model.FindEntityType("Order")?.GetNavigations().Single().Name); + Assert.Equal("Order", modelBuilder.Model.FindEntityType("OrderDetails")?.GetNavigations().Single().Name); + Assert.True(foreignKey.IsUnique); + + // TODO: Issue#4016 - The Exception message here should be about shadow entity type instead of shadow key. + Assert.Equal( + CoreStrings.ReferencedShadowKey("{'TempId'}", "OrderDetails.Order", "Order.OrderDetails"), + Assert.Throws(() => modelBuilder.Validate()).Message); + } + + [Fact] + public virtual void Cannot_create_navigation_on_non_shadow_entity_targeting_shadow_entity() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var orderEntityType = modelBuilder.Entity(typeof(Order)); + + Assert.Equal( + CoreStrings.NavigationToShadowEntity("OrderDetails", typeof(Order).DisplayName(fullName: false), "OrderDetails"), + Assert.Throws(() => orderEntityType.HasOne("OrderDetails", "OrderDetails")).Message); + } + + [Fact] + public virtual void Cannot_create_shadow_navigation_between_non_shadow_entity_types() + { + var modelBuilder = (NonGenericStringTestModelBuilder)CreateModelBuilder(); + var orderEntityType = modelBuilder.Entity(typeof(Order)); + + Assert.Equal( + CoreStrings.NoClrNavigation("OrderDetailsNavigation", typeof(Order).DisplayName(fullName: false)), + Assert.Throws(() => orderEntityType.HasOne(typeof(OrderDetails), "OrderDetailsNavigation")).Message); + } + protected override TestModelBuilder CreateTestModelBuilder(ModelBuilder modelBuilder) => new NonGenericStringTestModelBuilder(modelBuilder); } @@ -40,6 +164,12 @@ public NonGenericStringTestModelBuilder(ModelBuilder modelBuilder) public override TestEntityTypeBuilder Entity() => new NonGenericStringTestEntityTypeBuilder(ModelBuilder.Entity(typeof(TEntity))); + public NonGenericStringTestEntityTypeBuilder Entity(Type type) + => new NonGenericStringTestEntityTypeBuilder(ModelBuilder.Entity(type)); + + public NonGenericStringTestEntityTypeBuilder Entity(string name) + => new NonGenericStringTestEntityTypeBuilder(ModelBuilder.Entity(name)); + public override TestModelBuilder Entity(Action> buildAction) => new NonGenericStringTestModelBuilder(ModelBuilder.Entity(typeof(TEntity), entityTypeBuilder => buildAction(new NonGenericStringTestEntityTypeBuilder(entityTypeBuilder)))); @@ -69,6 +199,28 @@ public override TestCollectionNavigationBuilder HasMany => new NonGenericTestCollectionNavigationBuilder(EntityTypeBuilder.HasMany(typeof(TRelatedEntity).FullName, collection?.GetPropertyAccess().Name)); } + private class NonGenericStringTestEntityTypeBuilder + { + public NonGenericStringTestEntityTypeBuilder(EntityTypeBuilder entityTypeBuilder) + { + EntityTypeBuilder = entityTypeBuilder; + } + + private EntityTypeBuilder EntityTypeBuilder { get; } + + public NonGenericStringTestReferenceNavigationBuilder HasOne(string relatedTypeName, string navigationName) + => new NonGenericStringTestReferenceNavigationBuilder(EntityTypeBuilder.HasOne(relatedTypeName, navigationName)); + + public NonGenericStringTestCollectionNavigationBuilder HasMany(string relatedTypeName, string navigationName) + => new NonGenericStringTestCollectionNavigationBuilder(EntityTypeBuilder.HasMany(relatedTypeName, navigationName)); + + public NonGenericStringTestReferenceNavigationBuilder HasOne(Type relatedType, string navigationName) + => new NonGenericStringTestReferenceNavigationBuilder(EntityTypeBuilder.HasOne(relatedType, navigationName)); + + public NonGenericStringTestCollectionNavigationBuilder HasMany(Type relatedType, string navigationName) + => new NonGenericStringTestCollectionNavigationBuilder(EntityTypeBuilder.HasMany(relatedType, navigationName)); + } + private class NonGenericStringTestReferenceNavigationBuilder : NonGenericTestReferenceNavigationBuilder where TEntity : class where TRelatedEntity : class @@ -85,6 +237,35 @@ public override TestReferenceReferenceBuilder WithOne(E } } + private class NonGenericStringTestReferenceNavigationBuilder + { + public NonGenericStringTestReferenceNavigationBuilder(ReferenceNavigationBuilder referenceNavigationBuilder) + { + ReferenceNavigationBuilder = referenceNavigationBuilder; + } + + private ReferenceNavigationBuilder ReferenceNavigationBuilder { get; } + + public NonGenericStringTestReferenceCollectionBuilder WithMany(string collection = null) + => new NonGenericStringTestReferenceCollectionBuilder(ReferenceNavigationBuilder.WithMany(collection)); + + public NonGenericStringTestReferenceReferenceBuilder WithOne(string reference = null) + => new NonGenericStringTestReferenceReferenceBuilder(ReferenceNavigationBuilder.WithOne(reference)); + } + + private class NonGenericStringTestCollectionNavigationBuilder + { + public NonGenericStringTestCollectionNavigationBuilder(CollectionNavigationBuilder collectionNavigationBuilder) + { + CollectionNavigationBuilder = collectionNavigationBuilder; + } + + private CollectionNavigationBuilder CollectionNavigationBuilder { get; } + + public NonGenericStringTestReferenceCollectionBuilder WithOne(string reference = null) + => new NonGenericStringTestReferenceCollectionBuilder(CollectionNavigationBuilder.WithOne(reference)); + } + private class NonGenericStringTestReferenceReferenceBuilder : NonGenericTestReferenceReferenceBuilder where TEntity : class where TRelatedEntity : class @@ -103,5 +284,27 @@ public override TestReferenceReferenceBuilder HasForeig public override TestReferenceReferenceBuilder HasPrincipalKey(Expression> keyExpression) => Wrap(ReferenceReferenceBuilder.HasPrincipalKey(typeof(TPrincipalEntity).FullName, keyExpression.GetPropertyAccessList().Select(p => p.Name).ToArray())); } + + private class NonGenericStringTestReferenceReferenceBuilder + { + public NonGenericStringTestReferenceReferenceBuilder(ReferenceReferenceBuilder referenceReferenceBuilder) + { + ReferenceReferenceBuilder = referenceReferenceBuilder; + } + + private ReferenceReferenceBuilder ReferenceReferenceBuilder { get; } + public IMutableForeignKey Metadata => ReferenceReferenceBuilder.Metadata; + } + + private class NonGenericStringTestReferenceCollectionBuilder + { + public NonGenericStringTestReferenceCollectionBuilder(ReferenceCollectionBuilder referenceCollectionBuilder) + { + ReferenceCollectionBuilder = referenceCollectionBuilder; + } + + private ReferenceCollectionBuilder ReferenceCollectionBuilder { get; } + public IMutableForeignKey Metadata => ReferenceCollectionBuilder.Metadata; + } } }