.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.
Introduction
Section titled “Introduction”“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.
What You Will Learn
Section titled “What You Will Learn”This document covers the following topics:
- CLI tool management and usage - Parameters and execution methods for ReportGenerator, Verify.Tool, and Siren
- Source generator usage - Triggers and generated output for EntityIdGenerator and ObservablePortGenerator
- Source Generator debugging - Test project-based debugging and the Debugger.Launch() method
- .NET 10 file-based programs - Running scripts like SummarizeSlowestTests and ApiGenerator
- 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.
Summary
Section titled “Summary”Key Commands
Section titled “Key Commands”# Restore tools (after clone)dotnet tool restore
# Generate coverage reportdotnet reportgenerator -reports:**/*.cobertura.xml -targetdir:.coverage/reports/html -reporttypes:"Html;Cobertura"
# Approve Verify snapshotsdotnet verify accept -y
# Generate ER diagramdotnet siren-gen -a bin/Release/net10.0/MyApp.Persistence.dll -o ER-Diagram.md
# Analyze slow testsdotnet .coverage/scripts/SummarizeSlowestTests.cs --glob "**/*.trx" --threshold 30Key Procedures
Section titled “Key Procedures”1. Adding a new CLI tool:
dotnet tool install <package-name>(automatically registered in manifest)- Check
rollForwardsetting (set totrueif tool target framework is lower than current SDK) - Update related documentation
2. Source Generator debugging:
- Set breakpoints in the test project (recommended)
- Run Debug from Test Explorer
- Step into Generator internals with F11
Key Concepts
Section titled “Key Concepts”| Category | Tool | Purpose |
|---|---|---|
| CLI tool | ReportGenerator | Code coverage HTML report |
| CLI tool | Verify.Tool | Snapshot test approval |
| CLI tool | Siren | EF Core to Mermaid ER diagram |
| Source generator | EntityIdGenerator | Ulid-based EntityId auto-generation |
| Source generator | ObservablePortGenerator | Observability wrapping Pipeline generation |
| Source generator | UnionTypeGenerator | Auto-generation of Match/Switch methods for union types |
| .NET 10 script | SummarizeSlowestTests | Slow test analysis report |
Overview
Section titled “Overview”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.
| Category | Description | Examples |
|---|---|---|
| CLI tools | dotnet tool manifest management | ReportGenerator, Verify.Tool, Siren |
| Source generators | Automatic code generation at compile time | EntityIdGenerator, ObservablePortGenerator |
| .NET 10 scripts | Direct execution of .cs files | SummarizeSlowestTests, ApiGenerator |
Relationship with 02-solution-configuration.md: For
dotnet-tools.jsonmanifest 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 (.config/dotnet-tools.json)
Section titled “CLI Tools (.config/dotnet-tools.json)”Tool Management Basics
Section titled “Tool Management Basics”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:
| Value | Behavior | When to Use |
|---|---|---|
false (default) | Requires runtime exactly matching the tool’s target framework | When tool and SDK versions match |
true | Allows execution on higher version runtimes | When the tool targets an older version (e.g., running .NET 9 tool on .NET 10 SDK) |
Build-Local.ps1Step 1 automatically performsdotnet tool restore.
ReportGenerator (Code Coverage)
Section titled “ReportGenerator (Code Coverage)”| Item | Value |
|---|---|
| Package | dotnet-reportgenerator-globaltool |
| Command | reportgenerator |
| Purpose | Cobertura XML to HTML/Markdown coverage report conversion |
Standalone execution:
dotnet reportgenerator ` -reports:.coverage/reports/**/*.cobertura.xml ` -targetdir:.coverage/reports/html ` -reporttypes:"Html;Cobertura;MarkdownSummaryGithub"Key parameters:
| Parameter | Description | Example |
|---|---|---|
-reports | Input coverage files (glob) | **/*.cobertura.xml |
-targetdir | Output directory | .coverage/reports/html |
-reporttypes | Report formats | Html;Cobertura;MarkdownSummaryGithub |
-assemblyfilters | Assembly include/exclude | +MyApp*;-*.Tests* |
-filefilters | Source file include/exclude | -**/AssemblyReference.cs |
Build-Local.ps1Step 7 automatically generates HTML + Cobertura + Markdown reports.
Verify.Tool (Snapshot Management)
Section titled “Verify.Tool (Snapshot Management)”| Item | Value |
|---|---|
| Package | verify.tool |
| Command | dotnet-verify |
| Purpose | Approve Verify.Xunit snapshot .received to .verified |
Execution:
dotnet verify accept -yWhen 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.ps1automatically performs this command.
Siren (ER Diagrams)
Section titled “Siren (ER Diagrams)”| Item | Value |
|---|---|
| Package | gman.siren |
| Command | siren-gen |
| Purpose | EF Core DbContext to Mermaid ER diagram generation |
| rollForward | true (running .NET 9 tool on .NET 10) |
Input modes:
| Mode | Flag | Description |
|---|---|---|
| Assembly | -a <dll path> | Extract schema from assembly containing Migrations |
| Connection string | -c <connection string> | Read schema from existing database |
Execution examples:
# 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.mdKey parameters:
| Parameter | Description |
|---|---|
-o, --outputPath | Output Markdown file path (required) |
-a, --assemblyPath | Migration assembly DLL path |
-c, --connectionString | Database connection string |
-f, --filterEntities | Entity name filter to include (comma-separated) |
-s, --skipEntities | Entity names to exclude (comma-separated) |
-h, --filterSchemas | Schema filter to include |
-x, --skipSchemas | Schemas to exclude |
-t, --template | Rendering template (default: default) |
Constraints:
- Assembly mode (
-a): Requires EF Core Migrations. Does not work withEnsureCreated()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)”| Item | Value |
|---|---|
| Location | Tests.Hosts/01-SingleHost/Build-ERDiagram.ps1 |
| Purpose | Mermaid ER diagram generation based on EF Core Configuration |
| Output | Tests.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:
# Run in Tests.Hosts/01-SingleHost/ directory./Build-ERDiagram.ps1
# Help./Build-ERDiagram.ps1 -HelpReference 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.
Source Generators
Section titled “Source Generators”Functorium.SourceGenerators (Internal)
Section titled “Functorium.SourceGenerators (Internal)”| Item | Value |
|---|---|
| Project | Src/Functorium.SourceGenerators |
| Target | netstandard2.0 (Roslyn requirement) |
| NuGet packaging | Placed in analyzers/dotnet/cs path |
Provided generators:
The following table summarizes the source generators provided by Functorium and the code each generates.
| Generator | Trigger Attribute | Generated 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 |
EntityIdGenerator
Section titled “EntityIdGenerator”Applying [GenerateEntityId] to an Entity/AggregateRoot class automatically generates a Ulid-based EntityId.
[GenerateEntityId]public sealed class Product : AggregateRoot<ProductId> { ... }Generated code:
ProductIdrecord struct — implementsIEntityId<ProductId>,IParsable<ProductId>ProductIdConverter— EF CoreValueConverter<ProductId, string>ProductIdComparer— EF CoreValueComparer<ProductId>- JSON serialization/deserialization (
JsonConverter) - Comparison operators (
<,>,<=,>=)
ObservablePortGenerator
Section titled “ObservablePortGenerator”Applying [GenerateObservablePort] to an IObservablePort implementation class automatically generates an Observability wrapping Pipeline.
[GenerateObservablePort]public class EfCoreProductRepository : IProductRepository { ... }Generated code:
EfCoreProductRepositoryPipelineclass — inherits from the original class- Overrides each method to add:
ActivitySourcedistributed tracing (span creation)ILoggerstructured logging (request/response/error)IMeterFactorymetrics (counters, histograms)- Error classification (Expected vs Exceptional)
UnionTypeGenerator
Section titled “UnionTypeGenerator”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 typesSwitch(...)— void-returning version of pattern matching
Mediator.SourceGenerator
Section titled “Mediator.SourceGenerator”| Item | Value |
|---|---|
| Package | Mediator.SourceGenerator (v3.0.1) |
| Purpose | Auto-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" />Source Generator Debugging
Section titled “Source Generator Debugging”Debugging Method Comparison
Section titled “Debugging Method Comparison”| Method | Stability | Repeatability | Recommended |
|---|---|---|---|
| Debugging from test project | High | High | Recommended |
Using Debugger.Launch() | Medium | Medium | For emergencies |
| Attach to Process | Low | Low | Not recommended |
Method 1: Debugging from Test Project (Recommended)
Section titled “Method 1: Debugging from Test Project (Recommended)”Debug the source generator using existing unit tests.
- Set breakpoints in the test file (e.g., at the
_sut.Generate(input)call site) - Also set breakpoints in the source generator code
- Click Debug in Visual Studio Test Explorer or Debug Test above the code
- 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
Method 2: Using Debugger.Launch()
Section titled “Method 2: Using Debugger.Launch()”Automatically displays the debugger attach dialog when compilation starts.
- Set the Generator class’s
AttachDebuggerparameter totrue:[Generator(LanguageNames.CSharp)]public sealed class ObservablePortGenerator(): IncrementalGeneratorBase<ObservableClassInfo>(RegisterSourceProvider,Generate,AttachDebugger: true) // Enable debugging - Completely restart Visual Studio
- Build a project that uses the source generator
- Select the current VS instance in the Just-In-Time Debugger dialog
- After debugging, make sure to revert to
AttachDebugger: false(do not commit)
Debugging Tips
Section titled “Debugging Tips”Check generated code: In Solution Explorer > Dependencies > Analyzers > Functorium.SourceGenerators, check the generated .g.cs files
Check from build log:
dotnet build Observability.Adapters.Infrastructure -v:diag > build.log# Search for "SourceGenerator" in build.logUseful 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"
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Solution |
|---|---|---|
| Debugger not attached | Insufficient VS administrator privileges | Run VS as administrator |
| Breakpoint shows hollow circle | Symbols not loaded | Restart VS + delete bin/obj and rebuild |
| Code changes not reflected | Build cache | Close VS -> delete bin/obj -> restart VS -> Clean -> Rebuild |
| Cannot debug test | ProjectReference setting | Check 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 File-Based Programs
Section titled “.NET 10 File-Based Programs”.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.propsthat blocks the rootDirectory.Build.propsSource Link dependency. For details, see 02-solution-configuration.md §Nested configuration files.
SummarizeSlowestTests.cs
Section titled “SummarizeSlowestTests.cs”| Item | Value |
|---|---|
| Location | .coverage/scripts/SummarizeSlowestTests.cs |
| Purpose | Generate slow test analysis report from TRX test results |
| NuGet | System.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:
dotnet .coverage/scripts/SummarizeSlowestTests.cs ` --glob "**/*.trx" ` --threshold 30 ` --output .coverage/reportsKey parameters:
| Parameter | Description | Default |
|---|---|---|
--glob | TRX file search pattern | — |
--threshold | Slow test threshold (seconds) | 30 |
--output | Report output directory | — |
Automatically performed in
Build-Local.ps1Step 9.
ApiGenerator.cs
Section titled “ApiGenerator.cs”| Item | Value |
|---|---|
| Location | .release-notes/scripts/ApiGenerator.cs |
| Purpose | Generate Public API surface text from compiled DLLs |
| NuGet | PublicApiGenerator |
Behavior:
- Extracts Public API from the specified DLL
- Automatically resolves .NET 10 / ASP.NET Core reference assemblies
- Outputs API definitions as text or file
Execution:
dotnet .release-notes/scripts/ApiGenerator.cs ` --dll-path bin/Release/net10.0/MyLib.dll ` --output-path .api/MyLib.csExtractApiChanges.cs
Section titled “ExtractApiChanges.cs”| Item | Value |
|---|---|
| Location | .release-notes/scripts/ExtractApiChanges.cs |
| Purpose | Extract API changes between branches (for release notes) |
| NuGet | System.CommandLine, Spectre.Console |
Behavior:
- Search Functorium source projects (excluding tests)
- Publish each project in Release mode
- Call
ApiGenerator.csto generate API files - Merge all APIs into a single uber file
- Extract changes via Git diff
- 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.
Complete Tool Map
Section titled “Complete Tool Map”CLI Tools
Section titled “CLI Tools”The following table provides a complete map of CLI tools, source generators, and scripts organized by category.
| Package | Command | Purpose | Build-Local.ps1 Step |
|---|---|---|---|
dotnet-reportgenerator-globaltool | reportgenerator | Coverage report | Step 7 |
verify.tool | dotnet-verify | Snapshot approval | Build-VerifyAccept.ps1 |
gman.siren | siren-gen | ER diagram | Manual execution |
Source Generators
Section titled “Source Generators”| Generator | Attribute | Project |
|---|---|---|
| EntityIdGenerator | [GenerateEntityId] | Functorium.SourceGenerators |
| ObservablePortGenerator | [GenerateObservablePort] | Functorium.SourceGenerators |
| UnionTypeGenerator | [UnionType] | Functorium.SourceGenerators |
| Mediator.SourceGenerator | Interface-based | NuGet (v3.0.1) |
.NET 10 File-Based Programs
Section titled “.NET 10 File-Based Programs”| File | Purpose | Build-Local.ps1 Step |
|---|---|---|
.coverage/scripts/SummarizeSlowestTests.cs | Slow test analysis | Step 9 |
.release-notes/scripts/ApiGenerator.cs | Public API surface generation | Manual/Release |
.release-notes/scripts/ExtractApiChanges.cs | API change extraction | Manual/Release |
New Tool Addition Checklist
Section titled “New Tool Addition Checklist”Adding a CLI Tool
Section titled “Adding a CLI Tool”dotnet tool install <package-name>(automatically registered in manifest)- Check
rollForwardsetting in.config/dotnet-tools.json(set totrueif tool target framework is lower than current SDK) - Update the tool list table in 02-solution-configuration.md
- Add a detailed usage section in this document
Adding a Source Generator
Section titled “Adding a Source Generator”- Add
<PackageVersion>toDirectory.Packages.props - Add
<PackageReference>to the using project’s.csproj - Check whether
ExcludeAssets="analyzers"is needed for test project references
Adding a .NET 10 Script
Section titled “Adding a .NET 10 Script”- Create a
.csfile in the appropriate directory - Declare NuGet dependencies with the
#:packagedirective - Check whether a
Directory.Build.propsexists in the folder (create if root props blocking is needed) - Consider updating the target pattern in
Build-CleanRunFileCache.ps1
Troubleshooting
Section titled “Troubleshooting”rollForward Related Error
Section titled “rollForward Related Error”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}.NET 10 Script Package Loading Error
Section titled “.NET 10 Script Package Loading Error”Symptom: Packages like System.CommandLine are not loaded, or a previous version cache is used
Resolution: Clean the runfile cache with Build-CleanRunFileCache.ps1.
# Clean specific script cache./Build-CleanRunFileCache.ps1
# Clean all runfile cache./Build-CleanRunFileCache.ps1 -Pattern "All"
# Check deletion targets only./Build-CleanRunFileCache.ps1 -WhatIfCache 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):
-
Add NoWarn (recommended):
<PropertyGroup><NoWarn>$(NoWarn);CS0436</NoWarn></PropertyGroup> -
Disable Generator (Mediator example):
<PropertyGroup><Mediator_DisableGenerator>true</Mediator_DisableGenerator></PropertyGroup> -
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 withTreatWarningsAsErrorsenabled, it causes build failures and must be addressed.
Siren Assembly Mode Failure
Section titled “Siren Assembly Mode Failure”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:
- Use connection string mode: First create the DB, then run
siren-gen -c "Data Source=..." - 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.
References
Section titled “References”- 02-solution-configuration.md — dotnet-tools.json management, build script pipeline
- 01-project-structure.md — Project structure and dependencies
- 15a-unit-testing.md — Unit testing (including Verify.Xunit snapshots)
- 16-testing-library.md — Functorium.Testing library (including source generator testing)