Created
November 7, 2024 20:33
-
-
Save Espleth/60c7f53a663e3b594d1757a131fa049a to your computer and use it in GitHub Desktop.
EF Core Code-first extension to migrate C# enums from ints in Postgres to PG enums
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.EntityFrameworkCore.Migrations; | |
using Npgsql.NameTranslation; | |
namespace Celestial.Database.Extensions; | |
public static class MigrationsExtensions | |
{ | |
/// <summary> | |
/// Convert int column to PG enum column using c# enum which was previously mapped to this column | |
/// </summary> | |
public static void AlterIntToEnum<TEnum>(this MigrationBuilder migrationBuilder, string tableName, string columnName) | |
where TEnum : Enum | |
{ | |
// Get TEnum name as postgres enum name (e.g. from "UserType" to "user_type") | |
var pgEnumType = NpgsqlSnakeCaseNameTranslator.ConvertToSnakeCase(typeof(TEnum).Name); | |
var tempColumnName = $"{columnName}_temp"; | |
// Get list with all enum values | |
var allEnumValues = Enum.GetValues(typeof(TEnum)).Cast<TEnum>().ToList(); | |
var defaultValue = allEnumValues.First(); | |
// Add temp column to store PG enum values | |
migrationBuilder.AddColumn<TEnum>( | |
name: tempColumnName, | |
table: tableName, | |
type: pgEnumType, | |
nullable: false, | |
defaultValue: defaultValue); | |
var defaultPgValue = NpgsqlSnakeCaseNameTranslator.ConvertToSnakeCase(defaultValue.ToString()); | |
// Write sql code for "CASE" statement to convert int to PG enums | |
var sqlCaseCode = string.Join(" ", allEnumValues.Select(x => | |
$"WHEN {Convert.ToInt32(x)} THEN '{NpgsqlSnakeCaseNameTranslator.ConvertToSnakeCase(x.ToString())}'")); | |
// Update temporary columns with enum values | |
migrationBuilder.Sql($"UPDATE \"{tableName}\" " + | |
$"SET \"{tempColumnName}\" = CASE \"{columnName}\" " + | |
$"{sqlCaseCode} ELSE '{defaultPgValue}' " + | |
$"END::{pgEnumType};"); | |
// Drop original column with int values | |
migrationBuilder.DropColumn( | |
name: columnName, | |
table: tableName); | |
// Rename temp column to original column name | |
migrationBuilder.RenameColumn( | |
name: tempColumnName, | |
table: tableName, | |
newName: columnName); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For schema specific cases: