mirror of
https://github.com/azaion/annotations.git
synced 2026-04-22 21:56:31 +00:00
4780e8c61c
fix schema migrator for enums
97 lines
3.3 KiB
C#
97 lines
3.3 KiB
C#
using System.Data;
|
|
using LinqToDB.Data;
|
|
using LinqToDB.Mapping;
|
|
|
|
namespace Azaion.Common.Database;
|
|
|
|
public static class SchemaMigrator
|
|
{
|
|
public static void EnsureSchemaUpdated(DataConnection dbConnection, params Type[] entityTypes)
|
|
{
|
|
var connection = dbConnection.Connection;
|
|
var mappingSchema = dbConnection.MappingSchema;
|
|
|
|
if (connection.State == ConnectionState.Closed)
|
|
{
|
|
connection.Open();
|
|
}
|
|
|
|
foreach (var type in entityTypes)
|
|
{
|
|
var entityDescriptor = mappingSchema.GetEntityDescriptor(type);
|
|
var tableName = entityDescriptor.Name.Name;
|
|
var existingColumns = GetTableColumns(connection, tableName);
|
|
|
|
foreach (var column in entityDescriptor.Columns)
|
|
{
|
|
if (existingColumns.Contains(column.ColumnName, StringComparer.OrdinalIgnoreCase))
|
|
continue;
|
|
|
|
var columnDefinition = GetColumnDefinition(column);
|
|
dbConnection.Execute($"ALTER TABLE {tableName} ADD COLUMN {columnDefinition}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static HashSet<string> GetTableColumns(IDbConnection connection, string tableName)
|
|
{
|
|
var columns = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
using var cmd = connection.CreateCommand();
|
|
cmd.CommandText = $"PRAGMA table_info({tableName})";
|
|
using var reader = cmd.ExecuteReader();
|
|
while (reader.Read())
|
|
columns.Add(reader.GetString(1)); // "name" is in the second column
|
|
|
|
return columns;
|
|
}
|
|
|
|
private static string GetColumnDefinition(ColumnDescriptor column)
|
|
{
|
|
var type = column.MemberType;
|
|
var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
|
|
var sqliteType = GetSqliteType(underlyingType);
|
|
var defaultClause = GetSqlDefaultValue(type, underlyingType);
|
|
|
|
return $"\"{column.ColumnName}\" {sqliteType} {defaultClause}";
|
|
}
|
|
|
|
private static string GetSqliteType(Type type) =>
|
|
type switch
|
|
{
|
|
_ when type == typeof(int)
|
|
|| type == typeof(long)
|
|
|| type == typeof(bool)
|
|
|| type.IsEnum
|
|
=> "INTEGER",
|
|
|
|
_ when type == typeof(double)
|
|
|| type == typeof(float)
|
|
|| type == typeof(decimal)
|
|
=> "REAL",
|
|
|
|
_ when type == typeof(byte[])
|
|
=> "BLOB",
|
|
|
|
_ => "TEXT"
|
|
};
|
|
|
|
private static string GetSqlDefaultValue(Type originalType, Type underlyingType)
|
|
{
|
|
var isNullable = originalType.IsClass || Nullable.GetUnderlyingType(originalType) != null;
|
|
if (isNullable)
|
|
return "NULL";
|
|
|
|
var defaultValue = Activator.CreateInstance(underlyingType);
|
|
|
|
if (underlyingType == typeof(bool))
|
|
return $"NOT NULL DEFAULT {(Convert.ToBoolean(defaultValue) ? 1 : 0)}";
|
|
|
|
if (underlyingType.IsEnum)
|
|
return $"NOT NULL DEFAULT {(int)defaultValue}";
|
|
|
|
if (underlyingType.IsValueType && defaultValue is IFormattable f)
|
|
return $"NOT NULL DEFAULT {f.ToString(null, System.Globalization.CultureInfo.InvariantCulture)}";
|
|
|
|
return $"NOT NULL DEFAULT '{defaultValue}'";
|
|
}
|
|
} |