From d00e62327a698b71a2e03067760fe58037058ce1 Mon Sep 17 00:00:00 2001 From: jogibear9988 Date: Tue, 22 Sep 2026 14:13:43 +0200 Subject: [PATCH] docs: add GitHub Pages homepage and expand project documentation Create a responsive static homepage for DotNetProjects.Migrator with a C# migration example, installation guide, provider inventory, project links, accessible navigation, and progressive copy-to-clipboard controls. Compare Migrator with FluentMigrator, EF Core, DbUp, and Evolve using official documentation and repository implementation evidence. Cover authoring, ORM coupling, model scaffolding, raw SQL, downgrade behavior, history separation, transactions, execution, providers, and recurring work. Include dated sources and distinguish provider availability from compatibility guarantees and transaction rollback from migration reversal. Replace the outdated README with NuGet/build/Pages/framework/license badges, runnable SQLite examples, runner API guidance, migration scope semantics, provider mappings, build and test instructions, contribution guidance, and links to the homepage and comparison. Add a GitHub Actions workflow that publishes docs/ to GitHub Pages on relevant master changes or manual dispatch. Document initial Pages setup and local preview commands. Site assets use relative paths and require no build tools or client-side dependencies. Validation: - Compiled and exercised the documented SQLite snippets against the current source: create, repeated execution, downgrade, and reapply. - Passed all 19 focused migration runner tests. - Checked desktop/mobile layouts, citation disclosure, and copy controls. - Verified local documentation links, HTML anchors, and asset paths. - Passed JavaScript syntax and git whitespace checks. Publishing still requires these changes on master and GitHub Actions selected as the repository's Pages source. --- .github/workflows/pages.yml | 36 ++ README.md | 347 +++++++++++++--- docs/.nojekyll | 0 docs/README.md | 29 ++ docs/assets/favicon.svg | 1 + docs/assets/site.css | 799 ++++++++++++++++++++++++++++++++++++ docs/assets/site.js | 33 ++ docs/index.html | 688 +++++++++++++++++++++++++++++++ 8 files changed, 1872 insertions(+), 61 deletions(-) create mode 100644 .github/workflows/pages.yml create mode 100644 docs/.nojekyll create mode 100644 docs/README.md create mode 100644 docs/assets/favicon.svg create mode 100644 docs/assets/site.css create mode 100644 docs/assets/site.js create mode 100644 docs/index.html diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..7569a3b6 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,36 @@ +name: Deploy homepage to GitHub Pages + +on: + push: + branches: [master] + paths: + - "docs/**" + - ".github/workflows/pages.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: github-pages + cancel-in-progress: false + +jobs: + deploy: + if: github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v6 + - uses: actions/configure-pages@v5 + - uses: actions/upload-pages-artifact@v4 + with: + path: docs + - name: Deploy homepage + id: deployment + uses: actions/deploy-pages@v4 diff --git a/README.md b/README.md index 8955225b..285a7ee0 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,307 @@ -Info ----- -This Project includes most of the changes from all forks on GitHub! +# DotNetProjects.Migrator -But has also a few changes: - - SQLite support is better (Schema Reading, multiple Primary Keys, and much more) - - Remove direct References to the Dataproviders - - Dump Indexes from SQLServer - - Different Migration Scopes in one Database +**Versioned database migrations in C#, independent of your ORM.** +[![NuGet version](https://img.shields.io/nuget/v/DotNetProjects.Migrator.svg)](https://www.nuget.org/packages/DotNetProjects.Migrator/) +[![NuGet downloads](https://img.shields.io/nuget/dt/DotNetProjects.Migrator.svg)](https://www.nuget.org/packages/DotNetProjects.Migrator/) +[![Build and tests](https://github.com/dotnetprojects/Migrator.NET/actions/workflows/dotnetpull.yml/badge.svg?branch=master)](https://github.com/dotnetprojects/Migrator.NET/actions/workflows/dotnetpull.yml) +[![GitHub Pages](https://github.com/dotnetprojects/Migrator.NET/actions/workflows/pages.yml/badge.svg?branch=master)](https://github.com/dotnetprojects/Migrator.NET/actions/workflows/pages.yml) +[![Source target: .NET 9](https://img.shields.io/badge/source_target-.NET_9-512BD4)](src/Migrator/DotNetProjects.Migrator.csproj) +[![License: MPL-1.1](https://img.shields.io/badge/license-MPL--1.1-blue.svg)](https://www.mozilla.org/en-US/MPL/1.1/) -NuGet ------ -https://www.nuget.org/packages/DotNetProjects.Migrator/ +[Homepage & documentation](https://dotnetprojects.github.io/Migrator.NET/) · [NuGet](https://www.nuget.org/packages/DotNetProjects.Migrator/) · [Releases](https://github.com/dotnetprojects/Migrator.NET/releases) · [Issues](https://github.com/dotnetprojects/Migrator.NET/issues) · [Feature comparison](https://dotnetprojects.github.io/Migrator.NET/#compare) -Introduction ------------- +DotNetProjects.Migrator is a fork of [Migrator.NET](https://github.com/migratordotnet/Migrator.NET). Write each schema change as a numbered C# class, commit it alongside your application, and use the runner to bring a database to the required version. The database records which migrations have already been applied. -This project is a fork of "ye olde trusty" Migrator.Net - the original project can be found [here on google code][1], and has since [moved to github][2] - -Usage Example -------------- +## Contents + +- [Why use it?](#why-use-it) +- [Installation and requirements](#installation-and-requirements) +- [Quick start](#quick-start) +- [Migration versions and rollback](#migration-versions-and-rollback) +- [Multiple modules and migration scopes](#multiple-modules-and-migration-scopes) +- [Schema and data operations](#schema-and-data-operations) +- [Database providers](#database-providers) +- [Comparison with other .NET frameworks](#comparison-with-other-net-frameworks) +- [Building and testing](#building-and-testing) +- [Documentation and GitHub Pages](#documentation-and-github-pages) +- [Contributing and project history](#contributing-and-project-history) +- [License](#license) + +## Why use it? + +- **Explicit C# migrations.** Define forward and reverse changes with `Up()` and `Down()`; review them like application code. +- **No ORM dependency.** Use it alongside EF, Dapper, another data layer, or plain ADO.NET. +- **Database transformation API.** Work with tables, columns, keys, indexes and data, with raw SQL available for provider-specific operations. +- **Version tracking.** Apply pending migrations or target a specific version using database-backed history. +- **Scoped histories.** Track multiple modules in one database when each runner is given the appropriate migration set. +- **Bring your database driver.** The library does not directly reference database-driver packages; supply an ADO.NET connection or configure the driver factory. +- **SQLite schema handling.** This fork includes schema inspection and table-recreation logic for operations SQLite cannot perform directly. + +Migrator is a library you embed in a migration host. It does not provide EF-style model-difference scaffolding, a packaged command-line runner, or built-in migration-content checksum validation. + +## Installation and requirements + +```sh +dotnet add package DotNetProjects.Migrator +``` + +Install the ADO.NET driver for your database separately. For the SQLite example below: + +```sh +dotnet add package Microsoft.Data.Sqlite --version 9.0.7 +``` + +The **current source targets `net9.0`**. Check the [NuGet package's framework list](https://www.nuget.org/packages/DotNetProjects.Migrator/#supportedframeworks-body-tab) for the particular release you install; older package releases may target different frameworks. The SQLite driver version above matches the repository's test dependency. + +Building the `.slnx` solution requires an SDK that understands that format, such as .NET SDK 9.0.200 or later. The runtime required by the current source is .NET 9. + +## Quick start + +### 1. Create a migration host + +```sh +dotnet new console -n MigrationDemo -f net9.0 +cd MigrationDemo +dotnet add package DotNetProjects.Migrator +dotnet add package Microsoft.Data.Sqlite --version 9.0.7 +``` + +### 2. Add `CreateUsers.cs` + +Migrations must be public classes implementing the migration contract, decorated with `[Migration(version)]`. Each version must be unique within the set loaded by one runner. + +```csharp +using System.Data; +using DotNetProjects.Migrator.Framework; -M_001_InitialSchema.cs -```cs [Migration(1)] -public class M_001_InitialSchema : Migration +public class CreateUsers : Migration { - public override void Up() - { - Database.AddTable( - "Users", - new Column("Id", DbType.Guid, ColumnProperty.NotNull | ColumnProperty.PrimaryKey), - new Column("CreationDate", DbType.DateTime, ColumnProperty.NotNull), - new Column("ModificationDate", DbType.DateTime, ColumnProperty.NotNull), - new Column("Name", DbType.String, 255), - new Column("Password", DbType.String, 255), - new Column("ExplicitRoles", DbType.String, int.MaxValue)); - } - - public override void Down() - { - } + public override void Up() + { + Database.AddTable("Users", + new Column("Id", DbType.Int32, ColumnProperty.NotNull), + new Column("Name", DbType.String, 255)); + Database.AddPrimaryKey("PK_Users", "Users", "Id"); + } + + public override void Down() + { + Database.RemoveTable("Users"); + } } ``` -Code to Apply Migration: -```cs -using (var p = ProviderFactory.Create(ProviderTypes.SQLite, connection, null)) +### 3. Replace `Program.cs` + +```csharp +using DotNetProjects.Migrator; +using DotNetProjects.Migrator.Providers; +using Microsoft.Data.Sqlite; + +using var connection = new SqliteConnection("Data Source=app.db"); +connection.Open(); + +using var provider = ProviderFactory.Create( + ProviderTypes.SQLite, connection, defaultSchema: null); + +var migrator = new Migrator( + provider, typeof(CreateUsers).Assembly, trace: false); + +if (migrator.LastAppliedMigrationVersion is long applied + && applied > migrator.AssemblyLastMigrationVersion) { - var migrator = new Migrator(p, typeof(M_001_InitialSchema).Assembly, false)); - - if (migrator.LastAppliedMigrationVersion != null && migrator.LastAppliedMigrationVersion.Value > migrator.AssemblyLastMigrationVersion) - { - throw new Exception("Database has newer Migrations applied then the Software supports"); - } - else - { - migrator.MigrateToLastVersion(); - } + throw new InvalidOperationException( + "Database version is newer than this application."); } + +migrator.MigrateToLastVersion(); +``` + +### 4. Run it + +```sh +dotnet run +``` + +This creates a local SQLite database containing `Users` and the migration history table. Running the application again skips version `1` because it has already been recorded. Add a new class with `[Migration(2)]` for the next change. + +The example supplies an **open** `IDbConnection`. The caller owns that connection and disposes it after the provider. If you use the connection-string overload instead, the selected provider must be able to resolve the appropriate ADO.NET factory. + +## Migration versions and rollback + +Use increasing numeric versions, or the attribute's date-based constructor: + +```csharp +[Migration(2026, 9, 22, 12, 0, 0)] ``` -What's different in the fork ----------------------------- +Keep applied migration classes in source control. Change the schema with a new migration instead of editing an already applied one: history records the version, not a checksum of the migration's content. + +| API | Purpose | +| ------------------------------ | -------------------------------------------------------------------------------- | +| `MigrateToLastVersion()` | Apply through the latest version in the loaded migration set. | +| `MigrateTo(version)` | Move to a chosen version, invoking `Up()` or `Down()` as required. | +| `AppliedMigrations` | List the versions recorded for the provider's scope. | +| `LastAppliedMigrationVersion` | Highest applied version, or `null` when none are applied. | +| `AssemblyLastMigrationVersion` | Highest version in the loaded migration set. | +| `SchemaInfoTableName` | Customize the history table name before accessing history or running migrations. | + +With the runner above, `migrator.MigrateTo(0)` reverses all applied migrations in its set. In this example that drops `Users`, including its data. A `Down()` implementation is a reverse schema operation, not a backup restore. + +Migration execution starts a transaction for each migration and attempts rollback on failure. Actual atomicity depends on the database, driver and operation; some databases implicitly commit DDL. `AfterUp()` and `AfterDown()` run **after commit**, so a failure in those hooks cannot undo the committed migration. + +For deployment, run a dedicated migration host before the application needs the new schema. Coordinate it so competing instances do not migrate the same database concurrently. Review and test both directions against your actual database engine. + +## Multiple modules and migration scopes + +The default history table is `SchemaInfo`, with version, scope and timestamp information. The default scope is `"default"`. You can use separate scopes for modules sharing a database. + +Within a host with an open `connection`, select the module's migration types explicitly: + +```csharp +using var billingProvider = ProviderFactory.Create( + ProviderTypes.SQLite, + connection, + defaultSchema: null, + scope: "billing"); + +var billingMigrator = new Migrator( + billingProvider, + false, + typeof(Billing001), + typeof(Billing002)); + +billingMigrator.MigrateToLastVersion(); +``` -In this fork the main changes are: +`Billing001` and `Billing002` represent your own public migration classes. Alternatively, give the runner an assembly that contains only that module's migrations. -* Now targets .Net Framework 4.0 instead of 2.0/3.5. -* NetStandart 2.0 will be supported when released -* Support for reserved words. -* Support for guid types across all databases. -* Utility classes for removing all tables etc. from a database (to support migration integration tests). -* Warnings in Oracle when attempting to create column/table/key names that are over-length. -* Removed reliance on deprecated SqlServer views such as Sysconstraints (to support Azure Sql Server deployment). +Important details: - [1]: http://code.google.com/p/migratordotnet/ - [2]: https://github.com/migratordotnet/Migrator.NET +- A scope partitions **history**, not migration discovery. Passing a mixed assembly does not automatically filter it by scope. +- Leave `MigrationAttribute.Scope` unset to inherit the provider scope. If you override it, it must align with the history the runner reads. +- Duplicate versions are checked across the runner's whole loaded migration set. Separate scopes do not permit duplicate versions within one runner. +- Scopes do not isolate tables or data. Module migrations still need compatible table names and coordinated schema ownership. -## Live database testing +See [ProviderFactory](src/Migrator/ProviderFactory.cs), [MigrationLoader](src/Migrator/MigrationLoader.cs) and [history implementation](src/Migrator/Providers/TransformationProvider.cs). + +## Schema and data operations + +Inside a migration, `Database` implements [`ITransformationProvider`](src/Migrator/Framework/ITransformationProvider.cs). It includes: + +| Area | Examples | +| ------------------ | ------------------------------------------------------------------------------------- | +| Tables and columns | `AddTable`, `RemoveTable`, `RenameTable`, `AddColumn`, `ChangeColumn`, `RemoveColumn` | +| Keys and indexes | `AddPrimaryKey`, `AddForeignKey`, `AddIndex` and corresponding removal operations | +| Schema inspection | `TableExists`, `ColumnExists`, `GetTables`, `GetColumns` | +| Data and SQL | `Insert`, `Update`, `Delete`, `ExecuteNonQuery`, `ExecuteQuery`, `ExecuteScalar` | + +For example, a new migration can add a column: + +```csharp +public override void Up() +{ + Database.AddColumn("Users", new Column("Email", DbType.String, 320)); +} + +public override void Down() +{ + Database.RemoveColumn("Users", "Email"); +} +``` + +Provider implementations determine which operations are available and how they map to SQL. Use `Database.ExecuteNonQuery(...)` for custom SQL and keep dialect-specific statements explicit. The source also includes a [schema builder API](src/Migrator/Framework/SchemaBuilder/SchemaBuilder.cs). + +## Database providers + +The [provider factory](src/Migrator/ProviderFactory.cs) contains these database families: + +| Database | `ProviderTypes` value(s) | +| ------------ | ---------------------------- | +| SQL Server | `SqlServer`, `SqlServer2005` | +| PostgreSQL | `PostgreSQL`, `PostgreSQL82` | +| SQLite | `SQLite`, `MonoSQLite` | +| MySQL | `Mysql` | +| MariaDB | `MariaDB` | +| Oracle | `Oracle`, `MsOracle` | +| IBM Db2 | `IBM_DB2` | +| IBM Informix | `IBM_Informix` | +| Firebird | `Firebird` | +| Ingres | `Ingres` | +| Sybase | `Sybase` | + +This is an inventory of dialects present in source, **not a guarantee that every server version, driver or operation is supported**. Some entries are legacy variants. Verify the combination you deploy against the [provider implementations](src/Migrator/Providers/Impl) and [provider tests](src/Migrator.Tests/Providers). + +## Comparison with other .NET frameworks + +Reviewed **22 September 2026**. Migrator's column describes this repository; the alternatives summarize their official documentation. These are workflow differences, not performance benchmarks or a ranking. + +| Capability | Migrator.NET (this fork) | FluentMigrator | EF Core | DbUp | Evolve | +| ---------------------------- | --------------------------------- | -------------------------------------------- | ------------------------------------ | -------------------------- | --------------------------------- | +| Authoring | Handwritten C# transformation API | Handwritten C# fluent DSL | C# scaffolded from model differences | SQL or C# scripts | Versioned SQL files | +| ORM-independent workflow | Yes | Yes | Uses EF model / DbContext | Yes | Yes | +| Model-difference scaffolding | No built-in generator | Hand-authored | Yes, with model snapshots | Hand-authored | Hand-authored | +| Downgrade applied migrations | Authored `Down()` | `Down()`; supported auto-reverse expressions | Generated/editable `Down()` | Custom undo or forward fix | Forward fix; no Down command | +| Separate histories | Scope + selected assembly/types | Custom version table + filtering | Contexts + custom history table | Journals + script filters | Metadata table/schema + locations | +| Execution | Library / custom host | Library + CLI | CLI, scripts, bundles, runtime | Library / custom host | Library, .NET tool, CLI | +| Recurring work | Custom code | Maintenance migrations / profiles | Seeding APIs (EF 9+) | `RunAlways` scripts | Checksum-based repeatable SQL | + +All five can execute raw SQL. Transaction support depends on database capabilities: Migrator starts one per migration; DbUp makes transactions opt-in; the others have configurable transaction behavior. Reversing a completed migration is different from rolling back a failed transaction. Evolve's checksum-based repeatables also differ from always-run scripts or lifecycle hooks. + +- Choose **Migrator** for direct C# schema operations, scoped history and integration with your own host. +- Consider **FluentMigrator** for its fluent authoring API, packaged runners, tags and profiles. +- Consider **EF Core migrations** when your EF model drives the schema and you want scaffolding and deployment artifacts. +- Consider **DbUp** for a SQL-oriented runner composed in .NET, or **Evolve** for convention-based SQL with checksum validation and repeatables. + +Sources: [Migrator runner](src/Migrator/Migrator.cs), [FluentMigrator quick start](https://fluentmigrator.github.io/intro/quick-start.html) and [configuration](https://fluentmigrator.github.io/intro/configuration.html), [EF Core migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/) and [deployment](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/applying), [DbUp documentation](https://dbup.readthedocs.io/en/latest/) and [script types](https://dbup.readthedocs.io/en/latest/more-info/script-types/), [Evolve concepts](https://evolve-db.netlify.app/concepts/). The [full homepage comparison](https://dotnetprojects.github.io/Migrator.NET/#compare) includes transaction, provider and source details; its source is available in [docs/index.html](docs/index.html). + +## Building and testing + +```sh +dotnet restore Migrator.slnx +dotnet build Migrator.slnx --configuration Release --no-restore +``` + +Tests use NUnit. Run a focused runner test fixture without provisioning external databases: + +```sh +dotnet test src/Migrator.Tests/Migrator.Tests.csproj --configuration Release --filter "FullyQualifiedName~Migrator.Tests.MigratorTest" +``` + +The full suite includes database integration tests: + +```sh +dotnet test src/Migrator.Tests/Migrator.Tests.csproj --configuration Release +``` + +Use disposable test databases: integration tests create, alter and remove schema objects. Configure connections in `src/Migrator.Tests/appsettings.Development.json` using the structure and identifiers in [appsettings.json](src/Migrator.Tests/appsettings.json), and set `ASPNETCORE_ENVIRONMENT=Development`. The development settings file is gitignored; keep credentials there rather than committing them. + +The [.NET workflow](.github/workflows/dotnetpull.yml) documents CI database services and commands. Provider coverage varies; a passing build alone does not validate every supported database family. + +### Live database testing See [live database testing](docs/live-database-tests.md) for the CI matrix, pinned versions, local commands, coverage, engine limitations and excluded candidates. + +## Documentation and GitHub Pages + +The homepage in [`docs/`](docs/README.md) includes installation, a runnable quick start, provider information and a sourced feature comparison. It uses plain HTML, CSS and JavaScript with no build dependencies. + +Preview locally from the repository root: + +```sh +python -m http.server 8766 --directory docs --bind 127.0.0.1 +``` + +Open [localhost:8766](http://localhost:8766). To publish, select **GitHub Actions** under **Settings → Pages → Build and deployment**, then merge the site into `master`. The [Pages workflow](.github/workflows/pages.yml) deploys changes to `docs/` at [dotnetprojects.github.io/Migrator.NET](https://dotnetprojects.github.io/Migrator.NET/). The workflow can also be dispatched manually on `master`. + +## Contributing and project history + +Bug reports, provider fixes, tests and documentation improvements are welcome through [issues](https://github.com/dotnetprojects/Migrator.NET/issues) and [pull requests](https://github.com/dotnetprojects/Migrator.NET/pulls). Include the package version, database/driver versions, a minimal migration that reproduces the problem, and expected versus actual behavior. Add a focused regression test for a behavior change and run the relevant provider tests. + +This project continues the original [Migrator.NET](https://github.com/migratordotnet/Migrator.NET), which began on Google Code. This fork incorporates contributions from other forks and work on SQLite schema reading and recreation, composite primary keys, SQL Server index inspection, reserved identifiers, provider independence and migration scopes. + +## License + +The package declares **Mozilla Public License 1.1 (MPL-1.1)** in its [project metadata](src/Migrator/DotNetProjects.Migrator.csproj). See the [license text](https://www.mozilla.org/en-US/MPL/1.1/) and source-file notices. diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..99d7775c --- /dev/null +++ b/docs/README.md @@ -0,0 +1,29 @@ +# Migrator.NET homepage + +Static GitHub Pages site, with no build tools, external fonts or client-side dependencies. `index.html` contains the homepage, quick start and sourced framework comparison. Styling and progressive enhancements live in `assets/`. + +## Preview + +From the repository root: + +```sh +python -m http.server 8766 --directory docs --bind 127.0.0.1 +``` + +Open http://localhost:8766. Content and navigation work without JavaScript. Copy buttons require a secure context (HTTPS or localhost). + +## Publish on GitHub Pages + +1. In the repository's **Settings → Pages → Build and deployment**, set **Source** to **GitHub Actions**. +2. Merge the site and `.github/workflows/pages.yml` into `master`. +3. The workflow publishes only `docs/`. After enabling Pages, you can also run **Deploy homepage to GitHub Pages** manually on `master`. + +Expected project URL: https://dotnetprojects.github.io/Migrator.NET/ + +All site assets use relative URLs, so the repository subpath works without a custom domain. No deployment or repository setting changes are performed by a local preview. GitHub may require an environment approval if the repository has deployment protection rules. + +## Keep the comparison accurate + +The comparison distinguishes source capabilities from guarantees about released packages or database compatibility. Update the review date and source links together when reviewing it. Avoid equating transaction rollback with reversing completed migrations, treating a provider enum as a support guarantee, or treating scoped history as a migration discovery filter. + +The quick start targets the current source's .NET 9 API. Check the selected NuGet release's target frameworks. The SQLite driver version matches the repository test dependency. Validate authoring and runner snippets together when changing them. diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg new file mode 100644 index 00000000..5b4cd95b --- /dev/null +++ b/docs/assets/favicon.svg @@ -0,0 +1 @@ + diff --git a/docs/assets/site.css b/docs/assets/site.css new file mode 100644 index 00000000..3af8f11d --- /dev/null +++ b/docs/assets/site.css @@ -0,0 +1,799 @@ +:root { + color-scheme: light; + --ink: #152238; + --muted: #536176; + --blue: #1552cb; + --line: #dce3ed; + --paper: #fff; + --navy: #101a2d; + --mono: Consolas, "SFMono-Regular", monospace; + font-family: Inter, "Segoe UI", Arial, sans-serif; + color: var(--ink); + background: var(--paper); + font-size: 16px; + line-height: 1.65; +} +* { + box-sizing: border-box; +} +html { + scroll-behavior: smooth; + scroll-padding-top: 100px; +} +body { + margin: 0; +} +a { + color: var(--blue); + text-underline-offset: 4px; +} +a:hover { + text-decoration-thickness: 2px; +} +button, +a { + -webkit-tap-highlight-color: transparent; +} +a:focus-visible, +button:focus-visible, +summary:focus-visible, +[tabindex]:focus-visible { + outline: 3px solid #e38b00; + outline-offset: 5px; +} +button { + font: inherit; + cursor: pointer; +} +[hidden] { + display: none !important; +} +.container { + width: min(1240px, calc(100% - 96px)); + margin-inline: auto; +} +.site-header { + border-bottom: 1px solid var(--line); + background: #fffffff5; + position: sticky; + top: 0; + z-index: 10; +} +.header-inner { + min-height: 84px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; +} +.brand { + display: inline-flex; + align-items: center; + font-weight: 750; + font-size: 23px; + letter-spacing: -0.7px; + text-decoration: none; + color: var(--ink); + white-space: nowrap; +} +.brand-suffix { + font-weight: 400; + color: var(--muted); +} +.brand-mark { + margin-right: 12px; + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--blue); + color: white; + width: 38px; + height: 38px; + border-radius: 8px; + font-size: 22px; + letter-spacing: -4px; + padding-right: 4px; +} +.brand-mark span { + font-size: 17px; + align-self: flex-start; +} +nav { + display: flex; + gap: 30px; + align-items: center; +} +nav a { + font-weight: 600; + font-size: 14px; + color: var(--ink); + text-decoration: none; +} +nav a:hover { + color: var(--blue); +} +.hero { + padding: 84px 0 78px; +} +.hero-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 54px; + align-items: center; +} +.eyebrow { + font-size: 12px; + font-weight: 750; + letter-spacing: 1.8px; + color: var(--blue); + margin: 0 0 20px; +} +h1, +h2, +h3, +p { + margin-top: 0; +} +h1 { + font-size: clamp(38px, 4.1vw, 58px); + line-height: 1.13; + letter-spacing: -2.4px; + margin-bottom: 24px; +} +h1 span { + color: var(--blue); +} +.intro { + font-size: 19px; + color: var(--muted); + max-width: 480px; + line-height: 1.65; + margin-bottom: 30px; +} +.actions { + display: flex; + flex-wrap: wrap; + gap: 12px; +} +.button { + display: inline-flex; + align-items: center; + gap: 20px; + justify-content: center; + padding: 12px 18px; + border-radius: 6px; + font-size: 14px; + font-weight: 650; + text-decoration: none; + border: 1px solid var(--line); +} +.primary { + color: #fff; + background: var(--blue); + border-color: var(--blue); +} +.primary:hover { + background: #1043a7; +} +.secondary { + color: var(--ink); +} +.secondary:hover { + background: #f1f5fc; +} +.hero-meta { + font-size: 12px; + color: var(--muted); + margin: 22px 0 0; +} +.code-window { + background: var(--navy); + color: #e8edf5; + border-radius: 10px; + box-shadow: 0 18px 50px #15223820; + min-width: 0; + border: 1px solid #24334b; +} +.code-heading, +.snippet-heading { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; + padding: 14px 20px; + border-bottom: 1px solid #2d3a50; + font-size: 12px; +} +.code-heading > span:last-child { + color: #a4b3c9; + font-size: 12px; + letter-spacing: 1px; +} +.file-icon { + font-weight: 700; + color: #93b8ff; + margin-right: 8px; +} +pre { + margin: 0; + overflow: auto; + tab-size: 4; + font-family: var(--mono); + font-size: 14px; + line-height: 1.7; + padding: 22px; +} +code { + font-family: var(--mono); + font-size: 0.9em; + overflow-wrap: anywhere; +} +pre code { + font-size: inherit; + overflow-wrap: normal; +} +.code-window pre { + font-size: 13px; +} +.syntax-attribute { + color: #d7c087; +} +.syntax-keyword { + color: #acb9ff; +} +.syntax-string { + color: #91d9c2; +} +.code-footer { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 8px; + padding: 13px 20px; + border-top: 1px solid #2d3a50; + font-size: 12px; + color: #a4b3c9; +} +.code-footer a { + color: #b9d0ff; + text-decoration: none; +} +.database-strip { + background: #f6f8fc; + border-block: 1px solid var(--line); +} +.database-strip .container { + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 16px; + padding-block: 22px; + font-weight: 600; + font-size: 14px; +} +.strip-label { + font-size: 11px; + letter-spacing: 1.2px; + color: var(--muted); +} +.database-strip a { + font-size: 13px; + text-decoration: none; +} +.section { + padding-block: 82px; +} +h2 { + font-size: 38px; + line-height: 1.2; + letter-spacing: -1.2px; + margin-bottom: 24px; +} +h3 { + font-size: 19px; + line-height: 1.4; + letter-spacing: -0.25px; + margin-bottom: 12px; +} +.features-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 40px; + margin-top: 42px; +} +.features-grid article { + border-top: 2px solid var(--line); + padding-top: 23px; +} +.feature-number { + display: block; + font-size: 12px; + letter-spacing: 1px; + color: var(--blue); + margin-bottom: 22px; + font-weight: 650; +} +.features-grid p, +.fit-grid p, +.step-description p, +.project-section p { + color: var(--muted); +} +.quick-section { + background: #f6f8fc; + padding-block: 76px; +} +.section-heading { + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 36px; + margin-bottom: 32px; +} +.section-heading h2 { + margin-bottom: 0; +} +.section-heading > p { + font-size: 14px; + color: var(--muted); + margin-bottom: 3px; + max-width: 380px; +} +.step { + display: grid; + grid-template-columns: 1fr 1.65fr; + gap: 64px; + padding-block: 32px; + align-items: start; +} +.step-index { + display: flex; + width: 32px; + height: 32px; + align-items: center; + justify-content: center; + border: 1px solid #b7c9e9; + color: var(--blue); + border-radius: 50%; + font-weight: 650; + margin-bottom: 16px; +} +.step-description a { + font-size: 14px; +} +.snippet { + background: #fff; + border: 1px solid var(--line); + border-radius: 8px; + overflow: hidden; + min-width: 0; +} +.snippet-heading { + border-color: var(--line); + font-family: var(--mono); + color: var(--muted); + min-height: 50px; + padding-block: 9px; +} +.snippet-heading button { + font-family: inherit; + font-size: 12px; + color: var(--blue); + padding: 4px 10px; + background: #f3f6fc; + border: 1px solid #d3ddef; + border-radius: 4px; +} +.snippet-heading button:hover { + background: #e6efff; +} +.note { + border-left: 3px solid var(--blue); + background: #eaf0fc; + padding: 20px 24px; + font-size: 14px; + margin-top: 22px; +} +.note strong { + display: block; + margin-bottom: 5px; +} +.provider-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 50px; + padding-block: 20px; +} +.provider-list { + list-style: none; + display: flex; + gap: 10px; + flex-wrap: wrap; + padding: 0; + margin: 16px 0; +} +.provider-list li { + border: 1px solid var(--line); + padding: 7px 13px; + border-radius: 4px; + font-size: 14px; +} +.muted { + color: var(--muted); + font-size: 14px; + max-width: 980px; +} +.comparison-section { + background: var(--navy); + color: #eef3ff; + padding-block: 76px; +} +.comparison-section .eyebrow { + color: #97bbff; +} +.comparison-section a { + color: #b0cbff; +} +.comparison-section .section-heading > p, +.comparison-intro { + color: #b7c4d9; +} +.comparison-intro { + max-width: 810px; +} +.table-hint { + color: #aab9d0; + font-size: 12px; + margin-top: 32px; +} +.table-scroll { + overflow-x: auto; + border: 1px solid #3c4b64; + border-radius: 8px; + max-width: 100%; +} +table { + border-collapse: collapse; + min-width: 1100px; + width: 100%; + font-size: 14px; + line-height: 1.55; +} +caption { + text-align: left; + padding: 16px; + background: #18243a; + color: #b7c4d9; + font-size: 12px; + caption-side: bottom; +} +th, +td { + text-align: left; + vertical-align: top; + padding: 19px 16px; + border-bottom: 1px solid #344158; + border-right: 1px solid #344158; + width: 16.66%; +} +th:last-child, +td:last-child { + border-right: 0; +} +thead th { + background: #1d2a41; + font-size: 16px; + padding-block: 23px; +} +thead th span, +thead th a { + display: block; + font-size: 12px; + font-weight: 400; +} +thead th a { + margin-top: 7px; +} +tbody th { + font-weight: 550; + color: #d3dded; +} +tbody td { + color: #c2cee0; +} +.ours { + background: #1c3356; + color: #fff; +} +thead .ours { + background: #224477; +} +tbody tr:hover td, +tbody tr:hover th { + background: #243851; +} +tbody tr:last-child > * { + border-bottom: 0; +} +.comparison-notes { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 36px; + font-size: 14px; + color: #b7c4d9; + margin-top: 26px; +} +.comparison-notes strong { + color: #eef3ff; +} +.fit-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 36px; + padding-top: 26px; + margin-top: 20px; + border-top: 1px solid #344158; +} +.fit-grid h3 { + font-size: 18px; +} +.fit-grid p { + font-size: 14px; + color: #b7c4d9; +} +.fit-grid strong { + color: #e5edfb; +} +.sources { + border-top: 1px solid #344158; + margin-top: 28px; + padding-top: 22px; + font-size: 14px; + color: #b7c4d9; +} +.sources summary { + cursor: pointer; + color: #eef3ff; + font-weight: 600; + padding-block: 8px; +} +.sources > p { + margin-top: 22px; +} +.sources li { + padding: 8px 0; +} +.sources li:target { + background: #243851; + outline: 2px solid #97bbff; + outline-offset: 5px; +} +.project-section { + display: grid; + grid-template-columns: 1.1fr 1fr; + gap: 100px; + padding-block: 80px; + align-items: center; +} +.project-section p { + max-width: 510px; +} +.project-links a { + display: flex; + justify-content: space-between; + padding-block: 22px; + border-bottom: 1px solid var(--line); + font-size: 18px; + font-weight: 600; + text-decoration: none; +} +.project-links span { + font-weight: 400; +} +footer { + border-top: 1px solid var(--line); + display: flex; + align-items: center; + justify-content: space-between; + gap: 24px; + padding-block: 30px; +} +footer p, +footer > a:last-child { + font-size: 12px; + color: var(--muted); + margin: 0; +} +footer .brand { + font-size: 18px; +} +.skip { + position: fixed; + left: 20px; + top: -100px; + z-index: 20; + background: white; + padding: 12px; +} +.skip:focus { + top: 15px; +} +.sr-only { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} +@media (min-width: 1450px) { + .hero { + padding-block: 100px; + } + .hero-grid { + gap: 70px; + } +} +@media (max-width: 1050px) { + .container { + width: calc(100% - 48px); + } + .hero-grid { + gap: 28px; + } + .hero { + padding-block: 60px; + } + h1 { + font-size: 42px; + } + .hero-meta { + max-width: 280px; + } + .step { + gap: 30px; + } + .code-window pre { + font-size: 12px; + padding: 18px; + } + .features-grid { + gap: 26px; + } + .project-section { + gap: 45px; + } + nav { + gap: 20px; + } +} +@media (max-width: 760px) { + html { + scroll-padding-top: 130px; + } + .container { + width: calc(100% - 36px); + } + .header-inner { + flex-wrap: wrap; + gap: 12px; + justify-content: center; + padding-block: 14px; + min-height: 0; + } + .brand { + font-size: 21px; + } + nav { + justify-content: center; + gap: 22px; + flex-wrap: wrap; + } + nav a { + font-size: 12px; + } + .hero { + padding: 48px 0; + } + .hero-grid, + .step, + .provider-grid, + .project-section { + grid-template-columns: 1fr; + gap: 28px; + } + .hero-copy { + max-width: 540px; + } + h1 { + font-size: 44px; + letter-spacing: -1.8px; + } + .intro { + font-size: 18px; + } + .hero-meta { + max-width: none; + } + .code-window pre { + font-size: 13px; + } + .database-strip .container { + justify-content: flex-start; + gap: 13px 22px; + } + .strip-label { + width: 100%; + } + .section { + padding-block: 54px; + } + h2 { + font-size: 31px; + } + .features-grid, + .fit-grid, + .comparison-notes { + grid-template-columns: 1fr; + gap: 20px; + } + .features-grid { + margin-top: 32px; + } + .features-grid p { + margin-bottom: 0; + } + .feature-number { + margin-bottom: 14px; + } + .quick-section, + .comparison-section { + padding-block: 50px; + } + .section-heading { + display: block; + } + .section-heading > p { + margin-top: 20px; + } + .step { + padding-block: 25px; + gap: 12px; + } + .step-description p:last-child { + margin-bottom: 5px; + } + .snippet pre { + font-size: 13px; + padding: 18px; + } + .provider-grid { + padding-block: 4px; + } + .provider-grid > div + div { + margin-top: 10px; + } + .comparison-notes { + gap: 0; + } + .fit-grid { + gap: 16px; + } + .project-section { + padding-block: 54px; + } + footer { + flex-wrap: wrap; + gap: 15px; + } + footer p { + order: 3; + width: 100%; + } +} +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } +} diff --git a/docs/assets/site.js b/docs/assets/site.js new file mode 100644 index 00000000..ff873e83 --- /dev/null +++ b/docs/assets/site.js @@ -0,0 +1,33 @@ +// The documentation remains readable and navigable without JavaScript. +const status = document.getElementById("copy-status"); +if (navigator.clipboard && window.isSecureContext) { + document.querySelectorAll("[data-copy]").forEach((button) => { + button.hidden = false; + button.addEventListener("click", async () => { + try { + await navigator.clipboard.writeText( + document.getElementById(button.dataset.copy).textContent, + ); + button.textContent = "Copied"; + status.textContent = "Code copied to clipboard."; + setTimeout(() => { + button.textContent = "Copy"; + }, 2000); + } catch { + status.textContent = + "Unable to copy. Select the code and copy it manually."; + } + }); + }); +} + +// Open the source notes when following a direct citation or shared fragment. +function revealSource() { + const id = window.location.hash.slice(1); + if (id === "sources" || id.startsWith("source-")) { + document.getElementById("sources").open = true; + document.getElementById(id)?.scrollIntoView(); + } +} +window.addEventListener("hashchange", revealSource); +revealSource(); diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..ead239e9 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,688 @@ + + + + + + + + Migrator.NET — Database changes, in your code. + + + + + + + +
+
+
+
+

