diff --git a/src/EFCore.Design/Extensions/ScaffoldingModelExtensions.cs b/src/EFCore.Design/Extensions/ScaffoldingModelExtensions.cs index 1fe38f60993..86a430dc6b8 100644 --- a/src/EFCore.Design/Extensions/ScaffoldingModelExtensions.cs +++ b/src/EFCore.Design/Extensions/ScaffoldingModelExtensions.cs @@ -10,15 +10,15 @@ namespace Microsoft.EntityFrameworkCore; /// -/// Design-time model extensions. +/// Design-time model extensions. /// public static class ScaffoldingModelExtensions { /// - /// Check whether an entity type could be considered a many-to-many join entity type. + /// Check whether an entity type could be considered a many-to-many join entity type. /// /// The entity type to check. - /// if the entity type could be considered a join entity type. + /// if the entity type could be considered a join entity type. public static bool IsSimpleManyToManyJoinEntityType(this IEntityType entityType) { if (!entityType.GetNavigations().Any() @@ -46,18 +46,21 @@ public static bool IsSimpleManyToManyJoinEntityType(this IEntityType entityType) } /// - /// Gets a value indicating whether the specified skip navigation represents the left side of the relationship. + /// Gets a value indicating whether the specified skip navigation represents the left side of the relationship. /// /// The skip navigation to check. - /// if it represents the left side. + /// if it represents the left side. /// - /// The designation of left and right is arbitrary but deterministic. This method exists primarily to avoid configuring the same many-to-many relationship from both of its ends. + /// The designation of left and right is arbitrary but deterministic. This method exists primarily to avoid configuring the same + /// many-to-many relationship from both of its ends. /// public static bool IsLeftNavigation(this ISkipNavigation skipNavigation) - => skipNavigation.JoinEntityType.FindPrimaryKey()!.Properties[0].GetContainingForeignKeys().Single().PrincipalEntityType == skipNavigation.DeclaringEntityType; + => skipNavigation.JoinEntityType.FindPrimaryKey()!.Properties[0].GetContainingForeignKeys().Single().PrincipalEntityType + == skipNavigation.DeclaringEntityType; /// - /// Gets the name that should be used for the property on the class for this entity type. + /// Gets the name that should be used for the property on the class for this entity + /// type. /// /// The entity type. /// The property name. @@ -66,10 +69,10 @@ public static string GetDbSetName(this IReadOnlyEntityType entityType) ?? entityType.ShortName(); /// - /// Gets a value indicating whether the key would be configured by conventions. + /// Gets a value indicating whether the key would be configured by conventions. /// /// The key to check. - /// if the key would be configured by conventions. + /// if the key would be configured by conventions. public static bool IsHandledByConventions(this IKey key) => key is IConventionKey conventionKey && conventionKey.Properties.SequenceEqual( @@ -78,11 +81,11 @@ public static bool IsHandledByConventions(this IKey key) conventionKey.DeclaringEntityType.GetProperties())); /// - /// Gets value indicating whether this index can be entirely reperesented by a data annotation. + /// Gets value indicating whether this index can be entirely reperesented by a data annotation. /// /// The index. /// The provider's annotation code generator. - /// if this index can be reperesented by a data annotation. + /// if this index can be reperesented by a data annotation. public static bool HasDataAnnotation(this IIndex index, IAnnotationCodeGenerator annotationCodeGenerator) { var indexAnnotations = annotationCodeGenerator.FilterIgnoredAnnotations(index.GetAnnotations()) @@ -94,17 +97,26 @@ public static bool HasDataAnnotation(this IIndex index, IAnnotationCodeGenerator } /// - /// Gets the data annotations to configure an entity type. + /// Gets the data annotations to configure an entity type. /// /// The entity type. /// The provider's annotation code generator. /// The data annotations. - public static IEnumerable GetDataAnnotations(this IEntityType entityType, IAnnotationCodeGenerator annotationCodeGenerator) + public static IEnumerable GetDataAnnotations( + this IEntityType entityType, + IAnnotationCodeGenerator annotationCodeGenerator) { - if (entityType.FindPrimaryKey() == null) + var primaryKey = entityType.FindPrimaryKey(); + if (primaryKey == null) { yield return new AttributeCodeFragment(typeof(KeylessAttribute)); } + else if (primaryKey.Properties.Count > 1) + { + yield return new AttributeCodeFragment( + typeof(PrimaryKeyAttribute), + primaryKey.Properties.Select(p => p.Name).Cast().ToArray()); + } var tableName = entityType.GetTableName(); var schema = entityType.GetSchema(); @@ -118,14 +130,15 @@ public static IEnumerable GetDataAnnotations(this IEntity if (needsSchema) { tableNamedArgs.Add(nameof(TableAttribute.Schema), schema); - }; + } yield return new AttributeCodeFragment(typeof(TableAttribute), new object?[] { tableName }, tableNamedArgs); } foreach (var index in entityType.GetIndexes() - .Where(i => ((IConventionIndex)i).GetConfigurationSource() != ConfigurationSource.Convention - && i.HasDataAnnotation(annotationCodeGenerator))) + .Where( + i => ((IConventionIndex)i).GetConfigurationSource() != ConfigurationSource.Convention + && i.HasDataAnnotation(annotationCodeGenerator))) { var indexArgs = new List(); var indexNamedArgs = new Dictionary(); @@ -167,12 +180,14 @@ public static IEnumerable GetDataAnnotations(this IEntity } /// - /// Gets the data annotations to configure a property. + /// Gets the data annotations to configure a property. /// /// The property. /// The provider's annotation code generator. /// The data annotations. - public static IEnumerable GetDataAnnotations(this IProperty property, IAnnotationCodeGenerator annotationCodeGenerator) + public static IEnumerable GetDataAnnotations( + this IProperty property, + IAnnotationCodeGenerator annotationCodeGenerator) { if (property.FindContainingPrimaryKey() != null) { @@ -263,12 +278,14 @@ public static IEnumerable GetDataAnnotations(this IProper } /// - /// Gets the data annotations to configure a navigation property. + /// Gets the data annotations to configure a navigation property. /// /// The navigation property. /// The provider's annotation code generator. /// The data annotations. - public static IEnumerable GetDataAnnotations(this INavigation navigation, IAnnotationCodeGenerator annotationCodeGenerator) + public static IEnumerable GetDataAnnotations( + this INavigation navigation, + IAnnotationCodeGenerator annotationCodeGenerator) { if (navigation.IsOnDependent && navigation.ForeignKey.PrincipalKey.IsPrimaryKey()) @@ -286,12 +303,14 @@ public static IEnumerable GetDataAnnotations(this INaviga } /// - /// Gets the data annotations to configure a skip navigation property. + /// Gets the data annotations to configure a skip navigation property. /// /// The skip navigation property. /// The provider's annotation code generator. /// The data annotations. - public static IEnumerable GetDataAnnotations(this ISkipNavigation skipNavigation, IAnnotationCodeGenerator annotationCodeGenerator) + public static IEnumerable GetDataAnnotations( + this ISkipNavigation skipNavigation, + IAnnotationCodeGenerator annotationCodeGenerator) { if (skipNavigation.ForeignKey!.PrincipalKey.IsPrimaryKey()) { @@ -304,7 +323,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure a model. + /// Gets the fluent API calls to configure a model. /// /// The model. /// The provider's annotation code generator. @@ -331,13 +350,14 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure an entity type. + /// Gets the fluent API calls to configure an entity type. /// /// The entity type. /// The provider's annotation code generator. /// The fluent API calls. public static FluentApiCodeFragment? GetFluentApiCalls( - this IEntityType entityType, IAnnotationCodeGenerator annotationCodeGenerator) + this IEntityType entityType, + IAnnotationCodeGenerator annotationCodeGenerator) { FluentApiCodeFragment? root = null; @@ -365,10 +385,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa if (entityType.FindPrimaryKey() is null) { - var hasNoKey = new FluentApiCodeFragment(nameof(EntityTypeBuilder.HasNoKey)) - { - HasDataAnnotation = true - }; + var hasNoKey = new FluentApiCodeFragment(nameof(EntityTypeBuilder.HasNoKey)) { HasDataAnnotation = true }; root = root?.Chain(hasNoKey) ?? hasNoKey; } @@ -424,10 +441,10 @@ public static IEnumerable GetDataAnnotations(this ISkipNa { toTableArguments.Add(new NestedClosureCodeFragment("tb", toTableNestedCalls)); } + var toTable = new FluentApiCodeFragment(nameof(RelationalEntityTypeBuilderExtensions.ToTable)) { - Arguments = toTableArguments, - HasDataAnnotation = toTableHandledByDataAnnotations + Arguments = toTableArguments, HasDataAnnotation = toTableHandledByDataAnnotations }; root = root?.Chain(toTable) ?? toTable; @@ -440,10 +457,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa if (explicitViewSchema || viewName != null) { - var toView = new FluentApiCodeFragment(nameof(RelationalEntityTypeBuilderExtensions.ToView)) - { - Arguments = { viewName } - }; + var toView = new FluentApiCodeFragment(nameof(RelationalEntityTypeBuilderExtensions.ToView)) { Arguments = { viewName } }; if (explicitViewSchema) { @@ -459,7 +473,8 @@ public static IEnumerable GetDataAnnotations(this ISkipNa root = root?.Chain(annotationsRoot) ?? annotationsRoot; } - annotationsRoot = GenerateAnnotations(entityType, annotationsHandledByDataAnnotations, annotationCodeGenerator, hasDataAnnotation: true); + annotationsRoot = GenerateAnnotations( + entityType, annotationsHandledByDataAnnotations, annotationCodeGenerator, hasDataAnnotation: true); if (annotationsRoot is not null) { root = root?.Chain(annotationsRoot) ?? annotationsRoot; @@ -469,7 +484,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure a key. + /// Gets the fluent API calls to configure a key. /// /// The key. /// The provider's annotation code generator. @@ -492,7 +507,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure an index. + /// Gets the fluent API calls to configure an index. /// /// The index. /// The provider's annotation code generator. @@ -534,7 +549,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure a property. + /// Gets the fluent API calls to configure a property. /// /// The property. /// The provider's annotation code generator. @@ -566,10 +581,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa && property.ClrType.IsNullableType() && !property.IsPrimaryKey()) { - var isRequired = new FluentApiCodeFragment(nameof(PropertyBuilder.IsRequired)) - { - HasDataAnnotation = true - }; + var isRequired = new FluentApiCodeFragment(nameof(PropertyBuilder.IsRequired)) { HasDataAnnotation = true }; root = root?.Chain(isRequired) ?? isRequired; } @@ -579,8 +591,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa { var hasMaxLength = new FluentApiCodeFragment(nameof(PropertyBuilder.HasMaxLength)) { - Arguments = { maxLength.Value }, - HasDataAnnotation = true + Arguments = { maxLength.Value }, HasDataAnnotation = true }; root = root?.Chain(hasMaxLength) ?? hasMaxLength; @@ -592,12 +603,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa { var hasPrecision = new FluentApiCodeFragment(nameof(PropertyBuilder.HasPrecision)) { - Arguments = - { - precision.Value, - scale.Value - }, - HasDataAnnotation = true + Arguments = { precision.Value, scale.Value }, HasDataAnnotation = true }; root = root?.Chain(hasPrecision) ?? hasPrecision; @@ -606,8 +612,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa { var hasPrecision = new FluentApiCodeFragment(nameof(PropertyBuilder.HasPrecision)) { - Arguments = { precision.Value }, - HasDataAnnotation = true + Arguments = { precision.Value }, HasDataAnnotation = true }; root = root?.Chain(hasPrecision) ?? hasPrecision; @@ -615,10 +620,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa if (property.IsUnicode() != null) { - var isUnicode = new FluentApiCodeFragment(nameof(PropertyBuilder.IsUnicode)) - { - HasDataAnnotation = true - }; + var isUnicode = new FluentApiCodeFragment(nameof(PropertyBuilder.IsUnicode)) { HasDataAnnotation = true }; if (property.IsUnicode() == false) { @@ -662,7 +664,8 @@ public static IEnumerable GetDataAnnotations(this ISkipNa root = root?.Chain(annotationsRoot) ?? annotationsRoot; } - annotationsRoot = GenerateAnnotations(property, annotationsHandledByDataAnnotations, annotationCodeGenerator, hasDataAnnotation: true); + annotationsRoot = GenerateAnnotations( + property, annotationsHandledByDataAnnotations, annotationCodeGenerator, hasDataAnnotation: true); if (annotationsRoot is not null) { root = root?.Chain(annotationsRoot) ?? annotationsRoot; @@ -672,13 +675,16 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure a foreign key. + /// Gets the fluent API calls to configure a foreign key. /// /// The foreign key. /// The provider's annotation code generator. /// A value indicating wheter to use string fluent API overloads instead of ones that take a property accessor lambda. /// The fluent API calls. - public static FluentApiCodeFragment? GetFluentApiCalls(this IForeignKey foreignKey, IAnnotationCodeGenerator annotationCodeGenerator, bool useStrings = false) + public static FluentApiCodeFragment? GetFluentApiCalls( + this IForeignKey foreignKey, + IAnnotationCodeGenerator annotationCodeGenerator, + bool useStrings = false) { FluentApiCodeFragment? root = null; @@ -709,10 +715,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa root = root?.Chain(hasPrincipalKey) ?? hasPrincipalKey; } - var hasForeignKey = new FluentApiCodeFragment(nameof(ReferenceReferenceBuilder.HasForeignKey)) - { - HasDataAnnotation = true - }; + var hasForeignKey = new FluentApiCodeFragment(nameof(ReferenceReferenceBuilder.HasForeignKey)) { HasDataAnnotation = true }; if (foreignKey.IsUnique) { @@ -754,7 +757,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa } /// - /// Gets the fluent API calls to configure a sequence. + /// Gets the fluent API calls to configure a sequence. /// /// The sequence. /// The provider's annotation code generator. @@ -765,40 +768,28 @@ public static IEnumerable GetDataAnnotations(this ISkipNa if (sequence.StartValue != Sequence.DefaultStartValue) { - var startsAt = new FluentApiCodeFragment(nameof(SequenceBuilder.StartsAt)) - { - Arguments = { sequence.StartValue } - }; + var startsAt = new FluentApiCodeFragment(nameof(SequenceBuilder.StartsAt)) { Arguments = { sequence.StartValue } }; root = root?.Chain(startsAt) ?? startsAt; } if (sequence.IncrementBy != Sequence.DefaultIncrementBy) { - var incrementsBy = new FluentApiCodeFragment(nameof(SequenceBuilder.IncrementsBy)) - { - Arguments = { sequence.IncrementBy } - }; + var incrementsBy = new FluentApiCodeFragment(nameof(SequenceBuilder.IncrementsBy)) { Arguments = { sequence.IncrementBy } }; root = root?.Chain(incrementsBy) ?? incrementsBy; } if (sequence.MinValue != Sequence.DefaultMinValue) { - var hasMin = new FluentApiCodeFragment(nameof(SequenceBuilder.HasMin)) - { - Arguments = { sequence.MinValue } - }; + var hasMin = new FluentApiCodeFragment(nameof(SequenceBuilder.HasMin)) { Arguments = { sequence.MinValue } }; root = root?.Chain(hasMin) ?? hasMin; } if (sequence.MaxValue != Sequence.DefaultMaxValue) { - var hasMax = new FluentApiCodeFragment(nameof(SequenceBuilder.HasMax)) - { - Arguments = { sequence.MaxValue } - }; + var hasMax = new FluentApiCodeFragment(nameof(SequenceBuilder.HasMax)) { Arguments = { sequence.MaxValue } }; root = root?.Chain(hasMax) ?? hasMax; } @@ -813,7 +804,11 @@ public static IEnumerable GetDataAnnotations(this ISkipNa return root; } - private static FluentApiCodeFragment? GenerateAnnotations(IAnnotatable annotatable, Dictionary annotations, IAnnotationCodeGenerator annotationCodeGenerator, bool hasDataAnnotation = false) + private static FluentApiCodeFragment? GenerateAnnotations( + IAnnotatable annotatable, + Dictionary annotations, + IAnnotationCodeGenerator annotationCodeGenerator, + bool hasDataAnnotation = false) { FluentApiCodeFragment? root = null; @@ -829,12 +824,7 @@ public static IEnumerable GetDataAnnotations(this ISkipNa { var hasAnnotation = new FluentApiCodeFragment(nameof(ModelBuilder.HasAnnotation)) { - Arguments = - { - annotation.Name, - annotation.Value - }, - HasDataAnnotation = hasDataAnnotation + Arguments = { annotation.Name, annotation.Value }, HasDataAnnotation = hasDataAnnotation }; root = root?.Chain(hasAnnotation) ?? hasAnnotation; diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs index f9497d6cbb4..bb659443443 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.cs @@ -149,7 +149,6 @@ public virtual string TransformText() { var keyFluentApiCalls = key.GetFluentApiCalls(annotationCodeGenerator); if (keyFluentApiCalls != null - || key.Properties.Count > 1 || (!key.IsHandledByConventions() && !Options.UseDataAnnotations)) { if (keyFluentApiCalls != null) diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt index ca6309261e9..02971eb93ba 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpDbContextGenerator.tt @@ -122,7 +122,6 @@ public partial class <#= Options.ContextName #> : DbContext { var keyFluentApiCalls = key.GetFluentApiCalls(annotationCodeGenerator); if (keyFluentApiCalls != null - || key.Properties.Count > 1 || (!key.IsHandledByConventions() && !Options.UseDataAnnotations)) { if (keyFluentApiCalls != null) diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs index 6814231614d..f7466dbf537 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpEntityTypeGeneratorTest.cs @@ -531,7 +531,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Assert.Equal("PrimaryKey", model.FindEntityType("TestNamespace.Entity").FindPrimaryKey().Properties[0].Name)); [ConditionalFact] - public void KeyAttribute_is_generated_on_multiple_properties_but_configuring_using_fluent_api_for_composite_key() + public void KeyAttribute_is_generated_on_multiple_properties_but_and_uses_PrimaryKeyAttribute_for_composite_key() => Test( modelBuilder => modelBuilder .Entity( @@ -554,6 +554,7 @@ public void KeyAttribute_is_generated_on_multiple_properties_but_configuring_usi namespace TestNamespace; +[PrimaryKey(""Key"", ""Serial"")] public partial class Post { [Key] @@ -593,11 +594,6 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.Key, e.Serial }); - }); - OnModelCreatingPartial(modelBuilder); } @@ -1531,11 +1527,6 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity(entity => - { - entity.HasKey(e => new { e.Id1, e.Id2 }); - }); - modelBuilder.Entity(entity => { entity.Property(e => e.Id).UseIdentityColumn();