From 3148cd0347d4f5dfda2b26806709b0b21c707bfc Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 6 Aug 2019 12:08:22 +0200 Subject: [PATCH] Full OldTable on AlterTableOperation, further comment support Changed AlterTableOperation.OldTable from Annotatable to TableOperation, and did various fixes throughout to support comment alteration on tables. Some comment fixes and refactoring. Fixes #16819 Fixes #16798 --- .../CSharpMigrationOperationGenerator.cs | 15 ++ .../Internal/MigrationsModelDiffer.cs | 42 +++--- .../Migrations/MigrationBuilder.cs | 12 +- .../Operations/AlterTableOperation.cs | 9 +- .../Operations/Builders/ColumnsBuilder.cs | 7 +- .../Operations/CreateTableOperation.cs | 7 +- .../Migrations/Operations/TableOperation.cs | 19 +++ .../SqlServerMigrationsSqlGenerator.cs | 133 ++++++++++++------ .../CSharpMigrationOperationGeneratorTest.cs | 12 +- .../Internal/MigrationsModelDifferTest.cs | 32 +++++ .../SqlServerMigrationSqlGeneratorTest.cs | 127 ++++++++++++++++- 11 files changed, 332 insertions(+), 83 deletions(-) create mode 100644 src/EFCore.Relational/Migrations/Operations/TableOperation.cs diff --git a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs index 5d8bc4518be..e3cfec93bd4 100644 --- a/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs +++ b/src/EFCore.Design/Migrations/Design/CSharpMigrationOperationGenerator.cs @@ -768,6 +768,14 @@ protected virtual void Generate([NotNull] AlterTableOperation operation, [NotNul .Append(Code.Literal(operation.Comment)); } + if (operation.OldTable.Comment != null) + { + builder + .AppendLine(",") + .Append("oldComment: ") + .Append(Code.Literal(operation.OldTable.Comment)); + } + builder.Append(")"); Annotations(operation.GetAnnotations(), builder); @@ -1053,6 +1061,13 @@ protected virtual void Generate([NotNull] CreateTableOperation operation, [NotNu .Append(Code.UnknownLiteral(column.DefaultValue)); } + if (column.Comment != null) + { + builder + .Append(", comment: ") + .Append(Code.Literal(operation.Comment)); + } + builder.Append(")"); using (builder.Indent()) diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs index ea0ec10b6f3..d7b9eff0640 100644 --- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs +++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs @@ -557,39 +557,41 @@ protected virtual IEnumerable Diff( }; } - var operations = DiffAnnotations(source, target) - .Concat(Diff(source.GetProperties(), target.GetProperties(), diffContext)) - .Concat(Diff(source.GetKeys(), target.GetKeys(), diffContext)) - .Concat(Diff(source.GetIndexes(), target.GetIndexes(), diffContext)) - .Concat(Diff(source.GetCheckConstraints(), target.GetCheckConstraints(), diffContext)); - foreach (var operation in operations) - { - yield return operation; - } - - DiffData(source, target, diffContext); - } - - private IEnumerable DiffAnnotations( - [NotNull] TableMapping source, - [NotNull] TableMapping target) - { // Validation should ensure that all the relevant annotations for the collocated entity types are the same var sourceMigrationsAnnotations = MigrationsAnnotations.For(source.EntityTypes[0]).ToList(); var targetMigrationsAnnotations = MigrationsAnnotations.For(target.EntityTypes[0]).ToList(); - if (HasDifferences(sourceMigrationsAnnotations, targetMigrationsAnnotations)) + + if (source.GetComment() != target.GetComment() + || HasDifferences(sourceMigrationsAnnotations, targetMigrationsAnnotations)) { var alterTableOperation = new AlterTableOperation { Name = target.Name, Schema = target.Schema, - Comment = target.GetComment() + Comment = target.GetComment(), + OldTable = + { + Comment = source.GetComment() + } }; - alterTableOperation.AddAnnotations(targetMigrationsAnnotations); + alterTableOperation.AddAnnotations(targetMigrationsAnnotations); alterTableOperation.OldTable.AddAnnotations(sourceMigrationsAnnotations); + yield return alterTableOperation; } + + var operations = Diff(source.GetProperties(), target.GetProperties(), diffContext) + .Concat(Diff(source.GetKeys(), target.GetKeys(), diffContext)) + .Concat(Diff(source.GetIndexes(), target.GetIndexes(), diffContext)) + .Concat(Diff(source.GetCheckConstraints(), target.GetCheckConstraints(), diffContext)); + + foreach (var operation in operations) + { + yield return operation; + } + + DiffData(source, target, diffContext); } /// diff --git a/src/EFCore.Relational/Migrations/MigrationBuilder.cs b/src/EFCore.Relational/Migrations/MigrationBuilder.cs index cbba0e06c3f..28d63a50801 100644 --- a/src/EFCore.Relational/Migrations/MigrationBuilder.cs +++ b/src/EFCore.Relational/Migrations/MigrationBuilder.cs @@ -480,11 +480,13 @@ public virtual AlterOperationBuilder AlterSequence( /// The table name. /// The schema that contains the table, or null to use the default schema. /// A comment to associate with the table. + /// The previous comment to associate with the table. /// A builder to allow annotations to be added to the operation. public virtual AlterOperationBuilder AlterTable( [NotNull] string name, [CanBeNull] string schema = null, - [CanBeNull] string comment = null) + [CanBeNull] string comment = null, + [CanBeNull] string oldComment = null) { Check.NotEmpty(name, nameof(name)); @@ -492,7 +494,11 @@ public virtual AlterOperationBuilder AlterTable( { Schema = schema, Name = name, - Comment = comment + Comment = comment, + OldTable = new TableOperation + { + Comment = oldComment + } }; Operations.Add(operation); @@ -679,7 +685,7 @@ public virtual OperationBuilder CreateCheckConst /// /// A delegate allowing constraints to be applied over the columns configured by the 'columns' delegate above. /// - /// A comment to been applied to the table. + /// A comment to be applied to the table. /// A to allow further configuration to be chained. public virtual CreateTableBuilder CreateTable( [NotNull] string name, diff --git a/src/EFCore.Relational/Migrations/Operations/AlterTableOperation.cs b/src/EFCore.Relational/Migrations/Operations/AlterTableOperation.cs index 535cf6c92ac..533fe5ac285 100644 --- a/src/EFCore.Relational/Migrations/Operations/AlterTableOperation.cs +++ b/src/EFCore.Relational/Migrations/Operations/AlterTableOperation.cs @@ -10,7 +10,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Operations /// /// A to alter an existing table. /// - public class AlterTableOperation : MigrationOperation, IAlterMigrationOperation + public class AlterTableOperation : TableOperation, IAlterMigrationOperation { /// /// The name of the table. @@ -22,15 +22,10 @@ public class AlterTableOperation : MigrationOperation, IAlterMigrationOperation /// public virtual string Schema { get; [param: CanBeNull] set; } - /// - /// Comment for this table - /// - public virtual string Comment { get; [param: CanBeNull] set; } - /// /// An operation representing the table as it was before being altered. /// - public virtual Annotatable OldTable { get; [param: NotNull] set; } = new Annotatable(); + public virtual TableOperation OldTable { get; [param: NotNull] set; } = new TableOperation(); /// IMutableAnnotatable IAlterMigrationOperation.OldAnnotations => OldTable; diff --git a/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs b/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs index 9554b377f26..c0142ab04a4 100644 --- a/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs +++ b/src/EFCore.Relational/Migrations/Operations/Builders/ColumnsBuilder.cs @@ -44,6 +44,7 @@ public ColumnsBuilder([NotNull] CreateTableOperation createTableOperation) /// The SQL expression to use for the column's default constraint. /// The SQL expression to use to compute the column value. /// Indicates whether or not the column is constrained to fixed-length data. + /// A comment to be applied to the table. /// The same builder so that multiple calls can be chained. public virtual OperationBuilder Column( [CanBeNull] string type = null, @@ -55,7 +56,8 @@ public virtual OperationBuilder Column( [CanBeNull] object defaultValue = null, [CanBeNull] string defaultValueSql = null, [CanBeNull] string computedColumnSql = null, - bool? fixedLength = null) + bool? fixedLength = null, + [CanBeNull] string comment = null) { var operation = new AddColumnOperation { @@ -71,7 +73,8 @@ public virtual OperationBuilder Column( DefaultValue = defaultValue, DefaultValueSql = defaultValueSql, ComputedColumnSql = computedColumnSql, - IsFixedLength = fixedLength + IsFixedLength = fixedLength, + Comment = comment }; _createTableOperation.Columns.Add(operation); diff --git a/src/EFCore.Relational/Migrations/Operations/CreateTableOperation.cs b/src/EFCore.Relational/Migrations/Operations/CreateTableOperation.cs index dd7f917b4bc..b2df019d7e6 100644 --- a/src/EFCore.Relational/Migrations/Operations/CreateTableOperation.cs +++ b/src/EFCore.Relational/Migrations/Operations/CreateTableOperation.cs @@ -9,7 +9,7 @@ namespace Microsoft.EntityFrameworkCore.Migrations.Operations /// /// A for creating a new table. /// - public class CreateTableOperation : MigrationOperation + public class CreateTableOperation : TableOperation { /// /// The name of the table. @@ -21,11 +21,6 @@ public class CreateTableOperation : MigrationOperation /// public virtual string Schema { get; [param: CanBeNull] set; } - /// - /// Comment for this table - /// - public virtual string Comment { get; [param: CanBeNull] set; } - /// /// The representing the creation of the primary key for the table. /// diff --git a/src/EFCore.Relational/Migrations/Operations/TableOperation.cs b/src/EFCore.Relational/Migrations/Operations/TableOperation.cs new file mode 100644 index 00000000000..ed672d2264e --- /dev/null +++ b/src/EFCore.Relational/Migrations/Operations/TableOperation.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Migrations.Operations +{ + /// + /// A for operations on tables. + /// See also and . + /// + public class TableOperation : MigrationOperation + { + /// + /// Comment for this table + /// + public virtual string Comment { get; [param: CanBeNull] set; } + } +} diff --git a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs index de76644d0b3..77fd6791f2c 100644 --- a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs +++ b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -137,7 +138,10 @@ protected override void Generate( if (operation.Comment != null) { - AddDropComment(builder, operation.Comment, null, operation.Schema, operation.Table, operation.Name); + GenerateComment(builder, model, operation.Comment, null, + operation.Schema, + "Table", operation.Table, + "Column", operation.Name); } builder.EndCommand(suppressTransaction: IsMemoryOptimized(operation, model, operation.Schema, operation.Table)); @@ -339,7 +343,10 @@ protected override void Generate( if (operation.OldColumn.Comment != operation.Comment) { - AddDropComment(builder, operation.Comment, operation.OldColumn.Comment, operation.Schema, operation.Table, operation.Name); + GenerateComment(builder, model, operation.Comment, operation.OldColumn.Comment, + operation.Schema, + "Table", operation.Table, + "Column", operation.Name); } if (narrowed) @@ -478,12 +485,16 @@ protected override void Generate( if (operation.Comment != null) { - AddDropComment(builder, operation.Comment, null, operation.Schema, operation.Name); + GenerateComment(builder, model, operation.Comment, null, operation.Schema, "Table", operation.Name); } foreach (var column in operation.Columns.Where(c => c.Comment != null)) { - AddDropComment(builder, column.Comment, null, operation.Schema, operation.Name, column.Name); + GenerateComment(builder, model, column.Comment, null, + operation.Schema, + "Table", operation.Name, + "Column", column.Name, + firstComment: false); } builder.EndCommand(suppressTransaction: memoryOptimized); @@ -671,7 +682,7 @@ protected override void Generate(EnsureSchemaOperation operation, IModel model, Check.NotNull(operation, nameof(operation)); Check.NotNull(builder, nameof(builder)); - if (string.Equals(operation.Name, "DBO", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(operation.Name, "dbo", StringComparison.OrdinalIgnoreCase)) { return; } @@ -965,7 +976,12 @@ protected override void Generate(AlterTableOperation operation, IModel model, Mi throw new InvalidOperationException(SqlServerStrings.AlterMemoryOptimizedTable); } - base.Generate(operation, model, builder); + if (operation.OldTable.Comment != operation.Comment) + { + GenerateComment(builder, model, operation.Comment, operation.OldTable.Comment, operation.Schema, "Table", operation.Name); + } + + builder.EndCommand(suppressTransaction: IsMemoryOptimized(operation, model, operation.Schema, operation.Name)); } /// @@ -1640,41 +1656,76 @@ protected virtual void CreateIndexes( /// /// /// The command builder to use to build the commands. + /// The target model which may be null if the operations exist without a model. /// The new comment to be applied. /// The previous comment. /// The schema of the table. - /// The name of the table. - /// The column name if comment is being applied to a column. - protected virtual void AddDropComment( + /// The type of the level1 object (Table, Index). + /// The name of the table or index. + /// The type of the level2 object (Column). + /// The name of the column. + /// + /// Indicates whether this is the first comment operation being generated in this batch. + /// Only the first operation will cause the @schema variable to be declared and set. + /// + protected virtual void GenerateComment( [NotNull] MigrationCommandListBuilder builder, + [CanBeNull] IModel model, [CanBeNull] string comment, [CanBeNull] string oldComment, - [NotNull] string schema, - [NotNull] string table, - [CanBeNull] string columnName = null) + [CanBeNull] string schema, + [NotNull] string level1Type, + [NotNull] string level1Name, + [CanBeNull] string level2Type = null, + [CanBeNull] string level2Name = null, + bool firstComment = true) { if (comment == oldComment) { return; } + var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string)); + + schema ??= model?.GetDefaultSchema(); + if (schema == null) + { + if (firstComment) + { + builder.Append("DECLARE @schema AS nvarchar(max)") + .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator); + builder.Append("SET @schema = SCHEMA_NAME()") + .AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator); + } + schema = "@schema"; + } + else + { + schema = Literal(schema); + } + if (oldComment != null) { - GenerateDropExtendedProperty(builder, - "Comment", - "Schema", schema, - "Table", table, - columnName == null ? null : "Column", columnName); + GenerateDropExtendedProperty( + builder, + Literal("Comment"), + Literal("Schema"), schema, + Literal(level1Type), Literal(level1Name), + level2Type == null ? null : Literal(level2Type), + level2Type == null ? null : Literal(level2Name)); } if (comment != null) { GenerateAddExtendedProperty(builder, - "Comment", comment, - "Schema", schema, - "Table", table, - columnName == null ? null : "Column", columnName); + Literal("Comment"), Literal(comment), + Literal("Schema"), schema, + Literal(level1Type), Literal(level1Name), + level2Type == null ? null : Literal(level2Type), + level2Type == null ? null : Literal(level2Name)); } + + string Literal(string s) => stringTypeMapping.GenerateSqlLiteral(s); } /// @@ -1717,37 +1768,38 @@ protected virtual void GenerateAddExtendedProperty( Check.NotNull(builder, nameof(builder)); Check.NotNull(name, nameof(name)); - var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string)); - - builder.Append("EXEC sp_addextendedproperty @name = ").Append(stringTypeMapping.GenerateSqlLiteral(name)); + builder.Append("EXEC sp_addextendedproperty @name = ").Append(name); if (value != null) { - builder.Append(", @value = ").Append(stringTypeMapping.GenerateSqlLiteral(value)); + builder.Append(", @value = ").Append(value); } if (level0Type != null) { + Debug.Assert(level0Name != null); builder .Append(", @level0type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level0Type)) + .Append(level0Type) .Append(", @level0name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level0Name)); + .Append(level0Name); if (level1Type != null) { + Debug.Assert(level1Name != null); builder .Append(", @level1type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level1Type)) + .Append(level1Type) .Append(", @level1name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level1Name)); + .Append(level1Name); if (level2Type != null) { + Debug.Assert(level2Name != null); builder .Append(", @level2type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level2Type)) + .Append(level2Type) .Append(", @level2name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level2Name)); + .Append(level2Name); } } } @@ -1790,33 +1842,34 @@ protected virtual void GenerateDropExtendedProperty( Check.NotNull(builder, nameof(builder)); Check.NotNull(name, nameof(name)); - var stringTypeMapping = Dependencies.TypeMappingSource.GetMapping(typeof(string)); - - builder.Append("EXEC sp_dropextendedproperty @name = ").Append(stringTypeMapping.GenerateSqlLiteral(name)); + builder.Append("EXEC sp_dropextendedproperty @name = ").Append(name); if (level0Type != null) { + Debug.Assert(level0Name != null); builder .Append(", @level0type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level0Type)) + .Append(level0Type) .Append(", @level0name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level0Name)); + .Append(level0Name); if (level1Type != null) { + Debug.Assert(level1Name != null); builder .Append(", @level1type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level1Type)) + .Append(level1Type) .Append(", @level1name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level1Name)); + .Append(level1Name); if (level2Type != null) { + Debug.Assert(level2Name != null); builder .Append(", @level2type = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level2Type)) + .Append(level2Type) .Append(", @level2name = ") - .Append(stringTypeMapping.GenerateSqlLiteral(level2Name)); + .Append(level2Name); } } } diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs index a19d9da464b..e903052693a 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs @@ -778,17 +778,23 @@ public void AlterTableOperation_all_args() { Name = "Customer", Schema = "dbo", - Comment = "My Comment" + Comment = "My Comment 2", + OldTable = + { + Comment = "My Comment" + } }, "mb.AlterTable(" + _eol + " name: \"Customer\"," + _eol + " schema: \"dbo\"," + _eol + - " comment: \"My Comment\");", + " comment: \"My Comment 2\"," + _eol + + " oldComment: \"My Comment\");", o => { Assert.Equal("Customer", o.Name); Assert.Equal("dbo", o.Schema); - Assert.Equal("My Comment", o.Comment); + Assert.Equal("My Comment 2", o.Comment); + Assert.Equal("My Comment", o.OldTable.Comment); }); } diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index 35b9f058e1d..e4de183a56d 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -620,6 +620,38 @@ public void Create_table_columns_handles_self_referencing_one_to_one() }); } + [ConditionalFact] + public void Alter_table_comment() + { + Execute( + source => source.Entity( + "MountainLion", + x => + { + x.ToTable("MountainLion", "dbo"); + x.Property("Id"); + x.HasComment("Old comment"); + }), + target => target.Entity( + "MountainLion", + x => + { + x.ToTable("MountainLion", "dbo"); + x.Property("Id"); + x.HasComment("New comment"); + }), + operations => + { + Assert.Equal(1, operations.Count); + + var operation = Assert.IsType(operations[0]); + Assert.Equal("dbo", operation.Schema); + Assert.Equal("MountainLion", operation.Name); + Assert.Equal("New comment", operation.Comment); + Assert.Equal("Old comment", operation.OldTable.Comment); + }); + } + [ConditionalFact] public void Rename_table() { diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerMigrationSqlGeneratorTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerMigrationSqlGeneratorTest.cs index cd17d8b28cd..e205792e0c9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerMigrationSqlGeneratorTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerMigrationSqlGeneratorTest.cs @@ -35,6 +35,38 @@ FOREIGN KEY ([EmployerId]) REFERENCES [Companies] ([Id]) "); } + [ConditionalFact] + public void CreateTableOperation_default_schema_with_comment() + { + Generate( + new CreateTableOperation + { + Name = "People", + Columns = + { + new AddColumnOperation + { + Name = "Id", + Table = "People", + ClrType = typeof(int), + IsNullable = false, + Comment = "ID comment" + }, + }, + Comment = "Table comment" + }); + + AssertSql( + @"CREATE TABLE [People] ( + [Id] int NOT NULL +); +DECLARE @schema AS nvarchar(max); +SET @schema = SCHEMA_NAME(); +EXEC sp_addextendedproperty @name = N'Comment', @value = N'Table comment', @level0type = N'Schema', @level0name = @schema, @level1type = N'Table', @level1name = N'People'; +EXEC sp_addextendedproperty @name = N'Comment', @value = N'ID comment', @level0type = N'Schema', @level0name = @schema, @level1type = N'Table', @level1name = N'People', @level2type = N'Column', @level2name = N'Id'; +"); + } + public override void CreateIndexOperation_with_filter_where_clause() { base.CreateIndexOperation_with_filter_where_clause(); @@ -51,6 +83,76 @@ public override void CreateIndexOperation_with_filter_where_clause_and_is_unique "); } + [ConditionalFact] + public void AlterTableOperation_with_new_comment() + { + Generate( + new AlterTableOperation + { + Name = "People", + Schema = "dbo", + Comment = "My Comment" + }); + + AssertSql( + @"EXEC sp_addextendedproperty @name = N'Comment', @value = N'My Comment', @level0type = N'Schema', @level0name = N'dbo', @level1type = N'Table', @level1name = N'People'; +"); + } + + [ConditionalFact] + public void AlterTableOperation_with_different_comment_to_existing() + { + Generate( + modelBuilder => modelBuilder + .HasAnnotation(CoreAnnotationNames.ProductVersion, "1.1.0") + .Entity( + "Person", x => + { + x.HasComment("My Comment"); + }), + new AlterTableOperation + { + Schema = "dbo", + Name = "People", + Comment = "My Comment 2", + OldTable = new TableOperation + { + Comment = "My Comment" + } + }); + + AssertSql( + @"EXEC sp_dropextendedproperty @name = N'Comment', @level0type = N'Schema', @level0name = N'dbo', @level1type = N'Table', @level1name = N'People'; +EXEC sp_addextendedproperty @name = N'Comment', @value = N'My Comment 2', @level0type = N'Schema', @level0name = N'dbo', @level1type = N'Table', @level1name = N'People'; +"); + } + + [ConditionalFact] + public void AlterTableOperation_removing_comment() + { + Generate( + modelBuilder => modelBuilder + .HasAnnotation(CoreAnnotationNames.ProductVersion, "1.1.0") + .Entity( + "Person", x => + { + x.HasComment("My Comment"); + }), + new AlterTableOperation + { + Schema = "dbo", + Name = "People", + OldTable = new TableOperation + { + Comment = "My Comment" + } + }); + + AssertSql( + @"EXEC sp_dropextendedproperty @name = N'Comment', @level0type = N'Schema', @level0name = N'dbo', @level1type = N'Table', @level1name = N'People'; +"); + } + [ConditionalFact] public virtual void AddColumnOperation_with_computedSql() { @@ -255,7 +357,28 @@ public virtual void AddColumnOperation_with_comment() AssertSql( @"ALTER TABLE [People] ADD [FullName] nvarchar(max) NOT NULL; -EXEC sp_addextendedproperty @name = N'Comment', @value = N'My comment', @level0type = N'Schema', @level0name = NULL, @level1type = N'Table', @level1name = N'People', @level2type = N'Column', @level2name = N'FullName'; +DECLARE @schema AS nvarchar(max); +SET @schema = SCHEMA_NAME(); +EXEC sp_addextendedproperty @name = N'Comment', @value = N'My comment', @level0type = N'Schema', @level0name = @schema, @level1type = N'Table', @level1name = N'People', @level2type = N'Column', @level2name = N'FullName'; +"); + } + + [ConditionalFact] + public virtual void AddColumnOperation_with_comment_non_default_schema() + { + Generate( + new AddColumnOperation + { + Schema = "my", + Table = "People", + Name = "FullName", + ClrType = typeof(string), + Comment = "My comment" + }); + + AssertSql( + @"ALTER TABLE [my].[People] ADD [FullName] nvarchar(max) NOT NULL; +EXEC sp_addextendedproperty @name = N'Comment', @value = N'My comment', @level0type = N'Schema', @level0name = N'my', @level1type = N'Table', @level1name = N'People', @level2type = N'Column', @level2name = N'FullName'; "); } @@ -1024,7 +1147,7 @@ FROM [sys].[default_constraints] [d] } [ConditionalFact] - public void AlterColumnOperation_by_removing_comment() + public void AlterColumnOperation_removing_comment() { Generate( modelBuilder => modelBuilder