DOTNETPROJECTS / MIGRATOR.NET

+

Database changes.
Part of your code.

+

+ Write schema changes in C#. Version them with your application. + Run them with the database provider and ORM you choose. +

+ +

+ Open source · MPL-1.1 · Current source targets .NET 9 +

+
+
+
+ + 001_CreateUsers.csUP / DOWN +
+
[Migration(1)]
+public class CreateUsers : Migration
+{
+    public override void Up()
+    {
+        Database.AddTable("Users",
+            new Column("Id", DbType.Int32,
+                ColumnProperty.NotNull),
+            new Column("Name", DbType.String, 255));
+        Database.AddPrimaryKey("PK_Users", "Users", "Id");
+    }
+
+    public override void Down()
+    {
+        Database.RemoveTable("Users");
+    }
+}
+ +
+
+
+
+
+ PROVIDER DIALECTSSQL ServerPostgreSQLSQLiteMySQL / MariaDBOracleSee all → +
+
+
+

SMALL API. EXPLICIT CONTROL.

+

+ Your schema has a history.
Keep it in the repository. +

+
+
+ 01 / AUTHOR +

C# without an ORM dependency

+

+ Define tables, columns, indexes and constraints through a + transformation API. Use raw SQL when a change needs + database-specific behavior. +

+
+
+ 02 / VERSION +

