Skip to content

.NET Tools Guide

This document covers the detailed usage of .NET tools used in the project. They are categorized into three groups: CLI tools, source generators, and .NET 10 file-based programs.

“Are you manually generating code coverage reports every time?” “Are you spending time individually approving snapshot test results?” “Are you manually updating ER diagrams when EF Core schemas change?”

Repetitive development tasks such as generating code coverage reports, approving snapshot tests, generating ER diagrams, and analyzing slow tests can be automated with .NET tools. Integrating the right tools into the build pipeline reduces manual work and lets you focus on the development flow.

This document covers the following topics:

  1. CLI tool management and usage - Parameters and execution methods for ReportGenerator, Verify.Tool, and Siren
  2. Source generator usage - Triggers and generated output for EntityIdGenerator and ObservablePortGenerator
  3. Source Generator debugging - Test project-based debugging and the Debugger.Launch() method
  4. .NET 10 file-based programs - Running scripts like SummarizeSlowestTests and ApiGenerator
  5. New tool addition checklist - Addition procedures for CLI tools, source generators, and scripts

The key to leveraging .NET tools is automating repetitive development tasks with CLI tools and source generators, and integrating them into the build pipeline.

Terminal window
# Restore tools (after clone)
dotnet tool restore
# Generate coverage report
dotnet reportgenerator -reports:**/*.cobertura.xml -targetdir:.coverage/reports/html -reporttypes:"Html;Cobertura"
# Approve Verify snapshots
dotnet verify accept -y
# Generate ER diagram
dotnet siren-gen -a bin/Release/net10.0/MyApp.Persistence.dll -o ER-Diagram.md
# Analyze slow tests
dotnet .coverage/scripts/SummarizeSlowestTests.cs --glob "**/*.trx" --threshold 30

1. Adding a new CLI tool:

  1. dotnet tool install <package-name> (automatically registered in manifest)
  2. Check rollForward setting (set to true if tool target framework is lower than current SDK)
  3. Update related documentation

2. Source Generator debugging:

  1. Set breakpoints in the test project (recommended)
  2. Run Debug from Test Explorer
  3. Step into Generator internals with F11
CategoryToolPurpose
CLI toolReportGeneratorCode coverage HTML report
CLI toolVerify.ToolSnapshot test approval
CLI toolSirenEF Core to Mermaid ER diagram
Source generatorEntityIdGeneratorUlid-based EntityId auto-generation
Source generatorObservablePortGeneratorObservability wrapping Pipeline generation
Source generatorUnionTypeGeneratorAuto-generation of Match/Switch methods for union types
.NET 10 scriptSummarizeSlowestTestsSlow test analysis report

This guide covers the detailed usage of .NET tools used in the project. Tools are classified into three categories.

The following table summarizes the characteristics and representative tools for each tool category.

CategoryDescriptionExamples
CLI toolsdotnet tool manifest managementReportGenerator, Verify.Tool, Siren
Source generatorsAutomatic code generation at compile timeEntityIdGenerator, ObservablePortGenerator
.NET 10 scriptsDirect execution of .cs filesSummarizeSlowestTests, ApiGenerator

Relationship with 02-solution-configuration.md: For dotnet-tools.json manifest creation/management methods and build script pipeline overview, see 02-solution-configuration.md. This document covers the purpose, commands, parameters, and execution examples for each tool.

CLI tools are managed via the .config/dotnet-tools.json manifest. For manifest creation and tool installation/restoration methods, see 02-solution-configuration.md §.config/dotnet-tools.json.

rollForward setting:

ValueBehaviorWhen to Use
false (default)Requires runtime exactly matching the tool’s target frameworkWhen tool and SDK versions match
trueAllows execution on higher version runtimesWhen the tool targets an older version (e.g., running .NET 9 tool on .NET 10 SDK)

Build-Local.ps1 Step 1 automatically performs dotnet tool restore.

ItemValue
Packagedotnet-reportgenerator-globaltool
Commandreportgenerator
PurposeCobertura XML to HTML/Markdown coverage report conversion

Standalone execution:

