Two packages ship from this repo:
EfMigrationSafety.Cli— adotnet toolfor running checks from the command line or CI.EfMigrationSafety.Analyzers— the underlying analyzer library, for projects (e.g. custom tooling, MCP servers) that want to run the checks programmatically.
Static safety analyzer for EF Core migrations. Catch destructive operations before they hit your database.
EF Core migrations are powerful — and silent. A DropColumn followed by an AddColumn on the same column looks reasonable in a diff, compiles without warnings, and passes CI. Then it runs against production and destroys every value in that column. A non-nullable column added without a default value fails silently in development (empty table) and explodes on first deploy to a real database. By the time you realize what happened, the rollback window has closed.
ef-migration-safety runs as a CI step and flags these patterns statically, before the migration ever touches a database. It reads .cs migration files, parses them with Roslyn, and reports anything that has historically caused production incidents.
dotnet tool install -g EfMigrationSafety.Clidotnet add package EfMigrationSafety.Analyzersusing EfMigrationSafety.Analyzers;
using Microsoft.CodeAnalysis.CSharp;
var root = CSharpSyntaxTree.ParseText(migrationSource).GetRoot();
IMigrationAnalyzer[] analyzers =
[
new DropAddColumnAnalyzer(),
new NonNullableWithoutDefaultAnalyzer(),
// ...the remaining analyzers
];
var issues = analyzers.SelectMany(a => a.Analyze(root, filePath));# Analyze all migrations in a directory
ef-migration-safety check ./src/MyApp/Migrations
# Fail the build on any warnings (useful in CI)
ef-migration-safety check ./src/MyApp/Migrations --strict
# Output as JSON for downstream tooling
ef-migration-safety check ./src/MyApp/Migrations --output json20240315120000_ChangeEmailColumnType.cs
⚠ Line 18: DropColumn + AddColumn pattern detected for column 'Email' (potential data loss). Use RenameColumn instead to preserve data.
Recommendation: If this is a rename, replace with migrationBuilder.RenameColumn(). If this is intentional column replacement, document the data backfill strategy.
20240320100000_RenameEmailColumn.cs
ℹ Line 12: Column rename detected: 'Email' → 'EmailAddress' on table 'Users'. Ensure all application code, queries, and ORM mappings referencing 'Email' are updated in the same deployment.
Recommendation: Search the codebase for the old name before deploying. Common locations: entity classes, LINQ queries, raw SQL, stored procedures, reports, integration mappings.
Summary: 10 file(s) clean, 1 with info, 1 with warnings.
| Analyzer | Status | What it catches |
|---|---|---|
DropAddColumn |
Implemented | DropColumn + AddColumn on the same column name inside Up() — destroys all column data |
NonNullableWithoutDefault |
Implemented | Adding a non-nullable column to an existing table without a default value — fails on non-empty tables |
EmptyDownMethod |
Implemented | Down() method that is empty or throws NotImplementedException — makes rollbacks impossible |
RenameOperation |
Implemented (Info) | RenameColumn / RenameTable without coordinated application code update — breaks running instances |
AlterColumnTruncation |
Implemented | AlterColumn reducing the size of nvarchar, varchar, char, or nchar columns — causes silent data truncation in production if existing rows exceed the new size |
SqlInjection |
Implemented | Raw migrationBuilder.Sql() called with an interpolated string, string concatenation, or string.Format — potential SQL injection via dynamic SQL construction |
NoDataLoss |
Implemented | AlterColumn reducing decimal precision or scale, or making a column non-nullable without a defaultValue — risks truncating or rejecting existing data |
MissingIndexOnForeignKey |
Implemented | AddColumn for a likely foreign key column with no matching CreateIndex — causes slow lookups and full table scans as the table grows |
PotentiallyLossyTypeConversion |
Implemented | AlterColumn narrowing an integer type (bigint → int) or converting a string column to a non-string type — fails or truncates on existing data |
See ROADMAP.md for the full list of planned checks. Near-term additions:
- Detect index creation on large tables without
ONLINE = ON - Detect missing transactions wrapping multi-step migrations
--baselineflag to suppress known-safe issues with an inline comment
Contributions welcome. See CONTRIBUTING.md for setup instructions and the analyzer interface.
MIT — see LICENSE.