Move forward. Step back.

+

+ Number your migrations, implement Up() and + Down(), and migrate to a chosen version. Applied + migrations are recorded in the database. +

+
+
+ 03 / ORGANIZE +

Separate histories by scope

+

+ Keep module version histories in one database using named scopes. + Select each module’s migration assembly or types when you create + its runner. +

+
+
+
+
+
+
+
+

QUICK START

+

From code to schema.

+
+

+ A minimal SQLite example.
Use a .NET 9 console project for + the current source. +

+
+
+
+ 1 +

Install the packages

+

+ Add Migrator and an ADO.NET driver. This example passes an open + connection directly to the provider. +

+ View package versions on NuGet ↗ +
+
+
+ Terminal +
+
dotnet new console -n MigrationDemo -f net9.0
+cd MigrationDemo
+dotnet add package DotNetProjects.Migrator
+dotnet add package Microsoft.Data.Sqlite --version 9.0.7
+
+
+
+
+ 2 +

Describe the change

+

+ Add a public migration class. Each version must be unique within + the migration set loaded by a runner. +

+

+ Down() is your explicit reverse operation; dropping + a table also removes its data. +

+
+
+
+ CreateUsers.cs +
+
using System.Data;
+using DotNetProjects.Migrator.Framework;
+
+[Migration(1)]
+public class CreateUsers : Migration
+{
+    public override void Up()
+    {
+        Database.AddTable("Users",
+            new Column("Id", DbType.Int32,
+                ColumnProperty.NotNull),
+            new Column("Name", DbType.String, 255));
+        Database.AddPrimaryKey("PK_Users", "Users", "Id");
+    }
+
+    public override void Down()
+    {
+        Database.RemoveTable("Users");
+    }
+}
+
+
+
+
+ 3 +

