Skip to content

Instantly share code, notes, and snippets.

@fubar-coder
Last active August 23, 2021 01:43
Show Gist options
  • Save fubar-coder/1e8ba2c34ad95ae1cea3939b4e73e647 to your computer and use it in GitHub Desktop.
Save fubar-coder/1e8ba2c34ad95ae1cea3939b4e73e647 to your computer and use it in GitHub Desktop.
using Your.CustomFluentMigratorClasses;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCustomFluentMigratorSqliteGenerator(
this IServiceCollection services)
{
return services.AddScoped<CustomSqliteGenerator>()
.AddScoped<IMigrationGenerator>(sp => sp.GetRequiredService<CustomSqliteGenerator>());
}
}
}
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using FluentMigrator.Model;
using FluentMigrator.Runner.Generators.Base;
using FluentMigrator.Runner.Generators.SQLite;
namespace Your.CustomFluentMigratorClasses
{
/// <summary>
/// An almost 1:1 copy from the FM project
/// </summary>
internal class CustomSqliteColumn : ColumnBase
{
public CustomSqliteColumn()
: base(new CustomSqliteTypeMap(), new SQLiteQuoter())
{
}
/// <inheritdoc />
public override string Generate(IEnumerable<ColumnDefinition> columns, string tableName)
{
var colDefs = columns.ToList();
var foreignKeyColumns = colDefs.Where(x => x.IsForeignKey && x.ForeignKey != null);
var foreignKeyClauses = foreignKeyColumns
.Select(x => ", " + FormatForeignKey(x.ForeignKey, GenerateForeignKeyName));
// Append foreign key definitions after all column definitions and the primary key definition
return base.Generate(colDefs, tableName) + string.Concat(foreignKeyClauses);
}
/// <inheritdoc />
public override bool ShouldPrimaryKeysBeAddedSeparately(IEnumerable<ColumnDefinition> primaryKeyColumns)
{
// If there are no identity column then we can add as a separate constraint
var pkColDefs = primaryKeyColumns.ToList();
return !pkColDefs.Any(x => x.IsIdentity) && pkColDefs.Any(x => x.IsPrimaryKey);
}
/// <inheritdoc />
protected override string FormatIdentity(ColumnDefinition column)
{
// SQLite only supports the concept of Identity in combination with a single primary key
// see: http://www.sqlite.org/syntaxdiagrams.html#column-constraint syntax details
if (column.IsIdentity && !column.IsPrimaryKey && column.Type != DbType.Int32)
{
throw new ArgumentException("SQLite only supports identity on single integer, primary key coulmns");
}
return string.Empty;
}
/// <inheritdoc />
protected override string FormatPrimaryKey(ColumnDefinition column)
{
if (!column.IsPrimaryKey)
{
return string.Empty;
}
return column.IsIdentity ? "PRIMARY KEY AUTOINCREMENT" : string.Empty;
}
}
}
using FluentMigrator.Expressions;
using FluentMigrator.Runner.Generators;
using FluentMigrator.Runner.Generators.Generic;
using FluentMigrator.Runner.Generators.SQLite;
using Microsoft.Extensions.Options;
namespace Your.CustomFluentMigratorClasses
{
/// <summary>
/// An almost 1:1 copy from the FM project
/// </summary>
public class CustomSqliteGenerator : GenericGenerator
{
public CustomSqliteGenerator()
: this(new SQLiteQuoter())
{
}
public CustomSqliteGenerator(
SQLiteQuoter quoter)
: this(quoter, new OptionsWrapper<GeneratorOptions>(new GeneratorOptions()))
{
}
public CustomSqliteGenerator(
SQLiteQuoter quoter,
IOptions<GeneratorOptions> generatorOptions)
: base(new CustomSqliteColumn(), quoter, new EmptyDescriptionGenerator(), generatorOptions)
{
}
public override string RenameTable => "ALTER TABLE {0} RENAME TO {1}";
public override string Generate(AlterColumnExpression expression)
{
return CompatibilityMode.HandleCompatibilty("SQLite does not support alter column");
}
public override string Generate(RenameColumnExpression expression)
{
return CompatibilityMode.HandleCompatibilty("SQLite does not support renaming of columns");
}
public override string Generate(DeleteColumnExpression expression)
{
return CompatibilityMode.HandleCompatibilty("SQLite does not support deleting of columns");
}
public override string Generate(AlterDefaultConstraintExpression expression)
{
return CompatibilityMode.HandleCompatibilty("SQLite does not support altering of default constraints");
}
public override string Generate(CreateForeignKeyExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Foreign keys are not supported in SQLite");
}
public override string Generate(DeleteForeignKeyExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Foreign keys are not supported in SQLite");
}
public override string Generate(CreateSequenceExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Sequences are not supported in SQLite");
}
public override string Generate(DeleteSequenceExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Sequences are not supported in SQLite");
}
public override string Generate(DeleteDefaultConstraintExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Default constraints are not supported");
}
public override string Generate(CreateConstraintExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Constraints are not supported");
}
public override string Generate(DeleteConstraintExpression expression)
{
return CompatibilityMode.HandleCompatibilty("Constraints are not supported");
}
}
}
using System.Data;
using FluentMigrator.Runner.Generators.Base;
namespace Your.CustomFluentMigratorClasses
{
internal class CustomSqliteTypeMap : TypeMapBase
{
public const int AnsiStringCapacity = 8000;
public const int AnsiTextCapacity = 2147483647;
public const int UnicodeStringCapacity = 4000;
public const int UnicodeTextCapacity = 1073741823;
public const int ImageCapacity = 2147483647;
public const int DecimalCapacity = 19;
public const int XmlCapacity = 1073741823;
public override string GetTypeMap(DbType type, int? size, int? precision)
{
return base.GetTypeMap(type, size: null, precision: null);
}
protected override void SetupTypeMaps()
{
SetTypeMap(DbType.Binary, "BLOB");
SetTypeMap(DbType.Byte, "INTEGER");
SetTypeMap(DbType.Int16, "INTEGER");
SetTypeMap(DbType.Int32, "INTEGER");
SetTypeMap(DbType.Int64, "INTEGER");
SetTypeMap(DbType.SByte, "INTEGER");
SetTypeMap(DbType.UInt16, "INTEGER");
SetTypeMap(DbType.UInt32, "INTEGER");
SetTypeMap(DbType.UInt64, "INTEGER");
SetTypeMap(DbType.Currency, "NUMERIC");
SetTypeMap(DbType.Decimal, "NUMERIC");
SetTypeMap(DbType.Double, "NUMERIC");
SetTypeMap(DbType.Single, "NUMERIC");
SetTypeMap(DbType.VarNumeric, "NUMERIC");
SetTypeMap(DbType.AnsiString, "TEXT");
SetTypeMap(DbType.String, "TEXT");
SetTypeMap(DbType.AnsiStringFixedLength, "TEXT");
SetTypeMap(DbType.StringFixedLength, "TEXT");
SetTypeMap(DbType.Date, "DATETIME");
SetTypeMap(DbType.DateTime, "DATETIME");
SetTypeMap(DbType.DateTime2, "DATETIME");
SetTypeMap(DbType.DateTimeOffset, "DATETIME");
SetTypeMap(DbType.Time, "DATETIME");
// This is the relevant change to get "BOOLEAN" instead of "INTEGER"
SetTypeMap(DbType.Boolean, "BOOLEAN");
SetTypeMap(DbType.Guid, "UNIQUEIDENTIFIER");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment