diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseColumn.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseColumn.cs
index a463e9df03b..475957fec45 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseColumn.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseColumn.cs
@@ -61,5 +61,7 @@ public DatabaseColumn([NotNull] DatabaseTable table, [NotNull] string name, [Not
/// the database will not generate values.
///
public virtual ValueGenerated? ValueGenerated { get; set; }
+
+ public override string ToString() => Name;
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseForeignKey.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseForeignKey.cs
index ba179c5c09a..cc0c3cfd2a6 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseForeignKey.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseForeignKey.cs
@@ -58,5 +58,7 @@ public DatabaseForeignKey(
/// is deleted, or null if there is no action defined.
///
public virtual ReferentialAction? OnDelete { get; set; }
+
+ public override string ToString() => Name ?? "";
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseIndex.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseIndex.cs
index 7211a25dd4c..44000493ff6 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseIndex.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseIndex.cs
@@ -45,5 +45,7 @@ public DatabaseIndex([NotNull] DatabaseTable table, [CanBeNull] string? name)
/// The filter expression, or null if the index has no filter.
///
public virtual string? Filter { get; [param: CanBeNull] set; }
+
+ public override string ToString() => Name ?? "";
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabasePrimaryKey.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabasePrimaryKey.cs
index 34ee5c00ce2..0b3f5aa3d6e 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabasePrimaryKey.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabasePrimaryKey.cs
@@ -35,5 +35,7 @@ public DatabasePrimaryKey([NotNull] DatabaseTable table, [CanBeNull] string? nam
/// The ordered list of columns that make up the primary key.
///
public virtual IList Columns { get; }
+
+ public override string ToString() => Name ?? "";
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseSequence.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseSequence.cs
index f1bd6b2ff25..6488b8bcb6f 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseSequence.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseSequence.cs
@@ -63,5 +63,7 @@ public DatabaseSequence([NotNull] DatabaseModel database, [NotNull] string name)
/// Indicates whether or not the sequence will start over when the max value is reached, or null if not set.
///
public virtual bool? IsCyclic { get; set; }
+
+ public override string ToString() => Schema == null ? Name : $"{Schema}.{Name}";
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseTable.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseTable.cs
index c85a2098122..1c28c1fbc18 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseTable.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseTable.cs
@@ -68,5 +68,7 @@ public DatabaseTable([NotNull] DatabaseModel database, [NotNull] string name)
/// The list of foreign key constraints defined on the table.
///
public virtual IList ForeignKeys { get; }
+
+ public override string ToString() => Schema == null ? Name : $"{Schema}.{Name}";
}
}
diff --git a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseUniqueConstraint.cs b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseUniqueConstraint.cs
index aa91f333b10..7f24cc62a4a 100644
--- a/src/EFCore.Relational/Scaffolding/Metadata/DatabaseUniqueConstraint.cs
+++ b/src/EFCore.Relational/Scaffolding/Metadata/DatabaseUniqueConstraint.cs
@@ -35,5 +35,7 @@ public DatabaseUniqueConstraint([NotNull] DatabaseTable table, [CanBeNull] strin
/// The ordered list of columns that make up the constraint.
///
public virtual IList Columns { get; }
+
+ public override string ToString() => Name ?? "";
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/MigrationSqlGeneratorTestBase.cs b/test/EFCore.Relational.Specification.Tests/MigrationSqlGeneratorTestBase.cs
deleted file mode 100644
index d700103e8b2..00000000000
--- a/test/EFCore.Relational.Specification.Tests/MigrationSqlGeneratorTestBase.cs
+++ /dev/null
@@ -1,705 +0,0 @@
-// 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;
-using System.Linq;
-using Microsoft.EntityFrameworkCore.Metadata.Internal;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.Migrations.Operations;
-using Microsoft.EntityFrameworkCore.TestUtilities;
-using Microsoft.Extensions.DependencyInjection;
-using Xunit;
-
-// ReSharper disable ClassNeverInstantiated.Local
-// ReSharper disable InconsistentNaming
-namespace Microsoft.EntityFrameworkCore
-{
- public abstract class MigrationSqlGeneratorTestBase
- {
- protected static string EOL => Environment.NewLine;
-
- protected virtual string Sql { get; set; }
-
- [ConditionalFact]
- public virtual void CreateIndexOperation_with_filter_where_clause()
- => Generate(
- modelBuilder => modelBuilder.Entity("People").Property("Name").IsRequired(),
- new CreateIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Columns = new[] { "Name" },
- Filter = "[Name] IS NOT NULL"
- });
-
- [ConditionalFact]
- public virtual void CreateIndexOperation_with_filter_where_clause_and_is_unique()
- => Generate(
- modelBuilder => modelBuilder.Entity("People").Property("Name"),
- new CreateIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Columns = new[] { "Name" },
- IsUnique = true,
- Filter = "[Name] IS NOT NULL AND <> ''"
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_defaultValue()
- => Generate(
- new AddColumnOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "Name",
- ClrType = typeof(string),
- ColumnType = "varchar(30)",
- IsNullable = false,
- DefaultValue = "John Doe"
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_defaultValueSql()
- => Generate(
- new AddColumnOperation
- {
- Table = "People",
- Name = "Birthday",
- ClrType = typeof(DateTime),
- ColumnType = "date",
- IsNullable = true,
- DefaultValueSql = "CURRENT_TIMESTAMP"
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_without_column_type()
- => Generate(
- new AddColumnOperation
- {
- Table = "People",
- Name = "Alias",
- ClrType = typeof(string)
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_ansi()
- => Generate(
- modelBuilder => modelBuilder.Entity("Person").Property("Name").IsUnicode(false),
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- IsUnicode = false,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_unicode_overridden()
- => Generate(
- modelBuilder => modelBuilder.Entity("Person").Property("Name").IsUnicode(false),
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- IsUnicode = true,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_unicode_no_model()
- => Generate(
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- IsUnicode = false,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_fixed_length()
- => Generate(
- modelBuilder => modelBuilder.Entity("Person").Property("Name").HasMaxLength(100).IsFixedLength(),
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- IsUnicode = true,
- IsNullable = true,
- IsFixedLength = true,
- MaxLength = 100
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_fixed_length_no_model()
- => Generate(
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- IsUnicode = false,
- IsNullable = true,
- IsFixedLength = true,
- MaxLength = 100
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_maxLength()
- => Generate(
- modelBuilder => modelBuilder.Entity("Person").Property("Name").HasMaxLength(30),
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- MaxLength = 30,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_maxLength_overridden()
- => Generate(
- modelBuilder => modelBuilder.Entity("Person").Property("Name").HasMaxLength(30),
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- MaxLength = 32,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_maxLength_no_model()
- => Generate(
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- MaxLength = 30,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_maxLength_on_derived()
- => Generate(
- modelBuilder =>
- {
- modelBuilder.Entity("Person");
- modelBuilder.Entity(
- "SpecialPerson", b =>
- {
- b.HasBaseType("Person");
- b.Property("Name").HasMaxLength(30);
- });
-
- modelBuilder.Entity("MoreSpecialPerson").HasBaseType("SpecialPerson");
- },
- new AddColumnOperation
- {
- Table = "Person",
- Name = "Name",
- ClrType = typeof(string),
- MaxLength = 30,
- IsNullable = true
- });
-
- [ConditionalFact]
- public virtual void AddColumnOperation_with_shared_column()
- => Generate(
- modelBuilder =>
- {
- modelBuilder.Entity();
- modelBuilder.Entity();
- modelBuilder.Entity();
- },
- new AddColumnOperation
- {
- Table = "Base",
- Name = "Foo",
- ClrType = typeof(string),
- IsNullable = true
- });
-
- private class Base
- {
- // ReSharper disable once UnusedMember.Local
- public int Id { get; set; }
- }
-
- private class Derived1 : Base
- {
- // ReSharper disable once UnusedMember.Local
- public string Foo { get; set; }
- }
-
- private class Derived2 : Base
- {
- // ReSharper disable once UnusedMember.Local
- public string Foo { get; set; }
- }
-
- [ConditionalFact]
- public virtual void AddForeignKeyOperation_with_name()
- => Generate(
- new AddForeignKeyOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "FK_People_Companies",
- Columns = new[] { "EmployerId1", "EmployerId2" },
- PrincipalTable = "Companies",
- PrincipalSchema = "hr",
- PrincipalColumns = new[] { "Id1", "Id2" },
- OnDelete = ReferentialAction.Cascade
- });
-
- [ConditionalFact]
- public virtual void AddForeignKeyOperation_without_name()
- => Generate(
- new AddForeignKeyOperation
- {
- Table = "People",
- Columns = new[] { "SpouseId" },
- PrincipalTable = "People",
- PrincipalColumns = new[] { "Id" }
- });
-
- [ConditionalFact]
- public virtual void AddForeignKeyOperation_without_principal_columns()
- => Generate(
- new AddForeignKeyOperation
- {
- Table = "People",
- Columns = new[] { "SpouseId" },
- PrincipalTable = "People"
- });
-
- [ConditionalFact]
- public virtual void AddPrimaryKeyOperation_with_name()
- => Generate(
- new AddPrimaryKeyOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "PK_People",
- Columns = new[] { "Id1", "Id2" }
- });
-
- [ConditionalFact]
- public virtual void AddPrimaryKeyOperation_without_name()
- => Generate(
- new AddPrimaryKeyOperation { Table = "People", Columns = new[] { "Id" } });
-
- [ConditionalFact]
- public virtual void AddUniqueConstraintOperation_with_name()
- => Generate(
- new AddUniqueConstraintOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "AK_People_DriverLicense",
- Columns = new[] { "DriverLicense_State", "DriverLicense_Number" }
- });
-
- [ConditionalFact]
- public virtual void AddUniqueConstraintOperation_without_name()
- => Generate(
- new AddUniqueConstraintOperation { Table = "People", Columns = new[] { "SSN" } });
-
- [ConditionalFact]
- public virtual void CreateCheckConstraintOperation_with_name()
- => Generate(
- new CreateCheckConstraintOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "CK_People_DriverLicense",
- Sql = "DriverLicense_Number > 0"
- });
-
- [ConditionalFact]
- public virtual void AlterColumnOperation()
- => Generate(
- new AlterColumnOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "LuckyNumber",
- ClrType = typeof(int),
- ColumnType = "int",
- IsNullable = false,
- DefaultValue = 7
- });
-
- [ConditionalFact]
- public virtual void AlterColumnOperation_without_column_type()
- => Generate(
- new AlterColumnOperation
- {
- Table = "People",
- Name = "LuckyNumber",
- ClrType = typeof(int)
- });
-
- [ConditionalFact]
- public virtual void AlterSequenceOperation_with_minValue_and_maxValue()
- => Generate(
- new AlterSequenceOperation
- {
- Name = "EntityFrameworkHiLoSequence",
- Schema = "dbo",
- IncrementBy = 1,
- MinValue = 2,
- MaxValue = 816,
- IsCyclic = true
- });
-
- [ConditionalFact]
- public virtual void AlterSequenceOperation_without_minValue_and_maxValue()
- => Generate(
- new AlterSequenceOperation { Name = "EntityFrameworkHiLoSequence", IncrementBy = 1 });
-
- [ConditionalFact]
- public virtual void RenameTableOperation_legacy()
- => Generate(
- new RenameTableOperation
- {
- Name = "People",
- Schema = "dbo",
- NewName = "Person"
- });
-
- [ConditionalFact]
- public virtual void RenameTableOperation()
- => Generate(
- modelBuilder => modelBuilder.HasAnnotation(CoreAnnotationNames.ProductVersion, "2.1.0"),
- new RenameTableOperation
- {
- Name = "People",
- Schema = "dbo",
- NewName = "Person",
- NewSchema = "dbo"
- });
-
- [ConditionalFact]
- public virtual void CreateIndexOperation_unique()
- => Generate(
- new CreateIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Schema = "dbo",
- Columns = new[] { "FirstName", "LastName" },
- IsUnique = true
- });
-
- [ConditionalFact]
- public virtual void CreateIndexOperation_nonunique()
- => Generate(
- new CreateIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Columns = new[] { "Name" },
- IsUnique = false
- });
-
- [ConditionalFact]
- public virtual void CreateIndexOperation_with_where_clauses()
- => Generate(
- new CreateIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Columns = new[] { "Name" },
- IsUnique = false,
- Filter = "[Id] > 2"
- });
-
- [ConditionalFact]
- public virtual void CreateSequenceOperation_with_minValue_and_maxValue()
- => Generate(
- new CreateSequenceOperation
- {
- Name = "EntityFrameworkHiLoSequence",
- Schema = "dbo",
- StartValue = 3,
- IncrementBy = 1,
- MinValue = 2,
- MaxValue = 816,
- ClrType = typeof(long),
- IsCyclic = true
- });
-
- [ConditionalFact]
- public virtual void CreateSequenceOperation_with_minValue_and_maxValue_not_long()
- => Generate(
- new CreateSequenceOperation
- {
- Name = "EntityFrameworkHiLoSequence",
- Schema = "dbo",
- StartValue = 3,
- IncrementBy = 1,
- MinValue = 2,
- MaxValue = 816,
- ClrType = typeof(int),
- IsCyclic = true
- });
-
- [ConditionalFact]
- public virtual void CreateSequenceOperation_without_minValue_and_maxValue()
- => Generate(
- new CreateSequenceOperation
- {
- Name = "EntityFrameworkHiLoSequence",
- ClrType = typeof(long),
- StartValue = 3,
- IncrementBy = 1
- });
-
- [ConditionalFact]
- public virtual void CreateTableOperation()
- => Generate(
- new CreateTableOperation
- {
- Name = "People",
- Schema = "dbo",
- Columns =
- {
- new AddColumnOperation
- {
- Name = "Id",
- Table = "People",
- ClrType = typeof(int),
- IsNullable = false
- },
- new AddColumnOperation
- {
- Name = "EmployerId",
- Table = "People",
- ClrType = typeof(int),
- IsNullable = true,
- Comment = "Employer ID comment"
- },
- new AddColumnOperation
- {
- Name = "SSN",
- Table = "People",
- ClrType = typeof(string),
- ColumnType = "char(11)",
- IsNullable = true
- }
- },
- PrimaryKey = new AddPrimaryKeyOperation { Columns = new[] { "Id" } },
- UniqueConstraints = { new AddUniqueConstraintOperation { Columns = new[] { "SSN" } } },
- CheckConstraints = { new CreateCheckConstraintOperation { Sql = "SSN > 0" } },
- ForeignKeys =
- {
- new AddForeignKeyOperation
- {
- Columns = new[] { "EmployerId" },
- PrincipalTable = "Companies",
- PrincipalColumns = new[] { "Id" }
- }
- },
- Comment = "Table comment"
- });
-
- [ConditionalFact]
- public virtual void CreateTableOperation_no_key()
- => Generate(
- new CreateTableOperation
- {
- Name = "Anonymous",
- Columns =
- {
- new AddColumnOperation
- {
- Name = "Value",
- Table = "Anonymous",
- ClrType = typeof(int),
- IsNullable = false
- }
- }
- });
-
- [ConditionalFact]
- public virtual void DropColumnOperation()
- => Generate(
- new DropColumnOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "LuckyNumber"
- });
-
- [ConditionalFact]
- public virtual void DropForeignKeyOperation()
- => Generate(
- new DropForeignKeyOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "FK_People_Companies"
- });
-
- [ConditionalFact]
- public virtual void DropIndexOperation()
- => Generate(
- new DropIndexOperation
- {
- Name = "IX_People_Name",
- Table = "People",
- Schema = "dbo"
- });
-
- [ConditionalFact]
- public virtual void DropPrimaryKeyOperation()
- => Generate(
- new DropPrimaryKeyOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "PK_People"
- });
-
- [ConditionalFact]
- public virtual void DropSequenceOperation()
- => Generate(
- new DropSequenceOperation { Name = "EntityFrameworkHiLoSequence", Schema = "dbo" });
-
- [ConditionalFact]
- public virtual void DropTableOperation()
- => Generate(
- new DropTableOperation { Name = "People", Schema = "dbo" });
-
- [ConditionalFact]
- public virtual void DropUniqueConstraintOperation()
- => Generate(
- new DropUniqueConstraintOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "AK_People_SSN"
- });
-
- [ConditionalFact]
- public virtual void DropCheckConstraintOperation()
- => Generate(
- new DropCheckConstraintOperation
- {
- Table = "People",
- Schema = "dbo",
- Name = "CK_People_SSN"
- });
-
- [ConditionalFact]
- public virtual void SqlOperation()
- => Generate(
- new SqlOperation { Sql = "-- I <3 DDL" });
-
- [ConditionalFact]
- public virtual void InsertDataOperation()
- => Generate(
- new InsertDataOperation
- {
- Table = "People",
- Columns = new[] { "Id", "Full Name" },
- Values = new object[,]
- {
- { 0, null }, { 1, "Daenerys Targaryen" }, { 2, "John Snow" }, { 3, "Arya Stark" }, { 4, "Harry Strickland" }
- }
- });
-
- [ConditionalFact]
- public virtual void DeleteDataOperation_simple_key()
- => Generate(
- new DeleteDataOperation
- {
- Table = "People",
- KeyColumns = new[] { "Id" },
- KeyValues = new object[,] { { 2 }, { 4 } }
- });
-
- [ConditionalFact]
- public virtual void DeleteDataOperation_composite_key()
- => Generate(
- new DeleteDataOperation
- {
- Table = "People",
- KeyColumns = new[] { "First Name", "Last Name" },
- KeyValues = new object[,] { { "Hodor", null }, { "Daenerys", "Targaryen" } }
- });
-
- [ConditionalFact]
- public virtual void UpdateDataOperation_simple_key()
- => Generate(
- new UpdateDataOperation
- {
- Table = "People",
- KeyColumns = new[] { "Id" },
- KeyValues = new object[,] { { 1 }, { 4 } },
- Columns = new[] { "Full Name" },
- Values = new object[,] { { "Daenerys Stormborn" }, { "Homeless Harry Strickland" } }
- });
-
- [ConditionalFact]
- public virtual void UpdateDataOperation_composite_key()
- => Generate(
- new UpdateDataOperation
- {
- Table = "People",
- KeyColumns = new[] { "Id", "Last Name" },
- KeyValues = new object[,] { { 0, null }, { 4, "Strickland" } },
- Columns = new[] { "First Name" },
- Values = new object[,] { { "Hodor" }, { "Harry" } }
- });
-
- [ConditionalFact]
- public virtual void UpdateDataOperation_multiple_columns()
- => Generate(
- new UpdateDataOperation
- {
- Table = "People",
- KeyColumns = new[] { "Id" },
- KeyValues = new object[,] { { 1 }, { 4 } },
- Columns = new[] { "First Name", "Nickname" },
- Values = new object[,] { { "Daenerys", "Dany" }, { "Harry", "Homeless" } }
- });
-
- protected TestHelpers TestHelpers { get; }
-
- protected MigrationSqlGeneratorTestBase(TestHelpers testHelpers)
- {
- TestHelpers = testHelpers;
- }
-
- protected virtual void Generate(params MigrationOperation[] operation)
- => Generate(_ => { }, operation);
-
- protected virtual void Generate(Action buildAction, params MigrationOperation[] operation)
- {
- var modelBuilder = TestHelpers.CreateConventionBuilder();
- modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
- buildAction(modelBuilder);
-
- var batch = TestHelpers.CreateContextServices().GetRequiredService()
- .Generate(operation, modelBuilder.Model);
-
- Sql = string.Join(
- "GO" + EOL + EOL,
- batch.Select(b => b.CommandText));
- }
-
- protected void AssertSql(string expected)
- => Assert.Equal(expected, Sql, ignoreLineEndingDifferences: true);
- }
-}
diff --git a/test/EFCore.Relational.Specification.Tests/MigrationsFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/MigrationsFixtureBase.cs
deleted file mode 100644
index 17fdb2ce673..00000000000
--- a/test/EFCore.Relational.Specification.Tests/MigrationsFixtureBase.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-// 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 Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Migrations;
-using Microsoft.EntityFrameworkCore.TestUtilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore
-{
- public abstract class MigrationsFixtureBase : SharedStoreFixtureBase
- {
- public static string ActiveProvider { get; set; }
- public new RelationalTestStore TestStore => (RelationalTestStore)base.TestStore;
- protected override string StoreName { get; } = "MigrationsTest";
-
- public EmptyMigrationsContext CreateEmptyContext()
- => new EmptyMigrationsContext(
- TestStore.AddProviderOptions(
- new DbContextOptionsBuilder())
- .UseInternalServiceProvider(
- TestStoreFactory.AddProviderServices(
- new ServiceCollection())
- .BuildServiceProvider())
- .Options);
-
- public new virtual MigrationsContext CreateContext() => base.CreateContext();
-
- public class EmptyMigrationsContext : DbContext
- {
- public EmptyMigrationsContext(DbContextOptions options)
- : base(options)
- {
- }
- }
-
- public class MigrationsContext : PoolableDbContext
- {
- public MigrationsContext(DbContextOptions options)
- : base(options)
- {
- }
-
- public DbSet Foos { get; set; }
- }
-
- public class Foo
- {
- public int Id { get; set; }
- }
-
- [DbContext(typeof(MigrationsContext))]
- [Migration("00000000000001_Migration1")]
- private class Migration1 : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- MigrationsFixtureBase.ActiveProvider = migrationBuilder.ActiveProvider;
-
- migrationBuilder
- .CreateTable(
- name: "Table1",
- columns: x => new { Id = x.Column(), Foo = x.Column() })
- .PrimaryKey(
- name: "PK_Table1",
- columns: x => x.Id);
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- => migrationBuilder.DropTable("Table1");
- }
-
- [DbContext(typeof(MigrationsContext))]
- [Migration("00000000000002_Migration2")]
- private class Migration2 : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- => migrationBuilder.RenameColumn(
- name: "Foo",
- table: "Table1",
- newName: "Bar");
-
- protected override void Down(MigrationBuilder migrationBuilder)
- => migrationBuilder.RenameColumn(
- name: "Bar",
- table: "Table1",
- newName: "Foo");
- }
-
- [DbContext(typeof(MigrationsContext))]
- [Migration("00000000000003_Migration3")]
- private class Migration3 : Migration
- {
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- if (ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
- {
- migrationBuilder.Sql("CREATE DATABASE TransactionSuppressed;", suppressTransaction: true);
- migrationBuilder.Sql("DROP DATABASE TransactionSuppressed;", suppressTransaction: true);
- }
- }
-
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- }
- }
- }
-}
diff --git a/test/EFCore.Relational.Specification.Tests/MigrationsInfrastructureTestBase.cs b/test/EFCore.Relational.Specification.Tests/MigrationsInfrastructureTestBase.cs
new file mode 100644
index 00000000000..0db3e8a7452
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/MigrationsInfrastructureTestBase.cs
@@ -0,0 +1,408 @@
+// 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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.Extensions.DependencyInjection;
+using Xunit;
+
+// ReSharper disable InconsistentNaming
+namespace Microsoft.EntityFrameworkCore
+{
+ public abstract class MigrationsInfrastructureTestBase : IClassFixture
+ where TFixture : MigrationsFixtureBase, new()
+ {
+ protected TFixture Fixture { get; }
+
+ protected MigrationsInfrastructureTestBase(TFixture fixture)
+ {
+ Fixture = fixture;
+ Fixture.TestStore.CloseConnection();
+ }
+
+ protected string Sql { get; private set; }
+
+ protected string ActiveProvider { get; private set; }
+
+ // Database deletion can happen as async file operation and SQLClient
+ // doesn't account for this, so give some time for it to happen on slow C.I. machines
+ protected virtual void GiveMeSomeTime(DbContext db)
+ {
+ var stillExists = true;
+ for (var i = 0; stillExists && i < 10; i++)
+ {
+ try
+ {
+ Thread.Sleep(500);
+
+ stillExists = db.GetService().Exists();
+ }
+ catch
+ {
+ }
+ }
+ }
+
+ protected virtual async Task GiveMeSomeTimeAsync(DbContext db)
+ {
+ var stillExists = true;
+ for (var i = 0; stillExists && i < 10; i++)
+ {
+ try
+ {
+ await Task.Delay(500);
+
+ stillExists = await db.GetService().ExistsAsync();
+ }
+ catch
+ {
+ }
+ }
+ }
+
+ [ConditionalFact]
+ public virtual void Can_apply_all_migrations()
+ {
+ using var db = Fixture.CreateContext();
+ db.Database.EnsureDeleted();
+
+ GiveMeSomeTime(db);
+
+ db.Database.Migrate();
+
+ var history = db.GetService();
+ Assert.Collection(
+ history.GetAppliedMigrations(),
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
+ x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
+ x => Assert.Equal("00000000000003_Migration3", x.MigrationId));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_apply_one_migration()
+ {
+ using var db = Fixture.CreateContext();
+ db.Database.EnsureDeleted();
+
+ GiveMeSomeTime(db);
+
+ var migrator = db.GetService();
+ migrator.Migrate("Migration1");
+
+ var history = db.GetService();
+ Assert.Collection(
+ history.GetAppliedMigrations(),
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_revert_all_migrations()
+ {
+ using var db = Fixture.CreateContext();
+ db.Database.EnsureDeleted();
+
+ GiveMeSomeTime(db);
+
+ db.Database.Migrate();
+
+ var migrator = db.GetService();
+ migrator.Migrate(Migration.InitialDatabase);
+
+ var history = db.GetService();
+ Assert.Empty(history.GetAppliedMigrations());
+ }
+
+ [ConditionalFact]
+ public virtual void Can_revert_one_migrations()
+ {
+ using var db = Fixture.CreateContext();
+ db.Database.EnsureDeleted();
+
+ GiveMeSomeTime(db);
+
+ db.Database.Migrate();
+
+ var migrator = db.GetService();
+ migrator.Migrate("Migration1");
+
+ var history = db.GetService();
+ Assert.Collection(
+ history.GetAppliedMigrations(),
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId));
+ }
+
+ [ConditionalFact]
+ public virtual async Task Can_apply_all_migrations_async()
+ {
+ using var db = Fixture.CreateContext();
+ await db.Database.EnsureDeletedAsync();
+
+ await GiveMeSomeTimeAsync(db);
+
+ await db.Database.MigrateAsync();
+
+ var history = db.GetService();
+ Assert.Collection(
+ await history.GetAppliedMigrationsAsync(),
+ x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
+ x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
+ x => Assert.Equal("00000000000003_Migration3", x.MigrationId));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_no_migration_script()
+ {
+ using var db = Fixture.CreateEmptyContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript());
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_migration_from_initial_database_to_initial()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript(fromMigration: Migration.InitialDatabase, toMigration: Migration.InitialDatabase));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_up_scripts()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript());
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_one_up_script()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript(fromMigration: "00000000000001_Migration1", toMigration: "00000000000002_Migration2"));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_up_script_using_names()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript(fromMigration: "Migration1", toMigration: "Migration2"));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_idempotent_up_scripts()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(migrator.GenerateScript(idempotent: true));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_down_scripts()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(
+ migrator.GenerateScript(
+ fromMigration: "Migration2",
+ toMigration: Migration.InitialDatabase));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_one_down_script()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(
+ migrator.GenerateScript(
+ fromMigration: "00000000000002_Migration2",
+ toMigration: "00000000000001_Migration1"));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_down_script_using_names()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(
+ migrator.GenerateScript(
+ fromMigration: "Migration2",
+ toMigration: "Migration1"));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_generate_idempotent_down_scripts()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+
+ SetSql(
+ migrator.GenerateScript(
+ fromMigration: "Migration2",
+ toMigration: Migration.InitialDatabase,
+ idempotent: true));
+ }
+
+ [ConditionalFact]
+ public virtual void Can_get_active_provider()
+ {
+ using var db = Fixture.CreateContext();
+ var migrator = db.GetService();
+ MigrationsFixtureBase.ActiveProvider = null;
+
+ migrator.GenerateScript(toMigration: "Migration1");
+
+ ActiveProvider = MigrationsFixtureBase.ActiveProvider;
+ }
+
+ [ConditionalFact]
+ public abstract void Can_diff_against_2_2_model();
+
+ [ConditionalFact]
+ public abstract void Can_diff_against_3_0_ASP_NET_Identity_model();
+
+ [ConditionalFact]
+ public abstract void Can_diff_against_2_2_ASP_NET_Identity_model();
+
+ [ConditionalFact]
+ public abstract void Can_diff_against_2_1_ASP_NET_Identity_model();
+
+ protected virtual void DiffSnapshot(ModelSnapshot snapshot, DbContext context)
+ {
+ var sourceModel = ((IMutableModel)snapshot.Model).FinalizeModel();
+ var targetModel = context.Model;
+
+ var typeMapper = context.GetService();
+
+ foreach (var property in sourceModel.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties()))
+ {
+ Assert.NotNull(typeMapper.FindMapping(property));
+ }
+
+ foreach (var property in targetModel.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties()))
+ {
+ Assert.NotNull(typeMapper.FindMapping(property));
+ }
+
+ var modelDiffer = context.GetService();
+ var operations = modelDiffer.GetDifferences(sourceModel, targetModel);
+
+ Assert.Equal(0, operations.Count);
+ }
+
+ private void SetSql(string value) => Sql = value.Replace(ProductInfo.GetVersion(), "7.0.0-test");
+ }
+
+ public abstract class MigrationsFixtureBase : SharedStoreFixtureBase
+ {
+ public static string ActiveProvider { get; set; }
+ public new RelationalTestStore TestStore => (RelationalTestStore)base.TestStore;
+ protected override string StoreName { get; } = "MigrationsTest";
+
+ public EmptyMigrationsContext CreateEmptyContext()
+ => new EmptyMigrationsContext(
+ TestStore.AddProviderOptions(
+ new DbContextOptionsBuilder())
+ .UseInternalServiceProvider(
+ TestStoreFactory.AddProviderServices(
+ new ServiceCollection())
+ .BuildServiceProvider())
+ .Options);
+
+ public new virtual MigrationsContext CreateContext() => base.CreateContext();
+
+ public class EmptyMigrationsContext : DbContext
+ {
+ public EmptyMigrationsContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+ }
+
+ public class MigrationsContext : PoolableDbContext
+ {
+ public MigrationsContext(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ public DbSet Foos { get; set; }
+ }
+
+ public class Foo
+ {
+ public int Id { get; set; }
+ }
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000001_Migration1")]
+ private class Migration1 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ MigrationsFixtureBase.ActiveProvider = migrationBuilder.ActiveProvider;
+
+ migrationBuilder
+ .CreateTable(
+ name: "Table1",
+ columns: x => new { Id = x.Column(), Foo = x.Column() })
+ .PrimaryKey(
+ name: "PK_Table1",
+ columns: x => x.Id);
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ => migrationBuilder.DropTable("Table1");
+ }
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000002_Migration2")]
+ private class Migration2 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ => migrationBuilder.RenameColumn(
+ name: "Foo",
+ table: "Table1",
+ newName: "Bar");
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ => migrationBuilder.RenameColumn(
+ name: "Bar",
+ table: "Table1",
+ newName: "Foo");
+ }
+
+ [DbContext(typeof(MigrationsContext))]
+ [Migration("00000000000003_Migration3")]
+ private class Migration3 : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ if (ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
+ {
+ migrationBuilder.Sql("CREATE DATABASE TransactionSuppressed;", suppressTransaction: true);
+ migrationBuilder.Sql("DROP DATABASE TransactionSuppressed;", suppressTransaction: true);
+ }
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ }
+ }
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs b/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs
index 34757cac189..4867c874e89 100644
--- a/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/MigrationsTestBase.cs
@@ -2,409 +2,1452 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Data.Common;
+using System.Collections.Generic;
using System.Linq;
-using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Metadata.Conventions;
+using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Migrations.Operations;
+using Microsoft.EntityFrameworkCore.Scaffolding;
+using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
-// ReSharper disable InconsistentNaming
+#nullable enable
+
namespace Microsoft.EntityFrameworkCore
{
- public abstract class MigrationsTestBase : IClassFixture
- where TFixture : MigrationsFixtureBase, new()
+ public class MigrationsTestBase : IClassFixture
+ where TFixture : MigrationsTestBase.MigrationsFixtureBase, new()
{
+ private readonly ISqlGenerationHelper _sqlGenerationHelper;
+ private readonly IRelationalTypeMappingSource _typeMappingSource;
+
protected TFixture Fixture { get; }
protected MigrationsTestBase(TFixture fixture)
{
Fixture = fixture;
- Fixture.TestStore.CloseConnection();
+ _sqlGenerationHelper = Fixture.ServiceProvider.GetService();
+ _typeMappingSource = Fixture.ServiceProvider.GetService();
}
- protected string Sql { get; private set; }
-
- protected string ActiveProvider { get; private set; }
-
- // Database deletion can happen as async file operation and SQLClient
- // doesn't account for this, so give some time for it to happen on slow C.I. machines
- protected virtual void GiveMeSomeTime(DbContext db)
- {
- var stillExists = true;
- for (var i = 0; stillExists && i < 10; i++)
- {
- try
+ [ConditionalFact]
+ public virtual Task Create_table()
+ => Test(
+ builder => { },
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id").ValueGeneratedOnAdd();
+ e.Property("Name");
+ e.HasKey("Id");
+ }),
+ model =>
{
- Thread.Sleep(500);
+ var table = Assert.Single(model.Tables);
+ Assert.Collection(
+ table.Columns,
+ c => Assert.Equal("Id", c.Name),
+ c => Assert.Equal("Name", c.Name));
+ Assert.Same(
+ table.Columns.Single(c => c.Name == "Id"),
+ Assert.Single(table.PrimaryKey!.Columns));
+ });
- stillExists = db.GetService().Exists();
- }
- catch
+ [ConditionalFact]
+ public virtual async Task Create_table_all_settings()
+ {
+ var intStoreType = TypeMappingSource.FindMapping(typeof(int)).StoreType;
+ var char11StoreType = TypeMappingSource.FindMapping(typeof(string), storeTypeName: null, size: 11).StoreType;
+
+ await Test(
+ builder => builder.Entity(
+ "Employers", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ }),
+ builder => { },
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.ToTable("People", "dbo2");
+
+ e.Property("CustomId");
+ e.Property("EmployerId")
+ .HasComment("Employer ID comment");
+ e.Property("SSN")
+ .HasColumnType(char11StoreType)
+ .IsRequired(false);
+
+ e.HasKey("CustomId");
+ e.HasAlternateKey("SSN");
+ e.HasCheckConstraint("CK_SSN", $"{DelimitIdentifier("SSN")} > 0");
+ e.HasOne("Employers").WithMany("People").HasForeignKey("EmployerId");
+
+ e.HasComment("Table comment");
+ }),
+ model =>
{
- }
- }
+ var employersTable = Assert.Single(model.Tables, t => t.Name == "Employers");
+ var peopleTable = Assert.Single(model.Tables, t => t.Name == "People");
+
+ Assert.Equal("People", peopleTable.Name);
+ Assert.Equal("dbo2", peopleTable.Schema);
+
+ Assert.Collection(
+ peopleTable.Columns.OrderBy(c => c.Name),
+ c =>
+ {
+ Assert.Equal("CustomId", c.Name);
+ Assert.False(c.IsNullable);
+ Assert.Equal(intStoreType, c.StoreType);
+ Assert.Null(c.Comment);
+ },
+ c =>
+ {
+ Assert.Equal("EmployerId", c.Name);
+ Assert.False(c.IsNullable);
+ Assert.Equal(intStoreType, c.StoreType);
+ Assert.Equal("Employer ID comment", c.Comment);
+ },
+ c =>
+ {
+ Assert.Equal("SSN", c.Name);
+ Assert.False(c.IsNullable);
+ Assert.Equal(char11StoreType, c.StoreType);
+ Assert.Null(c.Comment);
+ });
+
+ Assert.Same(
+ peopleTable.Columns.Single(c => c.Name == "CustomId"),
+ Assert.Single(peopleTable.PrimaryKey!.Columns));
+ Assert.Same(
+ peopleTable.Columns.Single(c => c.Name == "SSN"),
+ Assert.Single(Assert.Single(peopleTable.UniqueConstraints).Columns));
+ // TODO: Need to scaffold check constraints, https://github.com/aspnet/EntityFrameworkCore/issues/15408
+
+ var foreignKey = Assert.Single(peopleTable.ForeignKeys);
+ Assert.Same(peopleTable, foreignKey.Table);
+ Assert.Same(peopleTable.Columns.Single(c => c.Name == "EmployerId"), Assert.Single(foreignKey.Columns));
+ Assert.Same(employersTable, foreignKey.PrincipalTable);
+ Assert.Same(employersTable.Columns.Single(), Assert.Single(foreignKey.PrincipalColumns));
+
+ Assert.Equal("Table comment", peopleTable.Comment);
+ });
}
- protected virtual async Task GiveMeSomeTimeAsync(DbContext db)
- {
- var stillExists = true;
- for (var i = 0; stillExists && i < 10; i++)
- {
- try
+ [ConditionalFact]
+ public virtual Task Create_table_no_key()
+ => Test(
+ builder => { },
+ builder => builder.Entity("Anonymous").Property("SomeColumn"),
+ model =>
{
- await Task.Delay(500);
+ var table = Assert.Single(model.Tables);
+ Assert.Null(table.PrimaryKey);
+ });
- stillExists = await db.GetService().ExistsAsync();
- }
- catch
+ [ConditionalFact]
+ public virtual Task Create_table_with_comments()
+ => Test(
+ builder => { },
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("Name").HasComment("Column comment");
+ e.HasComment("Table comment");
+ }),
+ model =>
{
- }
- }
- }
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("Table comment", table.Comment);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal("Column comment", column.Comment);
+ });
[ConditionalFact]
- public virtual void Can_apply_all_migrations()
+ public virtual Task Create_table_with_multiline_comments()
{
- using var db = Fixture.CreateContext();
- db.Database.EnsureDeleted();
+ var tableComment = @"This is a multi-line
+table comment.
+More information can
+be found in the docs.";
+ var columnComment = @"This is a multi-line
+column comment.
+More information can
+be found in the docs.";
+
+ return Test(
+ builder => { },
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("Name").HasComment(columnComment);
+ e.HasComment(tableComment);
+ }),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal(tableComment, table.Comment);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(columnComment, column.Comment);
+ });
+ }
- GiveMeSomeTime(db);
+ [ConditionalFact]
+ public virtual Task Alter_table_add_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").HasComment("Table comment"),
+ model => Assert.Equal("Table comment", Assert.Single(model.Tables).Comment));
- db.Database.Migrate();
+ [ConditionalFact]
+ public virtual Task Alter_table_add_comment_non_default_schema()
+ => Test(
+ builder => builder.Entity("People")
+ .ToTable("People", "SomeOtherSchema")
+ .Property("Id"),
+ builder => { },
+ builder => builder.Entity("People")
+ .ToTable("People", "SomeOtherSchema")
+ .HasComment("Table comment"),
+ model => Assert.Equal("Table comment", Assert.Single(model.Tables).Comment));
- var history = db.GetService();
- Assert.Collection(
- history.GetAppliedMigrations(),
- x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
- x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
- x => Assert.Equal("00000000000003_Migration3", x.MigrationId));
- }
+ [ConditionalFact]
+ public virtual Task Alter_table_change_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").HasComment("Table comment1"),
+ builder => builder.Entity("People").HasComment("Table comment2"),
+ model => Assert.Equal("Table comment2", Assert.Single(model.Tables).Comment));
[ConditionalFact]
- public virtual void Can_apply_one_migration()
- {
- using var db = Fixture.CreateContext();
- db.Database.EnsureDeleted();
+ public virtual Task Alter_table_remove_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").HasComment("Table comment1"),
+ builder => { },
+ model => Assert.Null(Assert.Single(model.Tables).Comment));
- GiveMeSomeTime(db);
+ [ConditionalFact]
+ public virtual Task Drop_table()
+ => Test(
+ builder => builder.Entity("People", e => e.Property("Id")),
+ builder => { },
+ model => Assert.Empty(model.Tables));
- var migrator = db.GetService();
- migrator.Migrate("Migration1");
+ [ConditionalFact]
+ public virtual Task Rename_table()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("people").Property("Id"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("people", table.Name);
+ });
- var history = db.GetService();
- Assert.Collection(
- history.GetAppliedMigrations(),
- x => Assert.Equal("00000000000001_Migration1", x.MigrationId));
- }
+ [ConditionalFact]
+ public virtual Task Rename_table_with_primary_key()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ }),
+ builder => builder.Entity(
+ "people", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ }),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("people", table.Name);
+ });
[ConditionalFact]
- public virtual void Can_revert_all_migrations()
- {
- using var db = Fixture.CreateContext();
- db.Database.EnsureDeleted();
+ public virtual Task Move_table()
+ => Test(
+ builder => builder.Entity("TestTable").Property("Id"),
+ builder => { },
+ builder => builder.Entity("TestTable").ToTable("TestTable", "TestTableSchema"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("TestTableSchema", table.Schema);
+ Assert.Equal("TestTable", table.Name);
+ });
- GiveMeSomeTime(db);
+ [ConditionalFact]
+ public virtual Task Create_schema()
+ => Test(
+ builder => { },
+ builder => builder.Entity("People")
+ .ToTable("People", "SomeOtherSchema")
+ .Property("Id"),
+ model => Assert.Equal("SomeOtherSchema", Assert.Single(model.Tables).Schema));
- db.Database.Migrate();
+ [ConditionalFact]
+ public virtual Task Add_column_with_defaultValue_string()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Name")
+ .IsRequired()
+ .HasDefaultValue("John Doe"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal(2, table.Columns.Count);
+ var nameColumn = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.False(nameColumn.IsNullable);
+ Assert.Contains("John Doe", nameColumn.DefaultValueSql);
+ });
- var migrator = db.GetService();
- migrator.Migrate(Migration.InitialDatabase);
+ [ConditionalFact]
+ public virtual Task Add_column_with_defaultValue_datetime()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Birthday")
+ .HasDefaultValue(new DateTime(2015, 4, 12, 17, 5, 0)),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal(2, table.Columns.Count);
+ var birthdayColumn = Assert.Single(table.Columns, c => c.Name == "Birthday");
+ Assert.False(birthdayColumn.IsNullable);
+ });
- var history = db.GetService();
- Assert.Empty(history.GetAppliedMigrations());
- }
+ [ConditionalFact]
+ public virtual Task Add_column_with_defaultValueSql()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Birthday")
+ .HasColumnType("date")
+ .HasDefaultValueSql("CURRENT_TIMESTAMP"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal(2, table.Columns.Count);
+ var nameColumn = Assert.Single(table.Columns, c => c.Name == "Birthday");
+ Assert.Equal("date", nameColumn.StoreType);
+ Assert.True(nameColumn.IsNullable);
+ Assert.Equal("(getdate())", nameColumn.DefaultValueSql);
+ });
[ConditionalFact]
- public virtual void Can_revert_one_migrations()
- {
- using var db = Fixture.CreateContext();
- db.Database.EnsureDeleted();
+ public virtual Task Add_column_with_computedSql()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ e.Property("LastName");
+ }),
+ builder => { },
+ builder => builder.Entity("People").Property("FullName").HasComputedColumnSql("FirstName + ' ' + LastName"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "FullName");
+ Assert.Contains("FirstName", column.ComputedColumnSql);
+ Assert.Contains("LastName", column.ComputedColumnSql);
+ });
- GiveMeSomeTime(db);
+ // TODO: Check this out
+ [ConditionalFact]
+ public virtual Task Add_column_with_required()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Name").IsRequired(),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(TypeMappingSource.FindMapping(typeof(string)).StoreType, column.StoreType);
+ Assert.False(column.IsNullable);
+ });
- db.Database.Migrate();
+ [ConditionalFact]
+ public virtual Task Add_column_with_ansi()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Name").IsUnicode(false),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(
+ TypeMappingSource
+ .FindMapping(typeof(string), storeTypeName: null, unicode: false)
+ .StoreType, column.StoreType);
+ Assert.True(column.IsNullable);
+ });
- var migrator = db.GetService();
- migrator.Migrate("Migration1");
+ [ConditionalFact]
+ public virtual Task Add_column_with_max_length()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Name").HasMaxLength(30),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(
+ TypeMappingSource
+ .FindMapping(typeof(string), storeTypeName: null, size: 30)
+ .StoreType,
+ column.StoreType);
+ });
- var history = db.GetService();
- Assert.Collection(
- history.GetAppliedMigrations(),
- x => Assert.Equal("00000000000001_Migration1", x.MigrationId));
- }
+ [ConditionalFact]
+ public virtual Task Add_column_with_max_length_on_derived()
+ => Test(
+ builder =>
+ {
+ builder.Entity("Person");
+ builder.Entity(
+ "SpecialPerson", e =>
+ {
+ e.HasBaseType("Person");
+ e.Property("Name").HasMaxLength(30);
+ });
+
+ builder.Entity("MoreSpecialPerson").HasBaseType("SpecialPerson");
+ },
+ builder => { },
+ builder => builder.Entity("Person").Property("Name").HasMaxLength(30),
+ model =>
+ {
+ var table = Assert.Single(model.Tables, t => t.Name == "Person");
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(
+ TypeMappingSource
+ .FindMapping(typeof(string), storeTypeName: null, size: 30)
+ .StoreType,
+ column.StoreType);
+ });
[ConditionalFact]
- public virtual async Task Can_apply_all_migrations_async()
- {
- using var db = Fixture.CreateContext();
- await db.Database.EnsureDeletedAsync();
+ public virtual Task Add_column_with_fixed_length()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("Name")
+ .IsFixedLength()
+ .HasMaxLength(100),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ Assert.Equal(
+ TypeMappingSource
+ .FindMapping(typeof(string), storeTypeName: null, fixedLength: true, size: 100)
+ .StoreType,
+ column.StoreType);
+ });
- await GiveMeSomeTimeAsync(db);
+ [ConditionalFact]
+ public virtual Task Add_column_with_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => { },
+ builder => builder.Entity("People").Property("FullName").HasComment("My comment"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "FullName");
+ Assert.Equal("My comment", column.Comment);
+ });
- await db.Database.MigrateAsync();
+ [ConditionalFact]
+ public virtual Task Add_column_shared()
+ => Test(
+ builder =>
+ {
+ builder.Entity("Base").Property("Id");
+ builder.Entity("Derived1").Property("Foo");
+ builder.Entity("Derived2").Property("Foo");
+ },
+ builder => { },
+ builder => builder.Entity("Base").Property("Foo"),
+ model =>
+ {
+ // var table = Assert.Single(model.Tables);
+ // var column = Assert.Single(table.Columns, c => c.Name == "Name");
+ // Assert.Equal("nvarchar(30)", column.StoreType);
+ });
- var history = db.GetService();
- Assert.Collection(
- await history.GetAppliedMigrationsAsync(),
- x => Assert.Equal("00000000000001_Migration1", x.MigrationId),
- x => Assert.Equal("00000000000002_Migration2", x.MigrationId),
- x => Assert.Equal("00000000000003_Migration3", x.MigrationId));
- }
+ [ConditionalFact]
+ public virtual Task Alter_column_change_type()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "SomeColumn");
+ Assert.Equal(_typeMappingSource.FindMapping(typeof(long)).StoreType, column.StoreType);
+ });
[ConditionalFact]
- public virtual void Can_generate_no_migration_script()
- {
- using var db = Fixture.CreateEmptyContext();
- var migrator = db.GetService();
+ public virtual Task Alter_column_make_required()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("SomeColumn");
+ }),
+ builder => { },
+ builder => builder.Entity("People").Property("SomeColumn").IsRequired(),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name != "Id");
+ Assert.False(column.IsNullable);
+ });
- SetSql(migrator.GenerateScript());
- }
+ [ConditionalFact]
+ public virtual Task Alter_column_make_required_with_index()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("SomeColumn");
+ e.HasIndex("SomeColumn");
+ }),
+ builder => { },
+ builder => builder.Entity("People").Property("SomeColumn").IsRequired(),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name != "Id");
+ Assert.False(column.IsNullable);
+ var index = Assert.Single(table.Indexes);
+ Assert.Same(column, Assert.Single(index.Columns));
+ });
[ConditionalFact]
- public virtual void Can_generate_migration_from_initial_database_to_initial()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Alter_column_make_required_with_composite_index()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ e.Property("LastName");
+ e.HasIndex("FirstName", "LastName");
+ }),
+ builder => { },
+ builder => builder.Entity("People").Property("FirstName").IsRequired(),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var firstNameColumn = Assert.Single(table.Columns, c => c.Name == "FirstName");
+ Assert.False(firstNameColumn.IsNullable);
+ var index = Assert.Single(table.Indexes);
+ Assert.Equal(2, index.Columns.Count);
+ Assert.Contains(table.Columns.Single(c => c.Name == "FirstName"), index.Columns);
+ Assert.Contains(table.Columns.Single(c => c.Name == "LastName"), index.Columns);
+ });
- SetSql(migrator.GenerateScript(fromMigration: Migration.InitialDatabase, toMigration: Migration.InitialDatabase));
- }
+ [ConditionalFact]
+ public virtual Task Alter_column_make_computed()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ e.Property("LastName");
+ }),
+ builder => builder.Entity("People").Property("FullName"),
+ builder => builder.Entity("People").Property("FullName").HasComputedColumnSql("FirstName + ' ' + LastName"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "FullName");
+ Assert.Contains("FirstName", column.ComputedColumnSql);
+ Assert.Contains("LastName", column.ComputedColumnSql);
+ });
[ConditionalFact]
- public virtual void Can_generate_up_scripts()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Alter_column_change_computed()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ e.Property("LastName");
+ e.Property("FullName");
+ }),
+ builder => builder.Entity("People").Property("FullName").HasComputedColumnSql("FirstName + ' ' + LastName"),
+ builder => builder.Entity("People").Property("FullName").HasComputedColumnSql("FirstName + ', ' + LastName"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns, c => c.Name == "FullName");
+ Assert.Contains("FirstName", column.ComputedColumnSql);
+ Assert.Contains("LastName", column.ComputedColumnSql);
+ });
- SetSql(migrator.GenerateScript());
- }
+ [ConditionalFact]
+ public virtual Task Alter_column_add_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("Id").HasComment("Some comment"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns);
+ Assert.Equal("Some comment", column.Comment);
+ });
[ConditionalFact]
- public virtual void Can_generate_one_up_script()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Alter_column_change_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id").HasComment("Some comment1"),
+ builder => builder.Entity("People").Property("Id").HasComment("Some comment2"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns);
+ Assert.Equal("Some comment2", column.Comment);
+ });
- SetSql(migrator.GenerateScript(fromMigration: "00000000000001_Migration1", toMigration: "00000000000002_Migration2"));
- }
+ [ConditionalFact]
+ public virtual Task Alter_column_remove_comment()
+ => Test(
+ builder => builder.Entity("People").Property("Id").HasComment("Some comment"),
+ builder => builder.Entity("People").Property("Id"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var column = Assert.Single(table.Columns);
+ Assert.Null(column.Comment);
+ });
[ConditionalFact]
- public virtual void Can_generate_up_script_using_names()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Drop_column()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => { },
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("Id", Assert.Single(table.Columns).Name);
+ });
- SetSql(migrator.GenerateScript(fromMigration: "Migration1", toMigration: "Migration2"));
- }
+ [ConditionalFact]
+ public virtual Task Drop_column_primary_key()
+ => Test(
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ }),
+ builder => { },
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal("SomeColumn", Assert.Single(table.Columns).Name);
+ });
[ConditionalFact]
- public virtual void Can_generate_idempotent_up_scripts()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Rename_column()
+ => Test(
+ builder => builder.Entity("People").Property("Id"),
+ builder => builder.Entity("People").Property("SomeColumn"),
+ builder => builder.Entity("People").Property("somecolumn"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ Assert.Equal(2, table.Columns.Count);
+ Assert.Single(table.Columns, c => c.Name == "somecolumn");
+ });
- SetSql(migrator.GenerateScript(idempotent: true));
- }
+ [ConditionalFact]
+ public virtual Task Create_index()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasIndex("FirstName"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var index = Assert.Single(table.Indexes);
+ Assert.Same(table, index.Table);
+ Assert.Same(table.Columns.Single(c => c.Name == "FirstName"), Assert.Single(index.Columns));
+ Assert.Equal("IX_People_FirstName", index.Name);
+ Assert.False(index.IsUnique);
+ Assert.Null(index.Filter);
+ });
[ConditionalFact]
- public virtual void Can_generate_down_scripts()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Create_index_unique()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ e.Property("LastName");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasIndex("FirstName", "LastName").IsUnique(),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var index = Assert.Single(table.Indexes);
+ Assert.True(index.IsUnique);
+ });
- SetSql(
- migrator.GenerateScript(
- fromMigration: "Migration2",
- toMigration: Migration.InitialDatabase));
- }
+ [ConditionalFact]
+ public virtual Task Create_index_with_filter()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasIndex("Name")
+ .HasFilter($"{DelimitIdentifier("Name")} IS NOT NULL"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var index = Assert.Single(table.Indexes);
+ Assert.Same(table.Columns.Single(c => c.Name == "Name"), Assert.Single(index.Columns));
+ Assert.Contains("Name", index.Filter);
+ });
[ConditionalFact]
- public virtual void Can_generate_one_down_script()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Create_unique_index_with_filter()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasIndex("Name").IsUnique()
+ .HasFilter($"{DelimitIdentifier("Name")} IS NOT NULL AND {DelimitIdentifier("Name")} <> ''"),
+ model => Assert.Contains("Name", model.Tables.Single().Indexes.Single().Filter));
- SetSql(
- migrator.GenerateScript(
- fromMigration: "00000000000002_Migration2",
- toMigration: "00000000000001_Migration1"));
- }
+ [ConditionalFact]
+ public virtual Task Drop_index()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("SomeField");
+ }),
+ builder => builder.Entity("People").HasIndex("SomeField"),
+ builder => { },
+ model => Assert.Empty(Assert.Single(model.Tables).Indexes));
[ConditionalFact]
- public virtual void Can_generate_down_script_using_names()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
+ public virtual Task Rename_index()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("FirstName");
+ }),
+ builder => builder.Entity("People").HasIndex("FirstName").HasName("Foo"),
+ builder => builder.Entity("People").HasIndex("FirstName").HasName("foo"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var index = Assert.Single(table.Indexes);
+ Assert.Equal("foo", index.Name);
+ });
- SetSql(
- migrator.GenerateScript(
- fromMigration: "Migration2",
- toMigration: "Migration1"));
- }
+ [ConditionalFact]
+ public virtual Task Add_primary_key()
+ => Test(
+ builder => builder.Entity("People").Property("SomeField"),
+ builder => { },
+ builder => builder.Entity("People").HasKey("SomeField"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var primaryKey = table.PrimaryKey;
+ Assert.NotNull(primaryKey);
+ Assert.Same(table, primaryKey!.Table);
+ Assert.Same(table.Columns.Single(), Assert.Single(primaryKey.Columns));
+ Assert.Equal("PK_People", primaryKey.Name);
+ });
[ConditionalFact]
- public virtual void Can_generate_idempotent_down_scripts()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
-
- SetSql(
- migrator.GenerateScript(
- fromMigration: "Migration2",
- toMigration: Migration.InitialDatabase,
- idempotent: true));
- }
+ public virtual Task Add_primary_key_with_name()
+ => Test(
+ builder => builder.Entity("People").Property("SomeField"),
+ builder => { },
+ builder => builder.Entity("People").HasKey("SomeField").HasName("PK_Foo"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var primaryKey = table.PrimaryKey;
+ Assert.NotNull(primaryKey);
+ Assert.Same(table, primaryKey!.Table);
+ Assert.Same(table.Columns.Single(), Assert.Single(primaryKey.Columns));
+ Assert.Equal("PK_Foo", primaryKey.Name);
+ });
[ConditionalFact]
- public virtual void Can_get_active_provider()
- {
- using var db = Fixture.CreateContext();
- var migrator = db.GetService();
- MigrationsFixtureBase.ActiveProvider = null;
+ public virtual Task Add_primary_key_composite_with_name()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("SomeField1");
+ e.Property("SomeField2");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasKey("SomeField1", "SomeField2").HasName("PK_Foo"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var primaryKey = table.PrimaryKey;
+ Assert.NotNull(primaryKey);
+ Assert.Same(table, primaryKey!.Table!);
+ Assert.Collection(
+ primaryKey.Columns,
+ c => Assert.Same(table.Columns[0], c),
+ c => Assert.Same(table.Columns[1], c));
+ Assert.Equal("PK_Foo", primaryKey.Name);
+ });
- migrator.GenerateScript(toMigration: "Migration1");
+ [ConditionalFact]
+ public virtual Task Drop_primary_key()
+ => Test(
+ builder => builder.Entity("People").Property("SomeField"),
+ builder => builder.Entity("People").HasKey("SomeField"),
+ builder => { },
+ model => Assert.Null(Assert.Single(model.Tables).PrimaryKey));
- ActiveProvider = MigrationsFixtureBase.ActiveProvider;
- }
+ [ConditionalFact]
+ public virtual Task Add_foreign_key()
+ => Test(
+ builder =>
+ {
+ builder.Entity(
+ "Customers", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ });
+ builder.Entity(
+ "Orders", e =>
+ {
+ e.Property("Id");
+ e.Property("CustomerId");
+ });
+ },
+ builder => { },
+ builder => builder.Entity("Orders").HasOne("Customers").WithMany()
+ .HasForeignKey("CustomerId"),
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables, t => t.Name == "Customers");
+ var ordersTable = Assert.Single(model.Tables, t => t.Name == "Orders");
+ var foreignKey = ordersTable.ForeignKeys.Single();
+ Assert.Equal("FK_Orders_Customers_CustomerId", foreignKey.Name);
+ Assert.Equal(ReferentialAction.NoAction, foreignKey.OnDelete);
+ Assert.Same(customersTable, foreignKey.PrincipalTable);
+ Assert.Same(customersTable.Columns.Single(), Assert.Single(foreignKey.PrincipalColumns));
+ Assert.Equal("CustomerId", Assert.Single(foreignKey.Columns).Name);
+ });
- ///
- /// Creating databases and executing DDL is slow. This oddly-structured test allows us to get the most amount of
- /// coverage using the least amount of database operations.
- ///
[ConditionalFact]
- public virtual async Task Can_execute_operations()
- {
- using var db = Fixture.CreateContext();
- await db.Database.EnsureDeletedAsync();
+ public virtual Task Add_foreign_key_with_name()
+ => Test(
+ builder =>
+ {
+ builder.Entity(
+ "Customers", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ });
+ builder.Entity(
+ "Orders", e =>
+ {
+ e.Property("Id");
+ e.Property("CustomerId");
+ });
+ },
+ builder => { },
+ builder => builder.Entity("Orders").HasOne("Customers").WithMany()
+ .HasForeignKey("CustomerId").HasConstraintName("FK_Foo"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables, t => t.Name == "Orders");
+ var foreignKey = table.ForeignKeys.Single();
+ Assert.Equal("FK_Foo", foreignKey.Name);
+ });
- await GiveMeSomeTimeAsync(db);
+ [ConditionalFact]
+ public virtual Task Drop_foreign_key()
+ => Test(
+ builder =>
+ {
+ builder.Entity(
+ "Customers", e =>
+ {
+ e.Property("Id");
+ e.HasKey("Id");
+ });
+ builder.Entity(
+ "Orders", e =>
+ {
+ e.Property("Id");
+ e.Property("CustomerId");
+ });
+ },
+ builder => builder.Entity("Orders").HasOne("Customers").WithMany().HasForeignKey("CustomerId"),
+ builder => { },
+ model =>
+ {
+ var customersTable = Assert.Single(model.Tables, t => t.Name == "Customers");
+ Assert.Empty(customersTable.ForeignKeys);
+ });
- await db.Database.EnsureCreatedAsync();
+ [ConditionalFact]
+ public virtual Task Add_unique_constraint()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("AlternateKeyColumn");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasAlternateKey("AlternateKeyColumn"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var uniqueConstraint = table.UniqueConstraints.Single();
+ Assert.Same(table, uniqueConstraint.Table);
+ Assert.Same(table.Columns.Single(c => c.Name == "AlternateKeyColumn"), Assert.Single(uniqueConstraint.Columns));
+ Assert.Equal("AK_People_AlternateKeyColumn", uniqueConstraint.Name);
+ });
- var services = db.GetInfrastructure();
- var connection = db.Database.GetDbConnection();
+ [ConditionalFact]
+ public virtual Task Add_unique_constraint_composite_with_name()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("AlternateKeyColumn1");
+ e.Property("AlternateKeyColumn2");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasAlternateKey("AlternateKeyColumn1", "AlternateKeyColumn2").HasName("AK_Foo"),
+ model =>
+ {
+ var table = Assert.Single(model.Tables);
+ var uniqueConstraint = table.UniqueConstraints.Single();
+ Assert.Same(table, uniqueConstraint.Table);
+ Assert.Collection(
+ uniqueConstraint.Columns,
+ c => Assert.Same(table.Columns.Single(c => c.Name == "AlternateKeyColumn1"), c),
+ c => Assert.Same(table.Columns.Single(c => c.Name == "AlternateKeyColumn2"), c));
+ Assert.Equal("AK_Foo", uniqueConstraint.Name);
+ });
- await db.Database.OpenConnectionAsync();
+ [ConditionalFact]
+ public virtual Task Drop_unique_constraint()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("AlternateKeyColumn");
+ }),
+ builder => builder.Entity("People").HasAlternateKey("AlternateKeyColumn"),
+ builder => { },
+ model =>
+ {
+ Assert.Empty(Assert.Single(model.Tables).UniqueConstraints);
+ });
- try
- {
- await ExecuteAsync(services, BuildFirstMigration);
- await AssertFirstMigrationAsync(connection);
- await ExecuteAsync(services, BuildSecondMigration);
- await AssertSecondMigrationAsync(connection);
- }
- finally
- {
- await db.Database.CloseConnectionAsync();
- }
- }
+ [ConditionalFact]
+ public virtual Task Add_check_constraint_with_name()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("DriverLicense");
+ }),
+ builder => { },
+ builder => builder.Entity("People").HasCheckConstraint("CK_Foo", $"{DelimitIdentifier("DriverLicense")} > 0"),
+ model =>
+ {
+ // TODO: no scaffolding support for check constraints, https://github.com/aspnet/EntityFrameworkCore/issues/15408
+ });
- protected virtual Task ExecuteAsync(IServiceProvider services, Action buildMigration)
- {
- var generator = services.GetRequiredService();
- var executor = services.GetRequiredService();
- var connection = services.GetRequiredService();
- var databaseProvider = services.GetRequiredService();
+ [ConditionalFact]
+ public virtual Task Drop_check_constraint()
+ => Test(
+ builder => builder.Entity(
+ "People", e =>
+ {
+ e.Property("Id");
+ e.Property("DriverLicense");
+ }),
+ builder => builder.Entity("People").HasCheckConstraint("CK_Foo", $"{DelimitIdentifier("DriverLicense")} > 0"),
+ builder => { },
+ model =>
+ {
+ // TODO: no scaffolding support for check constraints, https://github.com/aspnet/EntityFrameworkCore/issues/15408
+ });
- var migrationBuilder = new MigrationBuilder(databaseProvider.Name);
- buildMigration(migrationBuilder);
- var operations = migrationBuilder.Operations.ToList();
+ [ConditionalFact]
+ public virtual Task Create_sequence()
+ => Test(
+ builder => { },
+ builder => builder.HasSequence("TestSequence"),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal("TestSequence", sequence.Name);
+ });
- var commandList = generator.Generate(operations);
+ [ConditionalFact]
+ public virtual Task Create_sequence_all_settings()
+ => Test(
+ builder => { },
+ builder => builder.HasSequence("TestSequence", "dbo2")
+ .StartsAt(3)
+ .IncrementsBy(2)
+ .HasMin(2)
+ .HasMax(916)
+ .IsCyclic(),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal("TestSequence", sequence.Name);
+ Assert.Equal("dbo2", sequence.Schema);
+ Assert.Equal(3, sequence.StartValue);
+ Assert.Equal(2, sequence.IncrementBy);
+ Assert.Equal(2, sequence.MinValue);
+ Assert.Equal(916, sequence.MaxValue);
+ Assert.True(sequence.IsCyclic);
+ });
- return executor.ExecuteNonQueryAsync(commandList, connection);
- }
+ [ConditionalFact]
+ public virtual Task Alter_sequence_all_settings()
+ => Test(
+ builder => builder.HasSequence("foo"),
+ builder => { },
+ builder => builder.HasSequence("foo")
+ .StartsAt(-3)
+ .IncrementsBy(2)
+ .HasMin(-5)
+ .HasMax(10)
+ .IsCyclic(),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal(-3, sequence.StartValue);
+ Assert.Equal(2, sequence.IncrementBy);
+ Assert.Equal(-5, sequence.MinValue);
+ Assert.Equal(10, sequence.MaxValue);
+ Assert.True(sequence.IsCyclic);
+ });
[ConditionalFact]
- public abstract void Can_diff_against_2_2_model();
+ public virtual Task Alter_sequence_increment_by()
+ => Test(
+ builder => builder.HasSequence("foo"),
+ builder => { },
+ builder => builder.HasSequence("foo").IncrementsBy(2),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal(2, sequence.IncrementBy);
+ });
[ConditionalFact]
- public abstract void Can_diff_against_3_0_ASP_NET_Identity_model();
+ public virtual Task Drop_sequence()
+ => Test(
+ builder => builder.HasSequence("TestSequence"),
+ builder => { },
+ model => Assert.Empty(model.Sequences));
[ConditionalFact]
- public abstract void Can_diff_against_2_2_ASP_NET_Identity_model();
+ public virtual Task Rename_sequence()
+ => Test(
+ builder => builder.HasSequence("TestSequence"),
+ builder => builder.HasSequence("testsequence"),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal("testsequence", sequence.Name);
+ });
[ConditionalFact]
- public abstract void Can_diff_against_2_1_ASP_NET_Identity_model();
+ public virtual Task Move_sequence()
+ => Test(
+ builder => builder.HasSequence("TestSequence"),
+ builder => builder.HasSequence("TestSequence", "TestSequenceSchema"),
+ model =>
+ {
+ var sequence = Assert.Single(model.Sequences);
+ Assert.Equal("TestSequenceSchema", sequence.Schema);
+ Assert.Equal("TestSequence", sequence.Name);
+ });
- protected virtual void DiffSnapshot(ModelSnapshot snapshot, DbContext context)
- {
- var sourceModel = ((IMutableModel)snapshot.Model).FinalizeModel();
- var targetModel = context.Model;
+ [ConditionalFact]
+ public virtual Task InsertDataOperation()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ e.HasKey("Id");
+ }),
+ builder => { },
+ builder => builder.Entity("Person")
+ .HasData(
+ new Person { Id = 1, Name = "Daenerys Targaryen" },
+ new Person { Id = 2, Name = "John Snow" },
+ new Person { Id = 3, Name = "Arya Stark" },
+ new Person { Id = 4, Name = "Harry Strickland" },
+ new Person { Id = 5, Name = null }),
+ model => { });
- var typeMapper = context.GetService();
+ [ConditionalFact]
+ public virtual Task DeleteDataOperation_simple_key()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ e.HasKey("Id");
+ e.HasData(new Person { Id = 1, Name = "Daenerys Targaryen" });
+ }),
+ builder => builder.Entity("Person").HasData(new Person { Id = 2, Name = "John Snow" }),
+ builder => { },
+ model => { });
- foreach (var property in sourceModel.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties()))
- {
- Assert.NotNull(typeMapper.FindMapping(property));
- }
+ [ConditionalFact]
+ public virtual Task DeleteDataOperation_composite_key()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("AnotherId");
+ e.HasKey("Id", "AnotherId");
+ e.Property("Name");
+ e.HasData(
+ new Person
+ {
+ Id = 1,
+ AnotherId = 11,
+ Name = "Daenerys Targaryen"
+ });
+ }),
+ builder => builder.Entity("Person").HasData(
+ new Person
+ {
+ Id = 2,
+ AnotherId = 12,
+ Name = "John Snow"
+ }),
+ builder => { },
+ model => { });
- foreach (var property in targetModel.GetEntityTypes().SelectMany(e => e.GetDeclaredProperties()))
- {
- Assert.NotNull(typeMapper.FindMapping(property));
- }
+ [ConditionalFact]
+ public virtual Task UpdateDataOperation_simple_key()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ e.HasKey("Id");
+ e.HasData(new Person { Id = 1, Name = "Daenerys Targaryen" });
+ }),
+ builder => builder.Entity("Person").HasData(new Person { Id = 2, Name = "John Snow" }),
+ builder => builder.Entity("Person").HasData(new Person { Id = 2, Name = "Another John Snow" }),
+ model => { });
- var modelDiffer = context.GetService();
- var operations = modelDiffer.GetDifferences(sourceModel, targetModel);
+ [ConditionalFact]
+ public virtual Task UpdateDataOperation_composite_key()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("AnotherId");
+ e.HasKey("Id", "AnotherId");
+ e.Property("Name");
+ e.HasData(
+ new Person
+ {
+ Id = 1,
+ AnotherId = 11,
+ Name = "Daenerys Targaryen"
+ });
+ }),
+ builder => builder.Entity("Person").HasData(
+ new Person
+ {
+ Id = 2,
+ AnotherId = 11,
+ Name = "John Snow"
+ }),
+ builder => builder.Entity("Person").HasData(
+ new Person
+ {
+ Id = 2,
+ AnotherId = 11,
+ Name = "Another John Snow"
+ }),
+ model => { });
- Assert.Equal(0, operations.Count);
- }
+ [ConditionalFact]
+ public virtual Task UpdateDataOperation_multiple_columns()
+ => Test(
+ builder => builder.Entity(
+ "Person", e =>
+ {
+ e.Property("Id");
+ e.Property("Name");
+ e.Property("Age");
+ e.HasKey("Id");
+ e.HasData(
+ new Person
+ {
+ Id = 1,
+ Name = "Daenerys Targaryen",
+ Age = 18
+ });
+ }),
+ builder => builder.Entity("Person").HasData(
+ new Person
+ {
+ Id = 2,
+ Name = "John Snow",
+ Age = 20
+ }),
+ builder => builder.Entity("Person").HasData(
+ new Person
+ {
+ Id = 2,
+ Name = "Another John Snow",
+ Age = 21
+ }),
+ model => { });
- protected virtual void BuildFirstMigration(MigrationBuilder migrationBuilder)
+ [ConditionalFact]
+ public virtual async Task SqlOperation()
{
- migrationBuilder.CreateTable(
- name: "CreatedTable",
- columns: x => new
+ await Test(
+ builder => { },
+ new SqlOperation { Sql = "-- I <3 DDL" },
+ model =>
{
- Id = x.Column(),
- ColumnWithDefaultToDrop = x.Column(nullable: true, defaultValue: 0),
- ColumnWithDefaultToAlter = x.Column(nullable: true, defaultValue: 1)
- },
- constraints: x =>
- {
- x.PrimaryKey(
- name: "PK_CreatedTable",
- columns: t => t.Id);
+ Assert.Empty(model.Tables);
+ Assert.Empty(model.Sequences);
});
+
+ AssertSql(
+ @"-- I <3 DDL");
}
- protected virtual Task AssertFirstMigrationAsync(DbConnection connection)
+ private class Person
{
- AssertFirstMigration(connection);
-
- return Task.FromResult(0);
+ public int Id { get; set; }
+ public int AnotherId { get; set; }
+ public string? Name { get; set; }
+ public int Age { get; set; }
}
- protected virtual void AssertFirstMigration(DbConnection connection)
+ protected virtual DbContext CreateContext() => Fixture.CreateContext();
+
+ protected virtual string DelimitIdentifier(string unquotedIdentifier)
+ => _sqlGenerationHelper?.DelimitIdentifier(unquotedIdentifier)
+ ?? throw new InvalidOperationException(
+ $"No ISqlGenerationHelper singleton was found, consider overriding {nameof(DelimitIdentifier)}");
+
+ protected virtual IRelationalTypeMappingSource TypeMappingSource
+ => _typeMappingSource
+ ?? throw new InvalidOperationException(
+ $"No IRelationalTypeMappingSource singleton was found, consider overriding {nameof(TypeMappingSource)}");
+
+ protected virtual Task Test(
+ Action buildSourceAction,
+ Action buildTargetAction,
+ Action? asserter)
+ => Test(b => { }, buildSourceAction, buildTargetAction, asserter);
+
+ protected virtual Task Test(
+ Action buildCommonAction,
+ Action buildSourceAction,
+ Action buildTargetAction,
+ Action? asserter)
{
+ // Build the source and target models. Add current/latest product version if one wasn't set.
+ var sourceModelBuilder = CreateConventionlessModelBuilder();
+ buildCommonAction(sourceModelBuilder);
+ buildSourceAction(sourceModelBuilder);
+ var sourceModel = sourceModelBuilder.FinalizeModel();
+
+ var targetModelBuilder = CreateConventionlessModelBuilder();
+ buildCommonAction(targetModelBuilder);
+ buildTargetAction(targetModelBuilder);
+ var targetModel = targetModelBuilder.FinalizeModel();
+
+ var context = CreateContext();
+ var serviceProvider = ((IInfrastructure)context).Instance;
+ var modelDiffer = serviceProvider.GetRequiredService();
+
+ var operations = modelDiffer.GetDifferences(sourceModel, targetModel);
+
+ return Test(sourceModel, targetModel, operations, asserter);
}
- protected virtual void BuildSecondMigration(MigrationBuilder migrationBuilder)
+ protected virtual Task Test(
+ Action buildSourceAction,
+ MigrationOperation operation,
+ Action? asserter)
+ => Test(buildSourceAction, new[] { operation }, asserter);
+
+ protected virtual Task Test(
+ Action buildSourceAction,
+ IReadOnlyList operations,
+ Action? asserter)
{
- migrationBuilder.DropColumn(
- name: "ColumnWithDefaultToDrop",
- table: "CreatedTable");
- migrationBuilder.AlterColumn(
- name: "ColumnWithDefaultToAlter",
- table: "CreatedTable",
- nullable: true);
+ var sourceModelBuilder = CreateConventionlessModelBuilder();
+ buildSourceAction(sourceModelBuilder);
+ if (sourceModelBuilder.Model.GetProductVersion() is null)
+ {
+ sourceModelBuilder.Model.SetProductVersion(ProductInfo.GetVersion());
+ }
+
+ var sourceModel = sourceModelBuilder.FinalizeModel();
+
+ return Test(sourceModel, targetModel: null, operations, asserter);
}
- protected virtual Task AssertSecondMigrationAsync(DbConnection connection)
+ protected virtual async Task Test(
+ IModel sourceModel,
+ IModel? targetModel,
+ IReadOnlyList operations,
+ Action