Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
EF Core - Create table if not exists
using Microsoft.EntityFrameworkCore.Migrations.Operations;
namespace Gist.Extensions.Sql
{
internal sealed class CreateTableIfNotExistsOperation : CreateTableOperation
{
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
namespace Gist.Extensions.Sql
{
public sealed class CustomMigrationsSqlGenerator : SqliteMigrationsSqlGenerator
{
public CustomMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IRelationalAnnotationProvider migrationsAnnotations)
: base(dependencies,
migrationsAnnotations)
{
}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation is CreateTableIfNotExistsOperation createTableIfNotExistsOperation)
{
Generate(createTableIfNotExistsOperation, model, builder);
}
else
{
base.Generate(operation, model, builder);
}
}
private void Generate(CreateTableIfNotExistsOperation operation, IModel model, MigrationCommandListBuilder builder,
bool terminate = true)
{
builder
.Append("CREATE TABLE IF NOT EXISTS ")
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema))
.AppendLine(" (");
using (builder.Indent())
{
CreateTableColumns(operation, model, builder);
CreateTableConstraints(operation, model, builder);
builder.AppendLine();
}
builder.Append(")");
if (terminate)
{
builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator);
EndStatement(builder);
}
}
}
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Gist.Extensions.Sql
{
public class GistDbContext : DbContext {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
optionsBuilder
.UseSqlite($"Data Source={path};")
.ReplaceService<IMigrationsSqlGenerator, CustomMigrationsSqlGenerator>(); //This line is required
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
namespace Gist.Extensions.Sql
{
public static class MigrationExtensions
{
public static CreateTableBuilder<TColumns> CreateTableIfNotExists<TColumns>(
this MigrationBuilder migrationBuilder,
[NotNull] string name,
[NotNull] Func<ColumnsBuilder, TColumns> columns,
string schema = null,
Action<CreateTableBuilder<TColumns>> constraints = null,
string comment = null)
{
var createTableOperation = new CreateTableIfNotExistsOperation
{
Schema = schema,
Name = name,
Comment = comment
};
var columnsBuilder = new ColumnsBuilder(createTableOperation);
var columnsObject = columns(columnsBuilder);
var columnMap = new Dictionary<PropertyInfo, AddColumnOperation>();
foreach (var property in typeof(TColumns).GetTypeInfo().DeclaredProperties)
{
var addColumnOperation = ((IInfrastructure<AddColumnOperation>)property.GetMethod.Invoke(columnsObject, null))
.Instance;
if (addColumnOperation.Name == null)
{
addColumnOperation.Name = property.Name;
}
columnMap.Add(property, addColumnOperation);
}
var builder = new CreateTableBuilder<TColumns>(createTableOperation, columnMap);
constraints?.Invoke(builder);
migrationBuilder.Operations.Add(createTableOperation);
return builder;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment