Created
October 17, 2024 02:09
-
-
Save rkttu/1fc5a0ec68bb6939ef1738e2ccf8cf73 to your computer and use it in GitHub Desktop.
How to generate SQL DDL compatible with the entire model instead of auto-migration
This file contains 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
// Package Required: Microsoft.EntityFrameworkCore.Relational | |
// In this example, I am using the EF Core driver (Pomelo.EntityFrameworkCore.MySql) from the Pomelo Foundation Project to generate DDL for MariaDB. | |
// However, you can also use any other driver you want (Microsoft SQL Server, Sqlite, Oracle, etc.). | |
using System.ComponentModel.DataAnnotations; | |
using Microsoft.EntityFrameworkCore; | |
using Pomelo.EntityFrameworkCore.MySql; | |
using Pomelo.EntityFrameworkCore.MySql.Infrastructure; | |
var generatedScript = EFCoreExtensions.GenerateDatabaseDefinitions<SampleDbContext>( | |
o => o.UseMySql(ServerVersion.Create(11, 5, 2, ServerType.MariaDb))); | |
Console.Out.WriteLine(generatedScript); | |
#region Sample Model | |
public record Users | |
{ | |
[Key] | |
public required Guid Id { get; init; } | |
public required string Username { get; init; } | |
public required DateTime RegisteredDateTimeUtc { get; init; } | |
} | |
public class SampleDbContext : DbContext | |
{ | |
public SampleDbContext(DbContextOptions<SampleDbContext> options) : base(options) { } | |
public DbSet<Users> Users { get; set; } = default!; | |
} | |
#endregion // Sample Model | |
internal static class EFCoreExtensions | |
{ | |
public static string GenerateDatabaseDefinitions<TDbContext>( | |
Action<DbContextOptionsBuilder<TDbContext>> options) | |
where TDbContext : DbContext | |
{ | |
var optionsBuilder = new DbContextOptionsBuilder<TDbContext>(); | |
options.Invoke(optionsBuilder); | |
var dbContextType = typeof(TDbContext); | |
var publicConstructors = dbContextType.GetConstructors(); | |
foreach (var eachConstructor in publicConstructors) | |
{ | |
var parameters = eachConstructor.GetParameters(); | |
if (parameters.Length != 1) | |
continue; | |
var singleParameter = parameters.First(); | |
if (!singleParameter.ParameterType.IsGenericType) | |
continue; | |
var singleParameterTypedef = singleParameter.ParameterType.GetGenericTypeDefinition(); | |
if (singleParameterTypedef != typeof(DbContextOptions<>)) | |
continue; | |
var singleParameterTypeArgs = singleParameter.ParameterType.GetGenericArguments(); | |
if (singleParameterTypeArgs.Length != 1) | |
continue; | |
var singleParameterSingleTypeArg = singleParameterTypeArgs.First(); | |
if (singleParameterSingleTypeArg != dbContextType) | |
continue; | |
using var context = eachConstructor.Invoke(new object[] { optionsBuilder.Options }) as TDbContext; | |
if (context == null) | |
continue; | |
return context.Database.GenerateCreateScript(); | |
} | |
throw new NotSupportedException($"No supported public constructors for '{typeof(TDbContext)}' type."); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment