Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added documentation for flattening migrations #47

Merged
merged 3 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using Microsoft.EntityFrameworkCore;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! What are your thoughts on migrating this to the Data.SqlServer package? I set it up this morning being I know we are going to need it. One of our clients could actually use this soon as it is on their backlog. https://github.com/AndcultureCode/AndcultureCode.CSharp.Data.SqlServer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps projects could extend this, being required to override specific aspects and 'context' be a generic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for switching gears on your in terms of OSS packages - if you don't have time, we can just create a github issue do to it later on

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should be able to migrate this over on Friday. Once I do that, I'll also update this repo to use that package (I'll also address the master vs main issue when I migrate this over).

using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AndcultureCode.GB.Infrastructure.Data.SqlServer.Migrations
{
public partial class FlattenedMigration : Migration
{
public const string InitialMigrationId = "20200603183650_InitialCreate";

// This should contain all migration ID consts listed above, in the order that they should
// be run.
public static readonly List<string> FlattenedMigrations = new List<string> {
InitialMigrationId
};

#region Private

GBApiContext _context;

#endregion Private

#region Overrides

protected override void Up(MigrationBuilder migrationBuilder)
{
}

#endregion Overrides

public GBApiContext Context
{
get
{
if (this._context == null)
{
this._context = new GBApiContext();
}

return _context;
}
}

public void LogMigrationMessage(params string[] messages)
{
LogMigrationMessageLine("");
LogMigrationMessageLine("------------------------------------------------");
foreach (var m in messages)
{
LogMigrationMessageLine(m);
}
LogMigrationMessageLine("------------------------------------------------");
LogMigrationMessageLine("");
}

public void LogMigrationMessageLine(string line, string direction = "Up")
{
var migrationName = this.GetType().Name;
Console.WriteLine($"[Migration::{migrationName}#{direction}] {line}");
}

/// <summary>
/// This will check if, based on the sort order of the other migrations, this flattened
/// migration has not run on a fresh install.
/// </summary>
/// <param name="id">The ID of the flattened migration, as defined in the FlattenedMigrations list.</param>
/// <returns>True indicates the migration should be run. False indicates you should abort
/// running the migration, as this is not a fresh install, and the migration will likely fail.</returns>
public bool ValidateFlattenedShouldRun(string id)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick - alphabetization of methods

{
int count = 0;
using (var connection = Context.Database.GetDbConnection())
{
connection.Open();

using (var command = connection.CreateCommand())
{
command.CommandText = $"SELECT COUNT(*) FROM [dbo].[__EFMigrationsHistory]";
string result = command.ExecuteScalar().ToString();

int.TryParse(result, out count);
}
}

// If we have migrations after this one (this isn't a new DB), then abandon ship.
var migrationPosition = FlattenedMigrations.OrderBy(e => e).ToList().FindIndex(0, e => e == id);
if (count > migrationPosition)
{
return false;
}

return true;
}
}
}
17 changes: 17 additions & 0 deletions dotnet/api/Infrastructure/Data.SqlServer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Flattening Migrations

1. Checkout greatest common denominator brach -- typically `master`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Switch 'master' to 'main'

a. Master has migrations run on `master` and any additional mainline branches downstream, while development would have migrations that have not yet been run on anything upstream of `development`. Therefore, choose `master`.
2. Revert your database to the most recent flattened file, and delete all migrations other than the existing flattened migrations.
3. Create new migration -- this migration will now include everything to get the database to the state it was in after applying every migration you just deleted. Important: This does NOT include any changes manually made to the migration files!
4. Stash your changes and checkout the branch you'll be working off of (should be a branch off of the least common denominator branch, for example `development`).
5. Pop the changes from your stash. You should now have your flattened migration for everything that had made it's way to `master`, as well as additional migrations that haven't yet made their way into `master`.
6. EFCore uses string sorting to apply migrations in order -- thus the timestamp prepended to every migration you create. In this case, it will likely be at or near the end of the list, but we want to move it up to the top of the list to ensure it's run first.
* In order to do this, open the flattened migration and ensure it extends `FlattenedMigration` instead of `Migration`.
* In the `FlattenedMigration` class, add a new const for the ID of this migration. It should, when sorted as strings, be next in line from the previous flattened migration. Additionally, add it to the static list that contains all the migration ID consts.
* Open the `Designer.cs` file of the flattened migration, and update the name in the `[Migration("")]` attribute to use the const you just added.
* Rename the files themselves to match what you put in the `[Migration("")]` attribute.
7. Manually update the flattened migration to call `ValidateFlattenedShouldRun` passing in the ID of the migration you are working off of. This will check if, based on the sort order of the other flattened migrations, this flattened migration has not run on a fresh install. If that method call returns false, you should abort running the flattened migration, as it means this is not a fresh install.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can some subset of this comment be added as a comment above the method in that base class?

8. The final step, is to go back to `development` and take a glance at everything you just flattened to ensure there hasn't been any manual changes to migrations -- SQL being run directly, data manipulation, etc. -- and if there are, take those changes and create a new empty migration to ensure everything is consistent when the migrations are run. This migration should follow steps 6-8 in order to sort the migration correctly and validate it needs to be run.
9. Test, test, test! Verify that everything works as expected when running against your local database (and, if you have a production backup on your machine, test that it works against that as well). Additionally, test that it generates a fresh database successfully, and that all your integration tests against that fresh database run successfully.
10. Finally, be aware that it's possible your new flattened migration will cause people that haven't run the project for a while to be missing migrations when they start up the project. Let your team know this is the case so everyone has a chance to run `./sdk refresh-environment` on their machine before your flattened migration is merged in.