Terminal window
dotnet reportgenerator `
-reports:.coverage/reports/**/*.cobertura.xml `
-targetdir:.coverage/reports/html `
-reporttypes:"Html;Cobertura;MarkdownSummaryGithub"

Key parameters:

ParameterDescriptionExample
-reportsInput coverage files (glob)**/*.cobertura.xml
-targetdirOutput directory.coverage/reports/html
-reporttypesReport formatsHtml;Cobertura;MarkdownSummaryGithub
-assemblyfiltersAssembly include/exclude+MyApp*;-*.Tests*
-filefiltersSource file include/exclude-**/AssemblyReference.cs

Build-Local.ps1 Step 7 automatically generates HTML + Cobertura + Markdown reports.

ItemValue
Packageverify.tool
Commanddotnet-verify
PurposeApprove Verify.Xunit snapshot .received to .verified

Execution:

Terminal window
dotnet verify accept -y

When to use:

  • When *.received.* files are generated after running snapshot tests
  • When output has intentionally changed and the new snapshot needs to be approved

Build-VerifyAccept.ps1 automatically performs this command.

ItemValue
Packagegman.siren
Commandsiren-gen
PurposeEF Core DbContext to Mermaid ER diagram generation
rollForwardtrue (running .NET 9 tool on .NET 10)

Input modes:

ModeFlagDescription
Assembly-a <dll path>Extract schema from assembly containing Migrations
Connection string-c <connection string>Read schema from existing database

Execution examples:

Terminal window
# Assembly mode (project using Migrations)
dotnet siren-gen `
-a bin/Release/net10.0/MyApp.Persistence.dll `
-o ER-Diagram.md
# Connection string mode (existing DB)
dotnet siren-gen `
-c "Data Source=myapp.db" `
-o ER-Diagram.md

Key parameters:

ParameterDescription
-o, --outputPathOutput Markdown file path (required)
-a, --assemblyPathMigration assembly DLL path
-c, --connectionStringDatabase connection string
-f, --filterEntitiesEntity name filter to include (comma-separated)
-s, --skipEntitiesEntity names to exclude (comma-separated)
-h, --filterSchemasSchema filter to include
-x, --skipSchemasSchemas to exclude
-t, --templateRendering template (default: default)

Constraints:

  • Assembly mode (-a): Requires EF Core Migrations. Does not work with EnsureCreated() pattern projects
  • Connection string mode (-c): SQL Server only. SQLite/InMemory not supported

Siren is a general-purpose tool for rendering Mermaid diagrams to images, but here we only use the EF Core to Mermaid ER diagram generation feature.

Due to these constraints, the project uses the Build-ERDiagram.ps1 script (see §Build-ERDiagram.ps1) to directly generate Mermaid ER diagrams based on EF Core Configuration. Example: Tests.Hosts/01-SingleHost/ER-Diagram.md

Build-ERDiagram.ps1 (Direct ER Diagram Generation)

Section titled “Build-ERDiagram.ps1 (Direct ER Diagram Generation)”
ItemValue
LocationTests.Hosts/01-SingleHost/Build-ERDiagram.ps1
PurposeMermaid ER diagram generation based on EF Core Configuration
OutputTests.Hosts/01-SingleHost/ER-Diagram.md

This script bypasses Siren tool constraints (requiring Migrations or SQL Server only) by outputting ER diagram templates defined within the script to ER-Diagram.md. When schemas change, the $erDiagram variable inside the script must be manually updated.

Execution:

Terminal window
# Run in Tests.Hosts/01-SingleHost/ directory
./Build-ERDiagram.ps1
# Help
./Build-ERDiagram.ps1 -Help

Reference files: When EF Core Configuration changes, refer to the following files to update the script:

  • Src/LayeredArch.Adapters.Persistence/Repositories/EfCore/Configurations/

While CLI tools run independently in the build pipeline, source generators automatically generate code at compile time.

ItemValue
ProjectSrc/Functorium.SourceGenerators
Targetnetstandard2.0 (Roslyn requirement)
NuGet packagingPlaced in analyzers/dotnet/cs path

Provided generators:

The following table summarizes the source generators provided by Functorium and the code each generates.

GeneratorTrigger AttributeGenerated Output
EntityIdGenerator[GenerateEntityId]EntityId struct + EF Core Converter/Comparer
ObservablePortGenerator[GenerateObservablePort]Observability wrapping Pipeline class
UnionTypeGenerator[UnionType]Auto-generated Match/Switch methods for abstract partial record

Applying [GenerateEntityId] to an Entity/AggregateRoot class automatically generates a Ulid-based EntityId.

[GenerateEntityId]
public sealed class Product : AggregateRoot<ProductId> { ... }

Generated code:

  • ProductId record struct — implements IEntityId<ProductId>, IParsable<ProductId>
  • ProductIdConverter — EF Core ValueConverter<ProductId, string>
  • ProductIdComparer — EF Core ValueComparer<ProductId>
  • JSON serialization/deserialization (JsonConverter)
  • Comparison operators (<, >, <=, >=)

Applying [GenerateObservablePort] to an IObservablePort implementation class automatically generates an Observability wrapping Pipeline.

[GenerateObservablePort]
public class EfCoreProductRepository : IProductRepository { ... }

Generated code:

  • EfCoreProductRepositoryPipeline class — inherits from the original class
  • Overrides each method to add:
    • ActivitySource distributed tracing (span creation)
    • ILogger structured logging (request/response/error)
    • IMeterFactory metrics (counters, histograms)
    • Error classification (Expected vs Exceptional)

Applying [UnionType] to an abstract partial record automatically generates pattern matching methods.

[UnionType]
public abstract partial record Shape
{
public sealed record Circle(double Radius) : Shape;
public sealed record Rectangle(double Width, double Height) : Shape;
}

Generated code:

  • Match<TResult>(...) — exhaustive pattern matching for all derived types
  • Switch(...) — void-returning version of pattern matching
ItemValue
PackageMediator.SourceGenerator (v3.0.1)
PurposeAuto-generation of Mediator pattern handler code

Note: When referencing the host project from a test project, Mediator.SourceGenerator may run in duplicate, causing build errors. In this case, add ExcludeAssets="analyzers" to the host project reference.

<ProjectReference Include="..." ExcludeAssets="analyzers" />
MethodStabilityRepeatabilityRecommended
Debugging from test projectHighHighRecommended
Using Debugger.Launch()MediumMediumFor emergencies
Attach to ProcessLowLowNot recommended
Section titled “Method 1: Debugging from Test Project (Recommended)”

Debug the source generator using existing unit tests.

  1. Set breakpoints in the test file (e.g., at the _sut.Generate(input) call site)
  2. Also set breakpoints in the source generator code
  3. Click Debug in Visual Studio Test Explorer or Debug Test above the code
  4. Step into the source generator internals with F11 (Step Into)

Advantages: No compiler process timing issues, can test multiple times with the same input, full build not required

Automatically displays the debugger attach dialog when compilation starts.

  1. Set the Generator class’s AttachDebugger parameter to true:
    [Generator(LanguageNames.CSharp)]
    public sealed class ObservablePortGenerator()
    : IncrementalGeneratorBase<ObservableClassInfo>(
    RegisterSourceProvider,
    Generate,
    AttachDebugger: true) // Enable debugging
  2. Completely restart Visual Studio
  3. Build a project that uses the source generator
  4. Select the current VS instance in the Just-In-Time Debugger dialog
  5. After debugging, make sure to revert to AttachDebugger: false (do not commit)

Check generated code: In Solution Explorer > Dependencies > Analyzers > Functorium.SourceGenerators, check the generated .g.cs files

Check from build log:

Terminal window
dotnet build Observability.Adapters.Infrastructure -v:diag > build.log
# Search for "SourceGenerator" in build.log

Useful Watch window expressions:

classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
classSymbol.AllInterfaces.Select(i => i.Name).ToArray()
method.Parameters.Select(p => p.Type.ToDisplayString()).ToArray()

Conditional breakpoints: Right-click breakpoint > Conditions, set conditions like className == "RepositoryIo"

SymptomCauseSolution
Debugger not attachedInsufficient VS administrator privilegesRun VS as administrator
Breakpoint shows hollow circleSymbols not loadedRestart VS + delete bin/obj and rebuild
Code changes not reflectedBuild cacheClose VS -> delete bin/obj -> restart VS -> Clean -> Rebuild
Cannot debug testProjectReference settingCheck OutputItemType="Analyzer" ReferenceOutputAssembly="true"

While source generators operate at compile time, .NET 10 file-based programs execute .cs files directly without a separate build.

.NET 10 supports “file-based programs” that execute .cs files directly. NuGet dependencies are declared with the #:package directive.

Each script folder has its own Directory.Build.props that blocks the root Directory.Build.props Source Link dependency. For details, see 02-solution-configuration.md §Nested configuration files.

ItemValue
Location.coverage/scripts/SummarizeSlowestTests.cs
PurposeGenerate slow test analysis report from TRX test results
NuGetSystem.CommandLine, Microsoft.Extensions.FileSystemGlobbing

Generated reports:

  • Overall test statistics (passed, failed, skipped)
  • Execution time distribution per test project
  • Top 100 slowest test list
  • Failed test summary
  • Percentile analysis (50th, 90th, 95th, 99th)

Execution:

Terminal window
dotnet .coverage/scripts/SummarizeSlowestTests.cs `
--glob "**/*.trx" `
--threshold 30 `
--output .coverage/reports

Key parameters:

ParameterDescriptionDefault
--globTRX file search pattern
--thresholdSlow test threshold (seconds)30
--outputReport output directory

Automatically performed in Build-Local.ps1 Step 9.

ItemValue
Location.release-notes/scripts/ApiGenerator.cs
PurposeGenerate Public API surface text from compiled DLLs
NuGetPublicApiGenerator

Behavior:

  1. Extracts Public API from the specified DLL
  2. Automatically resolves .NET 10 / ASP.NET Core reference assemblies
  3. Outputs API definitions as text or file

Execution:

Terminal window
dotnet .release-notes/scripts/ApiGenerator.cs `
--dll-path bin/Release/net10.0/MyLib.dll `
--output-path .api/MyLib.cs
ItemValue
Location.release-notes/scripts/ExtractApiChanges.cs
PurposeExtract API changes between branches (for release notes)
NuGetSystem.CommandLine, Spectre.Console

Behavior:

  1. Search Functorium source projects (excluding tests)
  2. Publish each project in Release mode
  3. Call ApiGenerator.cs to generate API files
  4. Merge all APIs into a single uber file
  5. Extract changes via Git diff
  6. Generate summary report

Output: .analysis-output/api-changes-build-current/

Now that we have reviewed the detailed usage of individual tools, let us finally take an overall view of all tools.

The following table provides a complete map of CLI tools, source generators, and scripts organized by category.

PackageCommandPurposeBuild-Local.ps1 Step
dotnet-reportgenerator-globaltoolreportgeneratorCoverage reportStep 7
verify.tooldotnet-verifySnapshot approvalBuild-VerifyAccept.ps1
gman.sirensiren-genER diagramManual execution
GeneratorAttributeProject
EntityIdGenerator[GenerateEntityId]Functorium.SourceGenerators
ObservablePortGenerator[GenerateObservablePort]Functorium.SourceGenerators
UnionTypeGenerator[UnionType]Functorium.SourceGenerators
Mediator.SourceGeneratorInterface-basedNuGet (v3.0.1)
FilePurposeBuild-Local.ps1 Step
.coverage/scripts/SummarizeSlowestTests.csSlow test analysisStep 9
.release-notes/scripts/ApiGenerator.csPublic API surface generationManual/Release
.release-notes/scripts/ExtractApiChanges.csAPI change extractionManual/Release
  1. dotnet tool install <package-name> (automatically registered in manifest)
  2. Check rollForward setting in .config/dotnet-tools.json (set to true if tool target framework is lower than current SDK)
  3. Update the tool list table in 02-solution-configuration.md
  4. Add a detailed usage section in this document
  1. Add <PackageVersion> to Directory.Packages.props
  2. Add <PackageReference> to the using project’s .csproj
  3. Check whether ExcludeAssets="analyzers" is needed for test project references
  1. Create a .cs file in the appropriate directory
  2. Declare NuGet dependencies with the #:package directive
  3. Check whether a Directory.Build.props exists in the folder (create if root props blocking is needed)
  4. Consider updating the target pattern in Build-CleanRunFileCache.ps1

