Skip to content

Instantly share code, notes, and snippets.

@Grinderofl
Created June 19, 2019 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Grinderofl/147b7cd867fb55cdb9fbd0492043220a to your computer and use it in GitHub Desktop.
Save Grinderofl/147b7cd867fb55cdb9fbd0492043220a to your computer and use it in GitHub Desktop.
Specify starting migration
#addin nuget:?package=Newtonsoft.Json&version=12.0.1
using Newtonsoft.Json;
public class EfMigration
{
public string Id { get; set; }
public string Name { get; set; }
public string SafeName { get; set; }
}
public class EfContext
{
public string FullName { get; set; }
public string SafeName { get; set; }
public string Name { get; set; }
public string AssemblyQualifiedName { get; set; }
}
public class EfMigrationConfiguration
{
public EfMigrationConfiguration(string contextName, string fromMigration = null)
{
ContextName = contextName;
From = fromMigration;
}
public string ContextName { get; }
public string From { get; set; }
}
public partial class Configuration
{
public List<EfMigrationConfiguration> MigrationConfigurations { get; } = new List<EfMigrationConfiguration>();
public Configuration MigrateContext(string contextName, string fromMigration = null)
{
MigrationConfigurations.Add(new EfMigrationConfiguration(contextName, fromMigration));
context.Information("Added Migration From {0} for context {1}", fromMigration, contextName);
return this;
}
}
// known issue workaround https://github.com/aspnet/Home/releases/tag/2.1.3#known-issues
// and mysterious EF tool log messages
string SanitizeToolOutput(string toolOutput)
{
var lines = toolOutput.Split('\n');
var lineIndex = 0;
for (; lineIndex < lines.Length; lineIndex++)
{
var line = lines[lineIndex];
if (line.StartsWith("{") || line.StartsWith("[")) { break; }
}
return string.Join( "\n", lines.Skip(lineIndex));
}
var schemaFinder = new System.Text.RegularExpressions.Regex("N\'(.*)'");
string TransactionalizeMigration(string contents)
{
var lines = contents.Split('\n');
var output = new List<string>();
var currentTransaction = "";
foreach (var line in lines)
{
if (line.Contains("IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId]"))
{
var transaction = schemaFinder.Match(line).Groups[1].Value;
if(!string.IsNullOrWhiteSpace(currentTransaction) && !currentTransaction.Equals(transaction))
{
output.Add("COMMIT\n");
currentTransaction = "";
}
if (string.IsNullOrWhiteSpace(currentTransaction))
{
currentTransaction = transaction;
output.Add("BEGIN TRANSACTION\n");
}
}
var lineToAdd = !string.IsNullOrWhiteSpace(currentTransaction) ? $"\t{line}" : line;
output.Add(lineToAdd);
}
if (!string.IsNullOrWhiteSpace(currentTransaction))
{
output.Add("COMMIT");
}
return string.Join("\n", output);
}
IEnumerable<EfContext> GetAllDbContexts(DirectoryPath workingDirectory, string configuration)
{
var settings = new ProcessSettings()
{
WorkingDirectory = workingDirectory,
RedirectStandardOutput = true
};
settings.Arguments = string.Format("ef dbcontext list --configuration {0} --json", configuration);
var list = Enumerable.Empty<EfContext>();
using(var process = StartAndReturnProcess("dotnet", settings))
{
process.WaitForExit();
if(process.GetExitCode() == 0)
{
try
{
var outputAsJson = SanitizeToolOutput(string.Join(Environment.NewLine, process.GetStandardOutput()));
list = JsonConvert.DeserializeObject<List<EfContext>>(outputAsJson);
Verbose("Found {0} Db contexts", list.Count());
}
catch(Exception exception)
{
Error("Unable to determine db context's for {0} : {1}", workingDirectory, exception.Message);
}
}
}
return list.ToList();
}
IEnumerable<EfMigration> GetMigrationsForContext(string dbContext, DirectoryPath workingDirectory, string configuration)
{
var settings = new ProcessSettings()
{
WorkingDirectory = workingDirectory,
RedirectStandardOutput = true
};
settings.Arguments = string.Format("ef migrations list --configuration {0} --context {1} --json", configuration, dbContext);
var list = Enumerable.Empty<EfMigration>();
using(var process = StartAndReturnProcess("dotnet", settings))
{
process.WaitForExit();
if(process.GetExitCode() == 0)
{
try
{
var outputAsJson = SanitizeToolOutput(string.Join(Environment.NewLine, process.GetStandardOutput()));
list = JsonConvert.DeserializeObject<List<EfMigration>>(outputAsJson);
}
catch(Exception exception)
{
Error("Unable to determine db migration list for {0} : {1}", dbContext, exception.Message);
}
}
}
return list;
}
Task("Artifacts:DotNetCore:Ef:Migration-Script")
.IsDependentOn("Build")
.IsDependeeOf("Publish")
.Does<Configuration>(config =>
{
var efProjects = config.Solution.Projects.ToList();
Information("Generating scripts for {0} projects", efProjects.Count());
foreach(var project in efProjects) {
var assemblyName = config.Solution.GetProjectName(project);
var workingDirectory = project.ProjectFilePath.GetDirectory();
var availableDbContexts = GetAllDbContexts(workingDirectory, config.Solution.BuildConfiguration).ToList();
Information("Generating scripts for {0} containing {1} contexts", assemblyName, availableDbContexts.Count);
foreach(var dbContext in availableDbContexts)
{
Information("Generating Sql Script for {0}", dbContext.SafeName);
var migrations = GetMigrationsForContext(dbContext.SafeName, workingDirectory, config.Solution.BuildConfiguration);
var sqlScript = MakeAbsolute(File($"{config.Artifacts.Root}/sql/{dbContext.SafeName}.sql"));
if(FileExists(sqlScript)) {
DeleteFile(sqlScript);
}
var settings = new ProcessSettings()
{
WorkingDirectory = workingDirectory
};
var migrationConfiguration = config.MigrationConfigurations.FirstOrDefault(x => x.ContextName == dbContext.SafeName);
if(migrationConfiguration != null)
{
Information("Migrating {0} from {1}", migrationConfiguration.ContextName, migrationConfiguration.From);
settings.Arguments = string.Format("ef migrations script -i -o {0} --configuration {1} --context {2} {3}", sqlScript, config.Solution.BuildConfiguration, dbContext.SafeName, migrationConfiguration.From);
}
else
{
settings.Arguments = string.Format("ef migrations script -i -o {0} --configuration {1} --context {2}", sqlScript, config.Solution.BuildConfiguration, dbContext.SafeName);
}
using(var process = StartAndReturnProcess("dotnet", settings))
{
process.WaitForExit();
Verbose("Exit code: {0}", process.GetExitCode());
}
config.Artifacts.Add(ArtifactTypeOption.Other, sqlScript.GetFilename().ToString(), sqlScript);
}
}
});
#load ".cake/Configuration.cake"
/**********************************************************/
Setup<Configuration>(ctx => Configuration
.Create(ctx, "*.sln", c => {})
.MigrateContext("DataContext", "20181213073236_MigrateFromMe")
);
/**********************************************************/
#load ".cake/CI.cake"
// -- DotNetCore
#load ".cake/Npm-RunScript.cake"
#load ".cake/Restore-DotNetCore.cake"
#load ".cake/Build-DotNetCore.cake"
#load ".cake/Test-DotNetCore.cake"
#load ".cake/Test-Success.cake"
#load ".cake/Publish-Zip-DotNetCore.cake"
#load ".cake/Artifacts-DotNetCore-Ef.cake"
#load ".cake/Artifacts-Sql.cake"
#load ".cake/Artifacts-ARM.cake"
// -------------
RunTarget(Argument("target", Argument("Target", "Default")));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment