diff --git a/src/EntityFramework.Commands/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EntityFramework.Commands/Migrations/Design/CSharpSnapshotGenerator.cs index a9dbc21f459..e08b0b7ae0c 100644 --- a/src/EntityFramework.Commands/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EntityFramework.Commands/Migrations/Design/CSharpSnapshotGenerator.cs @@ -335,7 +335,14 @@ protected virtual void GenerateEntityTypeAnnotations([NotNull] IEntityType entit Check.NotNull(entityType, nameof(entityType)); Check.NotNull(stringBuilder, nameof(stringBuilder)); - var annotations = entityType.GetAnnotations().ToArray(); + var annotations = entityType.GetAnnotations().ToList(); + + var displayName = entityType.DisplayName(); + if (entityType.Name != displayName) + { + annotations.Add(new Annotation(CoreAnnotationNames.DisplayName, displayName)); + } + if (annotations.Any()) { foreach (var annotation in annotations) diff --git a/src/EntityFramework.Core/Metadata/Internal/CoreAnnotationNames.cs b/src/EntityFramework.Core/Metadata/Internal/CoreAnnotationNames.cs index 86edef248ba..4d28596e0f2 100644 --- a/src/EntityFramework.Core/Metadata/Internal/CoreAnnotationNames.cs +++ b/src/EntityFramework.Core/Metadata/Internal/CoreAnnotationNames.cs @@ -7,5 +7,6 @@ public static class CoreAnnotationNames { public const string MaxLengthAnnotation = "MaxLength"; public const string ProductVersionAnnotation = "ProductVersion"; + public const string DisplayName = "DisplayName"; } } diff --git a/src/EntityFramework.Core/Metadata/Internal/EntityType.cs b/src/EntityFramework.Core/Metadata/Internal/EntityType.cs index 9b274d780a3..3a3ac5f26dd 100644 --- a/src/EntityFramework.Core/Metadata/Internal/EntityType.cs +++ b/src/EntityFramework.Core/Metadata/Internal/EntityType.cs @@ -14,11 +14,11 @@ namespace Microsoft.Data.Entity.Metadata.Internal { - public class EntityType : - ConventionalAnnotatable, - IMutableEntityType, - ICanGetNavigations, - IPropertyCountsAccessor, + public class EntityType : + ConventionalAnnotatable, + IMutableEntityType, + ICanGetNavigations, + IPropertyCountsAccessor, ISnapshotFactorySource { private static readonly char[] _simpleNameChars = { '.', '+' }; @@ -76,7 +76,7 @@ public EntityType([NotNull] string name, [NotNull] Model model, ConfigurationSou _properties = new SortedDictionary(new PropertyComparer(this)); #if DEBUG - DebugName = DisplayName(); + DebugName = EntityTypeExtensions.DisplayName(this); #endif } @@ -113,7 +113,7 @@ public virtual Type ClrType || GetDirectlyDerivedTypes().Any() || GetProperties().Any()) { - throw new InvalidOperationException(CoreStrings.EntityTypeInUse(DisplayName())); + throw new InvalidOperationException(CoreStrings.EntityTypeInUse(EntityTypeExtensions.DisplayName(this))); } _typeOrName = value; @@ -253,23 +253,6 @@ public virtual string Name } } - public virtual string DisplayName() - { - if (ClrType != null) - { - return ClrType.DisplayName(false) ?? ParseSimpleName(); - } - return ParseSimpleName(); - } - - private string ParseSimpleName() - { - var fullName = (string)_typeOrName; - var lastDot = fullName.LastIndexOfAny(_simpleNameChars); - - return lastDot > 0 ? fullName.Substring(lastDot + 1) : fullName; - } - public override string ToString() => Name; public virtual ConfigurationSource GetConfigurationSource() => _configurationSource; @@ -304,7 +287,8 @@ public virtual Key SetPrimaryKey( { if (_baseType != null) { - throw new InvalidOperationException(CoreStrings.DerivedEntityTypeKey(DisplayName(), _baseType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.DerivedEntityTypeKey(EntityTypeExtensions.DisplayName(this), _baseType.DisplayName())); } if (_primaryKey != null) @@ -394,21 +378,29 @@ public virtual Key AddKey([NotNull] IReadOnlyList properties, if (_baseType != null) { - throw new InvalidOperationException(CoreStrings.DerivedEntityTypeKey(DisplayName(), _baseType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.DerivedEntityTypeKey(EntityTypeExtensions.DisplayName(this), _baseType.DisplayName())); } foreach (var property in properties) { if (FindProperty(property.Name) != property) { - throw new ArgumentException(CoreStrings.KeyPropertiesWrongEntity(Property.Format(properties), DisplayName())); + throw new ArgumentException( + CoreStrings.KeyPropertiesWrongEntity( + Property.Format(properties), + EntityTypeExtensions.DisplayName(this))); } } var key = FindKey(properties); if (key != null) { - throw new InvalidOperationException(CoreStrings.DuplicateKey(Property.Format(properties), DisplayName(), key.DeclaringEntityType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.DuplicateKey( + Property.Format(properties), + EntityTypeExtensions.DisplayName(this), + key.DeclaringEntityType.DisplayName())); } key = new Key(properties, configurationSource); @@ -522,7 +514,11 @@ public virtual ForeignKey AddForeignKey( var duplicateForeignKey = FindForeignKeysInHierarchy(properties, principalKey, principalEntityType).FirstOrDefault(); if (duplicateForeignKey != null) { - throw new InvalidOperationException(CoreStrings.DuplicateForeignKey(Property.Format(properties), DisplayName(), duplicateForeignKey.DeclaringEntityType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.DuplicateForeignKey( + Property.Format(properties), + EntityTypeExtensions.DisplayName(this), + duplicateForeignKey.DeclaringEntityType.DisplayName())); } var foreignKey = new ForeignKey(properties, principalKey, this, principalEntityType, configurationSource ?? ConfigurationSource.Convention); @@ -641,7 +637,7 @@ private ForeignKey RemoveForeignKey([NotNull] ForeignKey foreignKey) { foreignKey.DeclaringEntityType.RemoveNavigation(foreignKey.DependentToPrincipal.Name); } - + if (foreignKey.PrincipalToDependent != null) { foreignKey.PrincipalEntityType.RemoveNavigation(foreignKey.PrincipalToDependent.Name); @@ -690,14 +686,20 @@ public virtual Navigation AddNavigation( } throw new InvalidOperationException( - CoreStrings.DuplicateNavigation(name, DisplayName(), duplicateNavigation.DeclaringEntityType.DisplayName())); + CoreStrings.DuplicateNavigation( + name, + EntityTypeExtensions.DisplayName(this), + duplicateNavigation.DeclaringEntityType.DisplayName())); } var duplicateProperty = FindPropertiesInHierarchy(name).FirstOrDefault(); if (duplicateProperty != null) { - throw new InvalidOperationException(CoreStrings.ConflictingProperty(name, DisplayName(), - duplicateProperty.DeclaringEntityType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.ConflictingProperty( + name, + EntityTypeExtensions.DisplayName(this), + duplicateProperty.DeclaringEntityType.DisplayName())); } Debug.Assert(!GetNavigations().Any(n => (n.ForeignKey == foreignKey) && (n.IsDependentToPrincipal() == pointsToPrincipal)), @@ -788,14 +790,21 @@ public virtual Index AddIndex([NotNull] IReadOnlyList properties, { if (FindProperty(property.Name) != property) { - throw new ArgumentException(CoreStrings.IndexPropertiesWrongEntity(Property.Format(properties), DisplayName())); + throw new ArgumentException( + CoreStrings.IndexPropertiesWrongEntity( + Property.Format(properties), + EntityTypeExtensions.DisplayName(this))); } } var duplicateIndex = FindIndexesInHierarchy(properties).FirstOrDefault(); if (duplicateIndex != null) { - throw new InvalidOperationException(CoreStrings.DuplicateIndex(Property.Format(properties), DisplayName(), duplicateIndex.DeclaringEntityType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.DuplicateIndex( + Property.Format(properties), + EntityTypeExtensions.DisplayName(this), + duplicateIndex.DeclaringEntityType.DisplayName())); } var index = new Index(properties, this, configurationSource); @@ -877,14 +886,17 @@ public virtual Property AddProperty([NotNull] string name, if (duplicateProperty != null) { throw new InvalidOperationException(CoreStrings.DuplicateProperty( - name, DisplayName(), duplicateProperty.DeclaringEntityType.DisplayName())); + name, EntityTypeExtensions.DisplayName(this), duplicateProperty.DeclaringEntityType.DisplayName())); } var duplicateNavigation = FindNavigationsInHierarchy(name).FirstOrDefault(); if (duplicateNavigation != null) { - throw new InvalidOperationException(CoreStrings.ConflictingNavigation(name, DisplayName(), - duplicateNavigation.DeclaringEntityType.DisplayName())); + throw new InvalidOperationException( + CoreStrings.ConflictingNavigation( + name, + EntityTypeExtensions.DisplayName(this), + duplicateNavigation.DeclaringEntityType.DisplayName())); } var property = new Property(name, this, configurationSource); @@ -998,7 +1010,7 @@ public virtual void PropertyMetadataChanged() public virtual Func RelationshipSnapshotFactory => LazyInitializer.EnsureInitialized(ref _relationshipSnapshotFactory, CreateRelationshipSnapshotFactory); - private Func CreateRelationshipSnapshotFactory() + private Func CreateRelationshipSnapshotFactory() => new RelationshipSnapshotFactoryFactory().Create(this); public virtual Func OriginalValuesFactory diff --git a/src/EntityFramework.Core/Metadata/Internal/EntityTypeExtensions.cs b/src/EntityFramework.Core/Metadata/Internal/EntityTypeExtensions.cs index b3f9092e019..ba5c491f45e 100644 --- a/src/EntityFramework.Core/Metadata/Internal/EntityTypeExtensions.cs +++ b/src/EntityFramework.Core/Metadata/Internal/EntityTypeExtensions.cs @@ -16,18 +16,10 @@ namespace Microsoft.Data.Entity.Metadata.Internal public static class EntityTypeExtensions { public static string DisplayName([NotNull] this IEntityType entityType) - { - Check.NotNull(entityType, nameof(entityType)); - - if (entityType.ClrType != null) - { - return entityType.ClrType.DisplayName(false); - } - - var lastDot = entityType.Name.LastIndexOfAny(new[] { '.', '+' }); - - return lastDot > 0 ? entityType.Name.Substring(lastDot + 1) : entityType.Name; - } + => entityType.ClrType != null + ? entityType.ClrType.DisplayName(fullName: false) + : (entityType[CoreAnnotationNames.DisplayName] as string + ?? entityType.Name); public static IEnumerable GetAllBaseTypesInclusive([NotNull] this IEntityType entityType) { diff --git a/test/EntityFramework.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs b/test/EntityFramework.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs index ae516ac6f43..bb1718fb202 100644 --- a/test/EntityFramework.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs +++ b/test/EntityFramework.Commands.FunctionalTests/Migrations/ModelSnapshotTest.cs @@ -38,6 +38,17 @@ public class EntityWithStringKey public string Id { get; set; } } + public class EntityWithGenericKey + { + public Guid Id { get; set; } + } + + public class EntityWithGenericProperty + { + public int Id { get; set; } + public TProperty Property { get; set; } + } + public class BaseEntity { public int Id { get; set; } @@ -86,6 +97,8 @@ public void Entities_are_stored_in_model_snapshot() .ValueGeneratedOnAdd(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -96,6 +109,8 @@ public void Entities_are_stored_in_model_snapshot() b.Property(""AlternateId""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -126,11 +141,13 @@ public void EntityType_annotations_are_stored_in_snapshot() b.HasKey(""Id""); b.HasAnnotation(""AnnotationName"", ""AnnotationValue""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); ", o => { - Assert.Equal(1, o.GetEntityTypes().First().GetAnnotations().Count()); + Assert.Equal(2, o.GetEntityTypes().First().GetAnnotations().Count()); Assert.Equal("AnnotationValue", o.GetEntityTypes().First()["AnnotationName"]); }); } @@ -151,6 +168,8 @@ public void BaseType_is_stored_in_snapshot() .ValueGeneratedOnAdd(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""BaseEntity""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+AnotherDerivedEntity"", b => @@ -158,6 +177,8 @@ public void BaseType_is_stored_in_snapshot() b.HasBaseType(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+BaseEntity""); b.Property(""Title""); + + b.HasAnnotation(""DisplayName"", ""AnotherDerivedEntity""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+DerivedEntity"", b => @@ -165,6 +186,8 @@ public void BaseType_is_stored_in_snapshot() b.HasBaseType(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+BaseEntity""); b.Property(""Name""); + + b.HasAnnotation(""DisplayName"", ""DerivedEntity""); }); ", o => @@ -193,6 +216,8 @@ public void Properties_are_stored_in_snapshot() b.Property(""AlternateId""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -219,6 +244,8 @@ public void Primary_key_is_stored_in_snapshot() b.Property(""AlternateId""); b.HasKey(""Id"", ""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -248,6 +275,8 @@ public void Alternate_keys_are_stored_in_snapshot() b.HasKey(""Id""); b.HasAlternateKey(""Id"", ""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -276,6 +305,8 @@ public void Indexes_are_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -301,6 +332,8 @@ public void Indexes_are_stored_in_snapshot_including_composite_index() b.HasKey(""Id""); b.HasIndex(""Id"", ""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => @@ -325,6 +358,8 @@ public void Foreign_keys_are_stored_in_snapshot() .ValueGeneratedOnAdd(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -337,6 +372,8 @@ public void Foreign_keys_are_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -371,6 +408,8 @@ public void Relationship_principal_key_is_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -381,6 +420,8 @@ public void Relationship_principal_key_is_stored_in_snapshot() b.Property(""AlternateId""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => @@ -398,6 +439,115 @@ public void Relationship_principal_key_is_stored_in_snapshot() }); } + [Fact] + public void TableName_preserved_when_generic() + { + IModel originalModel = null; + + Test( + builder => + { + builder.Entity>(); + + originalModel = builder.Model; + }, + @" +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericKey"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd(); + + b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithGenericKey""); + }); +", + model => + { + var originalEntity = originalModel.FindEntityType(typeof(EntityWithGenericKey)); + var entity = model.FindEntityType(originalEntity.Name); + + Assert.NotNull(entity); + Assert.Equal(originalEntity.SqlServer().TableName, entity.SqlServer().TableName); + }); + } + + [Fact] + public void PrimaryKey_name_preserved_when_generic() + { + IModel originalModel = null; + + Test( + builder => + { + builder.Entity>(); + + originalModel = builder.Model; + }, + @" +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericKey"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd(); + + b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithGenericKey""); + }); +", + model => + { + var originalEntity = originalModel.FindEntityType(typeof(EntityWithGenericKey)); + var entity = model.FindEntityType(originalEntity.Name); + Assert.NotNull(entity); + + var originalPrimaryKey = originalEntity.FindPrimaryKey(); + var primaryKey = entity.FindPrimaryKey(); + + Assert.Equal(originalPrimaryKey.SqlServer().Name, primaryKey.SqlServer().Name); + }); + } + + [Fact] + public void AlternateKey_name_preserved_when_generic() + { + IModel originalModel = null; + + Test( + builder => + { + builder.Entity>().HasAlternateKey(e => e.Property); + + originalModel = builder.Model; + }, + @" +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericProperty"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd(); + + b.Property(""Property""); + + b.HasKey(""Id""); + + b.HasAlternateKey(""Property""); + + b.HasAnnotation(""DisplayName"", ""EntityWithGenericProperty""); + }); +", + model => + { + var originalEntity = originalModel.FindEntityType(typeof(EntityWithGenericProperty)); + var entity = model.FindEntityType(originalEntity.Name); + Assert.NotNull(entity); + + var originalAlternateKey = originalEntity.FindKey(originalEntity.FindProperty("Property")); + var alternateKey = entity.FindKey(entity.FindProperty("Property")); + + Assert.Equal(originalAlternateKey.SqlServer().Name, alternateKey.SqlServer().Name); + }); + } + #endregion #region Property @@ -415,6 +565,8 @@ public void Property_annotations_are_stored_in_snapshot() .HasAnnotation(""AnnotationName"", ""AnnotationValue""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); ", o => { Assert.Equal("AnnotationValue", o.GetEntityTypes().First().FindProperty("Id")["AnnotationName"]); } @@ -436,6 +588,8 @@ public void Property_isNullable_is_stored_in_snapshot() .IsRequired(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringProperty""); }); ", o => { Assert.Equal(false, o.GetEntityTypes().First().FindProperty("Name").IsNullable); }); @@ -456,6 +610,8 @@ public void Property_ValueGenerated_value_is_stored_in_snapshot() .ValueGeneratedOnAdd(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => { Assert.Equal(ValueGenerated.OnAdd, o.GetEntityTypes().First().FindProperty("AlternateId").ValueGenerated); }); @@ -476,6 +632,8 @@ public void Property_maxLength_is_stored_in_snapshot() .HasAnnotation(""MaxLength"", 100); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringProperty""); }); ", o => { Assert.Equal(100, o.GetEntityTypes().First().FindProperty("Name").GetMaxLength()); }); @@ -495,6 +653,8 @@ public void Property_RequiresValueGenerator_is_not_stored_in_snapshot() b.Property(""AlternateId""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => { Assert.Equal(false, o.GetEntityTypes().First().FindProperty("AlternateId").RequiresValueGenerator); }); @@ -515,6 +675,8 @@ public void Property_concurrencyToken_is_stored_in_snapshot() .IsConcurrencyToken(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => { Assert.Equal(true, o.GetEntityTypes().First().FindProperty("AlternateId").IsConcurrencyToken); }); @@ -541,6 +703,8 @@ public void Index_annotations_are_stored_in_snapshot() b.HasIndex(""AlternateId"") .HasAnnotation(""AnnotationName"", ""AnnotationValue""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => { Assert.Equal("AnnotationValue", o.GetEntityTypes().First().GetIndexes().First()["AnnotationName"]); }); @@ -563,6 +727,8 @@ public void Index_isUnique_is_stored_in_snapshot() b.HasIndex(""AlternateId"") .IsUnique(); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); ", o => { Assert.Equal(true, o.GetEntityTypes().First().GetIndexes().First().IsUnique); }); @@ -591,6 +757,8 @@ public void ForeignKey_annotations_are_stored_in_snapshot() .ValueGeneratedOnAdd(); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -603,6 +771,8 @@ public void ForeignKey_annotations_are_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""AlternateId""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -634,6 +804,8 @@ public void ForeignKey_isRequired_is_stored_in_snapshot() b.Property(""Id""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringKey""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithStringProperty"", b => @@ -647,6 +819,8 @@ public void ForeignKey_isRequired_is_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""Name""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithStringProperty"", b => @@ -676,6 +850,8 @@ public void ForeignKey_isUnique_is_stored_in_snapshot() b.Property(""Id""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringKey""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithStringProperty"", b => @@ -688,6 +864,8 @@ public void ForeignKey_isUnique_is_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""Name""); + + b.HasAnnotation(""DisplayName"", ""EntityWithStringProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithStringProperty"", b => @@ -719,6 +897,8 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot() b.HasKey(""Id""); b.HasIndex(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithOneProperty""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithTwoProperties"", b => @@ -729,6 +909,8 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot() b.Property(""AlternateId""); b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithTwoProperties""); }); builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithOneProperty"", b => @@ -742,6 +924,80 @@ public void ForeignKey_deleteBehavior_is_stored_in_snapshot() o => { Assert.Equal(DeleteBehavior.Cascade, o.FindEntityType(typeof(EntityWithOneProperty)).GetForeignKeys().First().DeleteBehavior); }); } + [Fact] + public void ForeignKey_name_preserved_when_generic() + { + IModel originalModel = null; + + Test( + builder => + { + builder.Entity>().HasMany>().WithOne() + .HasForeignKey(e => e.Property); + + originalModel = builder.Model; + }, + @" +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericKey"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd(); + + b.HasKey(""Id""); + + b.HasAnnotation(""DisplayName"", ""EntityWithGenericKey""); + }); + +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericProperty"", b => + { + b.Property(""Id"") + .ValueGeneratedOnAdd(); + + b.Property(""Property""); + + b.HasKey(""Id""); + + b.HasIndex(""Property""); + + b.HasAnnotation(""DisplayName"", ""EntityWithGenericProperty""); + }); + +builder.Entity(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericProperty"", b => + { + b.HasOne(""Microsoft.Data.Entity.Commands.Migrations.ModelSnapshotTest+EntityWithGenericKey"") + .WithMany() + .HasForeignKey(""Property"") + .OnDelete(DeleteBehavior.Cascade); + }); +", + model => + { + var originalParent = originalModel.FindEntityType(typeof(EntityWithGenericKey)); + var parent = model.FindEntityType(originalParent.Name); + Assert.NotNull(parent); + + var originalChild = originalModel.FindEntityType(typeof(EntityWithGenericProperty)); + var child = model.FindEntityType(originalChild.Name); + Assert.NotNull(child); + + var originalForeignKey = originalChild.FindForeignKey( + originalChild.FindProperty("Property"), + originalParent.FindPrimaryKey(), + originalParent); + var foreignKey = child.FindForeignKey( + child.FindProperty("Property"), + parent.FindPrimaryKey(), + parent); + + Assert.Equal(originalForeignKey.SqlServer().Name, foreignKey.SqlServer().Name); + + var originalIndex = originalChild.FindIndex(originalChild.FindProperty("Property")); + var index = child.FindIndex(child.FindProperty("Property")); + + Assert.Equal(originalIndex.SqlServer().Name, index.SqlServer().Name); + }); + } + #endregion private void Test(Action buildModel, string expectedCode, Action assert) @@ -771,6 +1027,7 @@ private void Test(Action buildModel, string expectedCode, Action(() => entry.PrepareToSave()).Message); entry[nonKeyProperty] = null; @@ -135,7 +135,7 @@ public virtual void Read_only_before_save_properties_throw_if_not_null_or_temp() entry[keyProperty] = 2; Assert.Equal( - CoreStrings.PropertyReadOnlyBeforeSave("Id", typeof(SomeEntity).Name), + CoreStrings.PropertyReadOnlyBeforeSave("Id", entityType.DisplayName()), Assert.Throws(() => entry.PrepareToSave()).Message); } @@ -166,7 +166,7 @@ public virtual void Read_only_after_save_properties_throw_if_modified() Assert.True(entry.IsModified(nonKeyProperty)); Assert.Equal( - CoreStrings.PropertyReadOnlyAfterSave("Name", typeof(SomeEntity).Name), + CoreStrings.PropertyReadOnlyAfterSave("Name", entityType.DisplayName()), Assert.Throws(() => entry.PrepareToSave()).Message); entry.SetPropertyModified(nonKeyProperty, isModified: false); @@ -176,7 +176,7 @@ public virtual void Read_only_after_save_properties_throw_if_modified() Assert.True(entry.IsModified(nonKeyProperty)); Assert.Equal( - CoreStrings.PropertyReadOnlyAfterSave("Name", typeof(SomeEntity).Name), + CoreStrings.PropertyReadOnlyAfterSave("Name", entityType.DisplayName()), Assert.Throws(() => entry.PrepareToSave()).Message); } @@ -201,14 +201,14 @@ public virtual void Key_properties_throw_immediately_if_modified() Assert.False(entry.IsModified(keyProperty)); Assert.Equal( - CoreStrings.KeyReadOnly("Id", typeof(SomeEntity).Name), + CoreStrings.KeyReadOnly("Id", entityType.DisplayName()), Assert.Throws(() => entry.SetPropertyModified(keyProperty)).Message); Assert.Equal(EntityState.Unchanged, entry.EntityState); Assert.False(entry.IsModified(keyProperty)); Assert.Equal( - CoreStrings.KeyReadOnly("Id", typeof(SomeEntity).Name), + CoreStrings.KeyReadOnly("Id", entityType.DisplayName()), Assert.Throws(() => entry[keyProperty] = 2).Message); Assert.Equal(EntityState.Unchanged, entry.EntityState); @@ -296,7 +296,7 @@ public virtual void Changing_state_with_temp_value_throws(EntityState targetStat entry.MarkAsTemporary(keyProperty); Assert.Equal( - CoreStrings.TempValuePersists("Id", "SomeEntity", targetState.ToString()), + CoreStrings.TempValuePersists("Id", entityType.DisplayName(), targetState.ToString()), Assert.Throws(() => entry.SetEntityState(targetState)).Message); } @@ -1178,7 +1178,9 @@ public void Unchanged_entity_with_conceptually_null_FK_without_cascade_delete_th entry[fkProperty] = null; Assert.Equal( - CoreStrings.RelationshipConceptualNull("SomeEntity", "SomeDependentEntity"), + CoreStrings.RelationshipConceptualNull( + model.FindEntityType(typeof(SomeEntity).FullName).DisplayName(), + entityType.DisplayName()), Assert.Throws(() => entry.HandleConceptualNulls()).Message); } @@ -1200,7 +1202,7 @@ public void Unchanged_entity_with_conceptually_null_non_FK_property_throws() entry[property] = null; Assert.Equal( - CoreStrings.PropertyConceptualNull("JustAProperty", "SomeDependentEntity"), + CoreStrings.PropertyConceptualNull("JustAProperty", entityType.DisplayName()), Assert.Throws(() => entry.HandleConceptualNulls()).Message); } diff --git a/test/EntityFramework.Core.Tests/Metadata/Internal/EntityTypeTest.cs b/test/EntityFramework.Core.Tests/Metadata/Internal/EntityTypeTest.cs index 2c7019a1e2e..70e9d65ba90 100644 --- a/test/EntityFramework.Core.Tests/Metadata/Internal/EntityTypeTest.cs +++ b/test/EntityFramework.Core.Tests/Metadata/Internal/EntityTypeTest.cs @@ -1183,13 +1183,10 @@ public void Display_name_is_prettified_CLR_name() } [Fact] - public void Display_name_is_part_of_name_following_final_separator_when_no_CLR_type() - { - Assert.Equal("Everything", new Model().AddEntityType("Everything").DisplayName()); - Assert.Equal("Is", new Model().AddEntityType("Everything.Is").DisplayName()); - Assert.Equal("Awesome", new Model().AddEntityType("Everything.Is.Awesome").DisplayName()); - Assert.Equal("WhenWe`reLivingOurDream", new Model().AddEntityType("Everything.Is.Awesome+WhenWe`reLivingOurDream").DisplayName()); - } + public void Display_name_is_entity_type_name_when_no_CLR_type() + => Assert.Equal( + "Everything.Is+Awesome>", + new Model().AddEntityType("Everything.Is+Awesome>").DisplayName()); [Fact] public void Name_is_prettified_CLR_full_name() diff --git a/test/EntityFramework.Relational.Tests/RelationalModelValidatorTest.cs b/test/EntityFramework.Relational.Tests/RelationalModelValidatorTest.cs index 28cea16e9fd..7ed1b60e301 100644 --- a/test/EntityFramework.Relational.Tests/RelationalModelValidatorTest.cs +++ b/test/EntityFramework.Relational.Tests/RelationalModelValidatorTest.cs @@ -3,6 +3,7 @@ using Microsoft.Data.Entity.Internal; using Microsoft.Data.Entity.Metadata.Conventions.Internal; +using Microsoft.Data.Entity.Metadata.Internal; using Microsoft.Data.Entity.Tests.Infrastructure; using Microsoft.Data.Entity.Tests.TestUtilities; using Microsoft.Extensions.Logging;