Note: this post on based on EF Core 5.x.
  In this post, let's see how we can add EF database migration for a table
  that has System-Versioning enabled. (If you don't know what System-Versioning
  enabled tables are in Microsoft SQL Server, you can read
  Temporal tables for more information, but if you landed here in this post, it's very
  likely you already know what that is).
  In my case, I wanted to add a computed column to a table that has System
  Versioning enabled. So if I just try to add a column to the target table, when
  applying migrations EF will throw an error, something like
  "System-versioned table schema modification failed because adding computed
    column while system-versioning is ON is not supported". So here what we need is to first turn off the system-versioning, do the necessary schema changes and finally turn on the system-versioning back again.
  So let's go by an example. I am adding a new computed column to my target
  table PropertyAccount and it's already System-Versioned.
public class PropertyAccountConfiguration : IEntityTypeConfiguration<PropertyAccount>
{
public void Configure(EntityTypeBuilder<PropertyAccount> builder)
{
// some other configuration
builder
.Property(x => x.AccountNumber)
.HasComputedColumnSql("RIGHT('000000'+CAST(Id AS VARCHAR(6)),6)")
.HasMaxLength(6);
}
}
  So now if I add a migration, EF will generate something like this.
public partial class PT_AddAccountNumberToPropertyAccount : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccount",
type: "nvarchar(6)",
maxLength: 6,
nullable: true,
computedColumnSql: "RIGHT('000000'+CAST(Id AS VARCHAR(6)),6)");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccount");
}
}
  But if we try to apply the migration, it's going to throw the above error.
  So now we need to turn off the system-versioning, add the column to target
  table and its History table, and then turn on the system-versioning
  again, something like below.
public partial class PT_AddAccountNumberToPropertyAccount : Migration
{
private const string PropertyAccountTableName = "pt.PropertyAccount";
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DisableSystemVersioning(PropertyAccountTableName);
migrationBuilder.AddColumn<string>(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccount",
type: "nvarchar(6)",
maxLength: 6,
nullable: true,
computedColumnSql: "RIGHT('000000'+CAST(Id AS VARCHAR(6)),6)");
migrationBuilder.AddColumn<string>(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccountHistory",
type: "varchar(6)",
maxLength: 6,
nullable: true);
migrationBuilder.EnableSystemVersioning(PropertyAccountTableName);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DisableSystemVersioning(PropertyAccountTableName);
migrationBuilder.DropColumn(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccount");
migrationBuilder.DropColumn(
name: "AccountNumber",
schema: "pt",
table: "PropertyAccountHistory");
migrationBuilder.EnableSystemVersioning(PropertyAccountTableName);
}
}
  The 2 methods DisableSystemVersioning and EnableSystemVersioning
  are some two extension methods I have created on MigrationBuilder.
public static class MigrationBuilderExtensions
{
public static void DisableSystemVersioning(this MigrationBuilder migrationBuilder, string tableName)
{
if (string.IsNullOrEmpty(tableName))
{
throw new ArgumentNullException(nameof(tableName));
}
migrationBuilder.Sql($"ALTER TABLE {tableName} SET (SYSTEM_VERSIONING = OFF);");
}
public static void EnableSystemVersioning(this MigrationBuilder migrationBuilder, string tableName, string historyTableName = null)
{
if (string.IsNullOrEmpty(tableName))
{
throw new ArgumentNullException(nameof(tableName));
}
if (string.IsNullOrEmpty(historyTableName))
{
historyTableName = $"{tableName}History";
}
migrationBuilder.Sql(@$"ALTER TABLE {tableName} SET
(
SYSTEM_VERSIONING = ON
( HISTORY_TABLE = {historyTableName})
);
");
}
}
And this works like a charm. 
Hope this helps.
Regards,
Jaliya
Jaliya
 
 
Very useful, thanks.
ReplyDelete