Symptom: “The tool … is not supported on the current .NET SDK” error during dotnet tool restore

Resolution: Set the tool’s rollForward to true in .config/dotnet-tools.json.

"tool-name": {
"version": "x.y.z",
"commands": ["cmd"],
"rollForward": true
}

Symptom: Packages like System.CommandLine are not loaded, or a previous version cache is used

Resolution: Clean the runfile cache with Build-CleanRunFileCache.ps1.

Terminal window
# Clean specific script cache
./Build-CleanRunFileCache.ps1
# Clean all runfile cache
./Build-CleanRunFileCache.ps1 -Pattern "All"
# Check deletion targets only
./Build-CleanRunFileCache.ps1 -WhatIf

Cache location: %TEMP%\dotnet\runfile\

Source Generator CS0436 Type Conflict Warning

Section titled “Source Generator CS0436 Type Conflict Warning”

Symptom: warning CS0436: The type 'AssemblyReference' conflicts

Cause: Occurs when Project A uses a Source Generator and Project B references A while using the same Source Generator. If InternalsVisibleTo is configured, internal generated types conflict.

Resolution (3 combinable approaches):

  1. Add NoWarn (recommended):

    <PropertyGroup>
    <NoWarn>$(NoWarn);CS0436</NoWarn>
    </PropertyGroup>
  2. Disable Generator (Mediator example):

    <PropertyGroup>
    <Mediator_DisableGenerator>true</Mediator_DisableGenerator>
    </PropertyGroup>
  3. ExcludeAssets setting:

    <ProjectReference Include="..\ProjectA\ProjectA.csproj">
    <ExcludeAssets>analyzers</ExcludeAssets>
    </ProjectReference>

Affected libraries: Commonly occurs in libraries using Source Generator patterns such as Mediator, CommunityToolkit.Maui, StronglyTypedId, xUnit, etc.

The CS0436 warning does not affect functionality and can be safely suppressed with NoWarn. However, in projects with TreatWarningsAsErrors enabled, it causes build failures and must be addressed.

Symptom: NullReferenceException or empty result when running siren-gen -a <dll>

Cause: Assembly mode may not work in projects that do not use EF Core Migrations (EnsureCreated() pattern).

Resolution:

  1. Use connection string mode: First create the DB, then run siren-gen -c "Data Source=..."
  2. Write the Mermaid ER diagram manually (manual alternative)

Q1. What is the difference between CLI tools and source generators?

Section titled “Q1. What is the difference between CLI tools and source generators?”

CLI tools are managed via the dotnet tool manifest and run independently from the command line. Source generators are referenced as NuGet packages and automatically generate code at compile time. CLI tools operate in the build pipeline, while source generators operate in real-time during development.

Q2. When should rollForward be set to true?

Section titled “Q2. When should rollForward be set to true?”

Set it when the tool’s target framework is lower than the current SDK version. For example, running a .NET 9 target tool on a .NET 10 SDK requires rollForward: true. Currently gman.siren uses this setting.

Q3. What should I do when a package error occurs in .NET 10 file-based programs?

Section titled “Q3. What should I do when a package error occurs in .NET 10 file-based programs?”

Clean the runfile cache with Build-CleanRunFileCache.ps1. The cache location is %TEMP%\dotnet\runfile\, and you can clean all caches with the -Pattern "All" option or clean only specific script caches with the default.

Q4. How can I check the code generated by Source Generators?

Section titled “Q4. How can I check the code generated by Source Generators?”

In Visual Studio’s Solution Explorer, check the generated .g.cs files under Dependencies > Analyzers > Functorium.SourceGenerators. Alternatively, generate a build log with dotnet build -v:diag > build.log and search for “SourceGenerator”.

Q5. What is the alternative when the Siren tool fails to generate ER diagrams?

Section titled “Q5. What is the alternative when the Siren tool fails to generate ER diagrams?”

Siren’s assembly mode requires EF Core Migrations, and connection string mode is SQL Server only. To bypass these constraints, use the Build-ERDiagram.ps1 script to directly generate Mermaid ER diagrams based on EF Core Configuration.