Run pending migrations

+

+ Replace Program.cs with this code, then run + dotnet run. The runner discovers the migration in + your assembly and records it under the default scope. +

+

+ Subsequent runs skip applied versions. Use + MigrateTo(version) to target an earlier or later + version. +

+
+
+
+ Program.cs +
+
using DotNetProjects.Migrator;
+using DotNetProjects.Migrator.Providers;
+using Microsoft.Data.Sqlite;
+
+using var connection = new SqliteConnection("Data Source=app.db");
+connection.Open();
+
+using var provider = ProviderFactory.Create(
+    ProviderTypes.SQLite, connection, defaultSchema: null);
+
+var migrator = new Migrator(
+    provider, typeof(CreateUsers).Assembly, trace: false);
+
+if (migrator.LastAppliedMigrationVersion is long applied
+    && applied > migrator.AssemblyLastMigrationVersion)
+{
+    throw new InvalidOperationException(
+        "Database version is newer than this application.");
+}
+
+migrator.MigrateToLastVersion();
+
+
+ +
+
+
+
+
+

DATABASE PROVIDERS

+

One API. Multiple dialects.

+
+

+ Supply your ADO.NET driver.
Migrator supplies the schema + operations. +

+
+
+
+

Common database families

+
    +
  • SQL Server
  • +
  • PostgreSQL
  • +
  • SQLite
  • +
  • MySQL
  • +
  • MariaDB
  • +
  • Oracle
  • +
