Analyzers
Available Since
FluentMigrator 7.2.0
FluentMigrator provides Roslyn-based code analyzers to help catch common mistakes and enforce best practices at compile time. These analyzers are distributed as a separate NuGet package and integrate directly into your IDE and build process.
Installation
The analyzers are available as a NuGet package that can be added to your migration projects:
dotnet add package FluentMigrator.AnalyzersOr via Package Manager Console:
Install-Package FluentMigrator.AnalyzersOnce installed, the analyzers will automatically run during builds and provide real-time feedback in your IDE.
Available Analyzers
FM0001: Migration Version Should Be Unique
Category: FluentMigrator Severity: Warning
This analyzer ensures that each migration in your project has a unique version number. Duplicate version numbers can cause unpredictable behavior during migration execution.
Example Problem
[Migration(20240101120000)]
public class CreateUsersTable : Migration
{
public override void Up()
{
Create.Table("Users")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Name").AsString(100);
}
public override void Down()
{
Delete.Table("Users");
}
}
// ❌ This will trigger FM0001
[Migration(20240101120000)] // Same version as above!
public class CreateProductsTable : Migration
{
public override void Up()
{
Create.Table("Products")
.WithColumn("Id").AsInt32().PrimaryKey().Identity()
.WithColumn("Name").AsString(100);
}
public override void Down()
{
Delete.Table("Products");
}
}Diagnostic Message:
FM0001: Migration attributes on CreateUsersTable, CreateProductsTable have conflicting version 20240101120000Solution
Ensure each migration has a unique version number. The recommended format is a timestamp: yyyyMMddHHmmss
[Migration(20240101120000)]
public class CreateUsersTable : Migration
{
// ... migration code ...
}
// ✅ Use a unique version
[Migration(20240101120100)] // One minute later
public class CreateProductsTable : Migration
{
// ... migration code ...
}Best Practices for Version Numbers
Use Timestamps: Format
yyyyMMddHHmmssensures chronological orderingcsharp[Migration(20240322143000)] // March 22, 2024 at 14:30:00Team Coordination: In team environments, coordinate version numbers to avoid conflicts
- Consider using Git commit timestamps
- Use a convention like developer initials in the seconds portion
- Resolve conflicts during merge
Sequential Ordering: Ensure migrations run in the intended order
csharp[Migration(20240322140000)] // Runs first [Migration(20240322140100)] // Runs second [Migration(20240322140200)] // Runs third
FM0002: Prefer $[name] Over $(name) For SQL Token Substitution
Category: FluentMigrator Severity: Warning
Execute.Sql(...) supports token substitution via SqlScriptTokenReplacer, which offers two token styles:
$(name)substitutes the parameter value verbatim/unquoted. Since the value is inserted as-is, it can be a SQL injection risk if the value originates from outside the migration itself (for example, user input, environment variables, or other external/untrusted sources).$[name]substitutes the parameter value as a safely quoted SQL string literal (wrapped in single quotes, with embedded single quotes escaped by doubling them), rendered viaIQuoter.
This analyzer flags uses of the raw $(name) token style (and its escaped form, $$((name)), which renders the literal text $(name) without substitution) and recommends switching to the automatically quoted $[name]/$$[[name]] forms instead, to reduce the software supply-chain risk of accidentally interpolating untrusted data unquoted into SQL.
Example Problem
[Migration(20240101120000)]
public class UpdateUserEmail : Migration
{
public override void Up()
{
// ❌ This will trigger FM0002: the value is substituted unquoted.
Execute.Sql(
"UPDATE Users SET Email = $(email) WHERE Id = $(id)",
new Dictionary<string, string> { ["email"] = email, ["id"] = id });
}
public override void Down()
{
}
}Diagnostic Message:
FM0002: Replace raw variable interpolation token '$(email)' with the automatically quoted '$[email]' token, unless the raw substitution is intentionalSolution
Use the $[name] token style so the value is safely quoted for you:
// ✅ The value is safely quoted as a SQL string literal.
Execute.Sql(
"UPDATE Users SET Email = $[email] WHERE Id = $[id]",
new Dictionary<string, string> { ["email"] = email, ["id"] = id });A code fix is available (in supporting IDEs) to automatically replace $(name) with $[name] (and $$((name)) with $$[[name]]).
When Raw Substitution Is Legitimate
The $(name) token style still has legitimate uses, since it is not always desirable (or valid SQL) to quote the substituted value — for example, when substituting an identifier, a SQL keyword/fragment, or when concatenating two similar queries that differ only in a clause, using UNION/UNION ALL:
// $(whereClause) intentionally substitutes a raw SQL fragment, not string data.
Execute.Sql(
"SELECT Id, Name FROM Users WHERE $(whereClause1) " +
"UNION ALL " +
"SELECT Id, Name FROM Users WHERE $(whereClause2)",
new Dictionary<string, string>
{
["whereClause1"] = "IsActive = 1",
["whereClause2"] = "IsActive = 0 AND LastLoginDate > '2024-01-01'",
});In such cases, since the value being substituted is trusted, fixed, developer-authored SQL (not external/untrusted data), the FM0002 warning can be suppressed for that call — see Suppressing Analyzer Warnings below.
IDE Integration
The analyzers work seamlessly with popular IDEs:
Visual Studio
Warnings appear in:
- Error List window during builds
- Inline in the code editor with squiggly underlines
- IntelliSense with detailed diagnostic messages
Visual Studio Code
When using the C# extension:
- Warnings appear in the Problems panel
- Inline diagnostics in the editor
- Quick fixes when available
Rider
- Warnings shown in the Solution-wide Analysis window
- Inline highlighting in the editor
- Integration with code inspections
Build Integration
Analyzers run automatically during build and can be configured to treat warnings as errors:
MSBuild Configuration
<PropertyGroup>
<!-- Treat all warnings as errors -->
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Or treat specific analyzer warnings as errors -->
<WarningsAsErrors>FM0001</WarningsAsErrors>
</PropertyGroup>CI/CD Integration
Since analyzers run during compilation, they automatically integrate with CI/CD pipelines:
# Analyzers will report warnings/errors during build
dotnet buildSuppressing Analyzer Warnings
In rare cases where you need to suppress an analyzer warning:
Using Pragma Directives
#pragma warning disable FM0001
[Migration(20240101120000)]
public class SpecialMigration : Migration
{
// ... migration code ...
}
#pragma warning restore FM0001Using Attributes
[Migration(20240101120000)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("FluentMigrator", "FM0001:Migration version isn't unique")]
public class SpecialMigration : Migration
{
// ... migration code ...
}Using .editorconfig
Create or modify .editorconfig in your project root:
[*.cs]
# Disable FM0001 for all files
dotnet_diagnostic.FM0001.severity = none
# Or reduce severity to suggestion
dotnet_diagnostic.FM0001.severity = suggestionWARNING
Suppressing analyzer warnings should be done sparingly and only when you have a specific reason. Duplicate migration versions can cause serious issues in production environments.
Troubleshooting
Analyzers Not Running
If analyzers aren't providing diagnostics:
Verify Package Installation
bashdotnet list package | grep FluentMigrator.AnalyzersClean and Rebuild
bashdotnet clean dotnet buildCheck IDE Analyzer Settings
- Visual Studio: Tools → Options → Text Editor → C# → Advanced → Enable "Run code analysis in background"
- VS Code: Ensure Roslyn analyzers are enabled in the C# extension settings
Verify SDK Version
- Analyzers require .NET SDK 5.0 or later
bashdotnet --version
Performance Concerns
Analyzers run during compilation and design-time builds. If you experience performance issues:
- Disable Background Analysis (temporary, for large solutions)
- Use Solution Filters to work on subsets of large solutions
- Adjust Analysis Scope in IDE settings
Future Analyzers
The FluentMigrator team is actively developing additional analyzers to help catch more issues at compile time. Check the GitHub releases for announcements of new analyzers.
Potential future analyzers may include:
- Migration method implementation validation
- Transaction behavior best practices
- Database-specific syntax validation
- Performance anti-patterns detection
Contributing
Interested in contributing new analyzers? Check out the FluentMigrator contribution guide and the existing analyzer implementation in the src/FluentMigrator.Analyzers directory.