diff --git a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs index 988653a58d5..c8a8ae5c8ab 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs @@ -805,8 +805,7 @@ protected virtual void GenerateEntityTypeAnnotations( if (((bool?)isExcludedAnnotation.Value) == true) { stringBuilder - .Append(", ") - .Append(Code.Literal(true)); + .Append(", t => t.IsExcludedFromMigrations()"); } annotations.Remove(isExcludedAnnotation.Name); } diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs index ecfd3cc15f6..3b6fca13d28 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs @@ -71,7 +71,9 @@ public static bool CanSetDefiningQuery( [CanBeNull] LambdaExpression query, bool fromDataAnnotation = false) #pragma warning disable EF1001 // Internal EF Core API usage. +#pragma warning disable CS0612 // Type or member is obsolete => entityTypeBuilder.CanSetAnnotation(CoreAnnotationNames.DefiningQuery, query, fromDataAnnotation); +#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore EF1001 // Internal EF Core API usage. } } diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs index 751fffe0c72..cbf076e9590 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs @@ -22,7 +22,9 @@ public static class InMemoryEntityTypeExtensions /// The LINQ query used as the default source. public static LambdaExpression GetDefiningQuery([NotNull] this IEntityType entityType) #pragma warning disable EF1001 // Internal EF Core API usage. +#pragma warning disable CS0612 // Type or member is obsolete => (LambdaExpression)Check.NotNull(entityType, nameof(entityType))[CoreAnnotationNames.DefiningQuery]; +#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore EF1001 // Internal EF Core API usage. /// @@ -35,7 +37,9 @@ public static void SetDefiningQuery( [CanBeNull] LambdaExpression definingQuery) => Check.NotNull(entityType, nameof(entityType)) #pragma warning disable EF1001 // Internal EF Core API usage. +#pragma warning disable CS0612 // Type or member is obsolete .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, definingQuery); +#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore EF1001 // Internal EF Core API usage. /// @@ -51,7 +55,9 @@ public static LambdaExpression SetDefiningQuery( bool fromDataAnnotation = false) => (LambdaExpression)Check.NotNull(entityType, nameof(entityType)) #pragma warning disable EF1001 // Internal EF Core API usage. +#pragma warning disable CS0612 // Type or member is obsolete .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, definingQuery, fromDataAnnotation) +#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore EF1001 // Internal EF Core API usage. ?.Value; @@ -62,7 +68,9 @@ public static LambdaExpression SetDefiningQuery( /// The configuration source for . public static ConfigurationSource? GetDefiningQueryConfigurationSource([NotNull] this IConventionEntityType entityType) #pragma warning disable EF1001 // Internal EF Core API usage. +#pragma warning disable CS0612 // Type or member is obsolete => entityType.FindAnnotation(CoreAnnotationNames.DefiningQuery)?.GetConfigurationSource(); +#pragma warning restore CS0612 // Type or member is obsolete #pragma warning restore EF1001 // Internal EF Core API usage. } } diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.cs index c9783f481e2..5c20f511e6d 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeBuilderExtensions.cs @@ -27,20 +27,20 @@ public static class RelationalEntityTypeBuilderExtensions public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name) - => ToTable(entityTypeBuilder, name, excludedFromMigrations: false); + => entityTypeBuilder.ToTable(name, (string)null); /// /// Configures the table that the entity type maps to when targeting a relational database. /// /// The builder for the entity type being configured. /// The name of the table. - /// A value indicating whether the table should be managed by migrations. + /// An action that performs configuration of the table. /// The same builder instance so that multiple calls can be chained. public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name, - bool excludedFromMigrations) - => entityTypeBuilder.ToTable(name, null, excludedFromMigrations); + [NotNull] Action buildAction) + => entityTypeBuilder.ToTable(name, null, buildAction); /// /// Configures the table that the entity type maps to when targeting a relational database. @@ -53,7 +53,7 @@ public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name) where TEntity : class - => (EntityTypeBuilder)ToTable((EntityTypeBuilder)entityTypeBuilder, name, excludedFromMigrations: false); + => entityTypeBuilder.ToTable(name, (string)null); /// /// Configures the table that the entity type maps to when targeting a relational database. @@ -61,14 +61,14 @@ public static EntityTypeBuilder ToTable( /// The entity type being configured. /// The builder for the entity type being configured. /// The name of the table. - /// A value indicating whether the table should be managed by migrations. + /// An action that performs configuration of the table. /// The same builder instance so that multiple calls can be chained. public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name, - bool excludedFromMigrations) + [NotNull] Action> buildAction) where TEntity : class - => (EntityTypeBuilder)ToTable((EntityTypeBuilder)entityTypeBuilder, name, excludedFromMigrations); + => entityTypeBuilder.ToTable(name, null, buildAction); /// /// Configures the table that the entity type maps to when targeting a relational database. @@ -81,7 +81,11 @@ public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name, [CanBeNull] string schema) - => entityTypeBuilder.ToTable(name, schema, excludedFromMigrations: false); + { + entityTypeBuilder.Metadata.SetTableName(name); + entityTypeBuilder.Metadata.SetSchema(schema); + return entityTypeBuilder; + } /// /// Configures the table that the entity type maps to when targeting a relational database. @@ -89,21 +93,20 @@ public static EntityTypeBuilder ToTable( /// The builder for the entity type being configured. /// The name of the table. /// The schema of the table. - /// A value indicating whether the table should be managed by migrations. + /// An action that performs configuration of the table. /// The same builder instance so that multiple calls can be chained. public static EntityTypeBuilder ToTable( - [NotNull] this EntityTypeBuilder entityTypeBuilder, - [CanBeNull] string name, - [CanBeNull] string schema, - bool excludedFromMigrations) + [NotNull] this EntityTypeBuilder entityTypeBuilder, + [NotNull] string name, + [CanBeNull] string schema, + [NotNull] Action buildAction) { - Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); - Check.NullButNotEmpty(name, nameof(name)); + Check.NotNull(name, nameof(name)); Check.NullButNotEmpty(schema, nameof(schema)); + buildAction(new TableBuilder(name, schema, entityTypeBuilder.Metadata)); entityTypeBuilder.Metadata.SetTableName(name); entityTypeBuilder.Metadata.SetSchema(schema); - entityTypeBuilder.Metadata.SetIsTableExcludedFromMigrations(excludedFromMigrations); return entityTypeBuilder; } @@ -121,7 +124,11 @@ public static EntityTypeBuilder ToTable( [CanBeNull] string name, [CanBeNull] string schema) where TEntity : class - => (EntityTypeBuilder)ToTable((EntityTypeBuilder)entityTypeBuilder, name, schema, excludedFromMigrations: false); + { + entityTypeBuilder.Metadata.SetTableName(name); + entityTypeBuilder.Metadata.SetSchema(schema); + return entityTypeBuilder; + } /// /// Configures the table that the entity type maps to when targeting a relational database. @@ -130,15 +137,24 @@ public static EntityTypeBuilder ToTable( /// The builder for the entity type being configured. /// The name of the table. /// The schema of the table. - /// A value indicating whether the table should be managed by migrations. + /// An action that performs configuration of the table. /// The same builder instance so that multiple calls can be chained. public static EntityTypeBuilder ToTable( [NotNull] this EntityTypeBuilder entityTypeBuilder, [CanBeNull] string name, [CanBeNull] string schema, - bool excludedFromMigrations) + [NotNull] Action> buildAction) where TEntity : class - => (EntityTypeBuilder)ToTable((EntityTypeBuilder)entityTypeBuilder, name, schema, excludedFromMigrations); + { + Check.NotNull(name, nameof(name)); + Check.NullButNotEmpty(schema, nameof(schema)); + + buildAction(new TableBuilder(name, schema, entityTypeBuilder.Metadata)); + entityTypeBuilder.Metadata.SetTableName(name); + entityTypeBuilder.Metadata.SetSchema(schema); + + return entityTypeBuilder; + } /// /// Configures the table that the entity type maps to when targeting a relational database. diff --git a/src/EFCore.Relational/Metadata/Builders/TableBuilder.cs b/src/EFCore.Relational/Metadata/Builders/TableBuilder.cs new file mode 100644 index 00000000000..66ac541922a --- /dev/null +++ b/src/EFCore.Relational/Metadata/Builders/TableBuilder.cs @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.ComponentModel; +using System.Diagnostics; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Microsoft.EntityFrameworkCore.Metadata.Builders +{ + /// + /// + /// Instances of this class are returned from methods when using the API + /// and it is not designed to be directly constructed in your application code. + /// + /// + public class TableBuilder + { + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + public TableBuilder([NotNull] string name, [CanBeNull] string schema, [NotNull] IMutableEntityType entityType) + { + EntityType = entityType; + } + + private IMutableEntityType EntityType { [DebuggerStepThrough] get; } + + /// + /// Configures the table to be ignored by migrations. + /// + /// A value indicating whether the table should be managed by migrations. + /// The same builder instance so that multiple calls can be chained. + public virtual TableBuilder IsExcludedFromMigrations(bool excluded = true) + { + EntityType.SetIsTableExcludedFromMigrations(excluded); + return this; + } + + #region Hidden System.Object members + + /// + /// Returns a string that represents the current object. + /// + /// A string that represents the current object. + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() => base.ToString(); + + /// + /// Determines whether the specified object is equal to the current object. + /// + /// The object to compare with the current object. + /// if the specified object is equal to the current object; otherwise, . + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) => base.Equals(obj); + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() => base.GetHashCode(); + + #endregion + } +} diff --git a/src/EFCore.Relational/Metadata/Builders/TableBuilder`.cs b/src/EFCore.Relational/Metadata/Builders/TableBuilder`.cs new file mode 100644 index 00000000000..1a633a52295 --- /dev/null +++ b/src/EFCore.Relational/Metadata/Builders/TableBuilder`.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace Microsoft.EntityFrameworkCore.Metadata.Builders +{ + /// + /// + /// Instances of this class are returned from methods when using the API + /// and it is not designed to be directly constructed in your application code. + /// + /// + /// The entity type being configured. + public class TableBuilder : TableBuilder + where TEntity : class + { + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + public TableBuilder([NotNull] string name, [CanBeNull] string schema, [NotNull] IMutableEntityType entityType) + : base(name, schema, entityType) + { + } + + /// + /// Configures the table to be ignored by migrations. + /// + /// A value indicating whether the table should be managed by migrations. + /// The same builder instance so that multiple calls can be chained. + public new virtual TableBuilder IsExcludedFromMigrations(bool excluded = true) + => (TableBuilder)base.IsExcludedFromMigrations(excluded); + } +} diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs index aca643ea497..88daa81d1b2 100644 --- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs +++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs @@ -206,6 +206,7 @@ public static class CoreAnnotationNames /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// + [Obsolete] public const string DefiningQuery = "DefiningQuery"; /// @@ -313,7 +314,9 @@ public static class CoreAnnotationNames AfterSaveBehavior, BeforeSaveBehavior, QueryFilter, +#pragma warning disable CS0612 // Type or member is obsolete DefiningQuery, +#pragma warning restore CS0612 // Type or member is obsolete EagerLoaded, ProviderClrType, InverseNavigations, diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index fcddc404723..4abadc36e4b 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -147,7 +147,9 @@ public void Test_new_annotations_handled_for_properties() CoreAnnotationNames.NavigationAccessMode, CoreAnnotationNames.EagerLoaded, CoreAnnotationNames.QueryFilter, +#pragma warning disable CS0612 // Type or member is obsolete CoreAnnotationNames.DefiningQuery, +#pragma warning restore CS0612 // Type or member is obsolete CoreAnnotationNames.DiscriminatorProperty, CoreAnnotationNames.DiscriminatorValue, CoreAnnotationNames.InverseNavigations, diff --git a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs index 6fd1a4499b3..e6dcc840e6b 100644 --- a/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs +++ b/test/EFCore.Design.Tests/Migrations/ModelSnapshotSqlServerTest.cs @@ -434,7 +434,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT_with_one_exclu builder => { builder.Entity() - .ToTable("DerivedEntity", "foo", excludedFromMigrations: true); + .ToTable("DerivedEntity", "foo", t => t.IsExcludedFromMigrations()); builder.Entity(); }, AddBoilerPlate( @@ -462,7 +462,7 @@ public virtual void Entities_are_stored_in_model_snapshot_for_TPT_with_one_exclu b.Property(""Name"") .HasColumnType(""nvarchar(max)""); - b.ToTable(""DerivedEntity"", ""foo"", true); + b.ToTable(""DerivedEntity"", ""foo"", t => t.IsExcludedFromMigrations()); });"), o => { @@ -704,7 +704,7 @@ public virtual void EntityType_annotations_are_stored_in_snapshot() });"), o => { - Assert.Equal(6, o.GetEntityTypes().First().GetAnnotations().Count()); + Assert.Equal(5, o.GetEntityTypes().First().GetAnnotations().Count()); Assert.Equal("AnnotationValue", o.GetEntityTypes().First()["AnnotationName"]); }); } diff --git a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs index e35349b068e..03cebd3753f 100644 --- a/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs +++ b/test/EFCore.Relational.Tests/Infrastructure/RelationalModelValidatorTest.cs @@ -411,8 +411,8 @@ public virtual void Passes_for_compatible_excluded_shared_table_inverted() var modelBuilder = CreateConventionalModelBuilder(); modelBuilder.Entity().HasOne().WithOne().IsRequired().HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id); - modelBuilder.Entity().ToTable("Table", excludedFromMigrations: true); - modelBuilder.Entity().ToTable("Table", excludedFromMigrations: true); + modelBuilder.Entity().ToTable("Table", t => t.IsExcludedFromMigrations()); + modelBuilder.Entity().ToTable("Table", t => t.IsExcludedFromMigrations()); Validate(modelBuilder.Model); } @@ -423,7 +423,7 @@ public virtual void Passes_for_compatible_excluded_shared_table_owned() var modelBuilder = CreateConventionalModelBuilder(); modelBuilder.Entity().OwnsOne(b => b.A); - modelBuilder.Entity().ToTable("Table", excludedFromMigrations: true); + modelBuilder.Entity().ToTable("Table", t => t.IsExcludedFromMigrations()); var model = Validate(modelBuilder.Model); @@ -437,7 +437,7 @@ public virtual void Passes_for_compatible_excluded_table_derived() { var modelBuilder = CreateConventionalModelBuilder(); - modelBuilder.Entity().ToTable("Table", excludedFromMigrations: true); + modelBuilder.Entity().ToTable("Table", t => t.IsExcludedFromMigrations()); modelBuilder.Entity(); var model = Validate(modelBuilder.Model); @@ -453,7 +453,7 @@ public virtual void Detect_partially_excluded_shared_table() var modelBuilder = CreateConventionalModelBuilder(); modelBuilder.Entity().HasOne().WithOne().IsRequired().HasPrincipalKey(a => a.Id).HasForeignKey(b => b.Id); - modelBuilder.Entity().ToTable("Table", excludedFromMigrations: true); + modelBuilder.Entity().ToTable("Table", t => t.IsExcludedFromMigrations()); modelBuilder.Entity().ToTable("Table"); VerifyError( diff --git a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs index ff42971587c..222d9257dfc 100644 --- a/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs +++ b/test/EFCore.Relational.Tests/RelationalApiConsistencyTest.cs @@ -91,6 +91,8 @@ public override bool TryGetProviderOptionsDelegate(out Action), typeof(SequenceBuilder), typeof(MigrationBuilder), typeof(AlterOperationBuilder<>),