+
+
+

Additional dialects in source

+
    +
  • IBM Db2
  • +
  • IBM Informix
  • +
  • Firebird
  • +
  • Ingres
  • +
  • Sybase
  • +
+
+
+

+ This is an implementation inventory, not a certification of every + server or driver version. Schema operations and transactional DDL vary + by provider. Check the + provider factory + and + provider tests + for your database. +

+
+
+
+
+
+

THE .NET MIGRATION LANDSCAPE

+

Choose by how you work.

+
+

+ Feature comparison · Reviewed 22 September 2026
Read the sources and qualifications ↓ +

+
+

+ Migrator fits applications that want explicit C# migrations and + scoped history without coupling schema changes to an ORM. Other + tools offer different authoring and deployment workflows. +

+

+ Scroll horizontally to compare all five frameworks on smaller + screens. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Built-in capabilities and documented workflows. “Custom” means + application code or configuration is needed. +
Capability + Migrator.NET DotNetProjects forkSource [1] + + FluentMigrator Sources [2] + + EF Core Sources [3] + + DbUp Sources [4] + + Evolve Sources [5] +
Authoring styleHandwritten C#
Transformation API
Handwritten C#
Fluent DSL
C# generated from model changes; editableSQL scripts; C# scripts also supportedVersioned SQL files
ORM-independent workflowYesYesUses EF model and DbContextYesYes
+ Generate migrations from model differences + No built-in generatorHand-authoredYes — model snapshotsHand-authoredHand-authored
Raw SQLExecuteNonQueryExecute.Sql / scriptsmigrationBuilder.SqlPrimary workflowPrimary workflow
Downgrade an applied version + Authored Down()
MigrateTo +
+ Down(); auto-reverse for supported expressions + Down(); target an earlier migrationForward fixes; custom undo workflowForward fixes; no Down command
History / module separation + Scope in history table + selected assembly/types + Custom version tables + migration filtering + Separate contexts / migrations + custom history tables + Separate journals + script filteringMetadata table/schema + script locations
TransactionsPer migrationPer migration by default; configurableMost migrations wrapped automaticallyOpt-in per script or whole run; none by defaultPer migration by default; whole-run option
Execution / deploymentLibrary; write your own hostIn-process runner + CLICLI, SQL scripts, bundles, runtime APILibrary; host in a console app or application.NET library, .NET tool, CLI
Database abstractionProvider dialects for schema operationsProvider-specific SQL generators + Relational providers; migrations may differ by provider + Database integrations; you write dialect-specific SQLDatabase integrations; you write dialect-specific SQL
Repeatable / recurring workCustom application codeMaintenance migrations / profilesSeeding APIs (EF 9+); custom codeRunAlways scriptsRepeatable SQL reruns on checksum change
+
+
+

+ Rollback has two meanings. Reversing an already + applied migration uses authored reverse operations. Rolling back a + failed transaction depends on the database’s DDL support. Neither + restores data removed by a successful destructive migration. +

+

+ Recurring work is not the same as change detection. + Evolve stores script checksums and validates changes; Migrator + records versions and scopes without built-in content checksum + validation. Maintenance hooks, seeding and RunAlways have + different execution rules. +

+
+
+
+

Keep migrations in C#

+

+ Migrator: direct schema operations, scoped + history, and integration through your own host. + FluentMigrator: a fluent DSL with packaged + runners, tags and profiles. +

+
+
+

Let the model drive changes

+

+ EF Core: a natural fit when an EF model defines + your schema and you want migration scaffolding, SQL generation + and deployment bundles. +

+
+
+

Keep SQL as the source

+

+ DbUp: compose a script runner in .NET. + Evolve: convention-based versioned SQL, + checksum validation and repeatable scripts. +

+
+
+
+ Sources & comparison methodology +

+ Our column is based on the current repository source, which + targets net9.0. Other columns summarize official + documentation reviewed on 22 September 2026, rather than claiming + parity across every released package. Check your chosen release, + provider and database version. Suitability notes are our + interpretation of these documented capabilities. +

+
    +
  1. + DotNetProjects.Migrator: + target framework, + runner, + execution and transactions, + history and schema operations, + migration discovery. +
  2. +
  3. + FluentMigrator: + quick start and runners, + configuration and version tables, + auto-reversing migrations, + maintenance migrations, + profiles, + authoring and providers. +
  4. +
  5. + EF Core: + model snapshots, + authoring and transactions, + scripts, bundles and downgrade, + custom history tables, + multiple providers, + seeding. +
  6. +
  7. + DbUp: + execution, + transactions, + journaling, + script types, + forward-change philosophy, + SQL and C# script providers. +
  8. +
  9. + Evolve: + commands, checksums, repeatables and transactions, + configuration, + execution options. +
  10. +
+
+
+
+
+
+

CONTINUING MIGRATOR.NET

+

A familiar idea.
A maintained fork.

+

+ DotNetProjects.Migrator continues the original Migrator.NET project, + bringing together fork contributions with work on SQLite schema + handling, provider independence and migration scopes. +

+
+ +
+
+ + + +