Breadcrumb Documentation¶
Needlr's source generator can include helpful documentation comments (breadcrumbs) in generated code to help developers understand why and how code was generated. This is particularly useful for debugging, learning, and working with AI assistants.
Overview¶
Breadcrumbs are self-documenting comments embedded in generated source code that explain:
- What triggered the code generation
- Source file locations of discovered types
- Lifetime configurations (Singleton, Scoped, Transient)
- Interceptor chains and their execution order
- Decorator chains and their order
Breadcrumb Levels¶
Needlr supports three breadcrumb levels:
| Level | Description | Use Case |
|---|---|---|
None |
Only // <auto-generated/> header |
Production builds where file size matters |
Minimal |
Brief inline comments (default) | Normal development |
Verbose |
Full context boxes with source paths | Debugging or learning how generation works |
Configuration¶
Set the breadcrumb level using the NeedlrBreadcrumbLevel MSBuild property.
Per-Project Configuration¶
In your .csproj file:
Solution-Wide Configuration¶
In a Directory.Build.props file at your solution root:
<Project>
<PropertyGroup>
<NeedlrBreadcrumbLevel>Verbose</NeedlrBreadcrumbLevel>
</PropertyGroup>
<!-- Import Needlr generator props -->
<Import Project="$(MSBuildThisFileDirectory)../packages/nexuslabs.needlr.generators/*/build/NexusLabs.Needlr.Generators.props"
Condition="Exists('$(MSBuildThisFileDirectory)../packages/nexuslabs.needlr.generators')" />
</Project>
Inheritance and Overrides¶
Child projects automatically inherit the breadcrumb level from parent Directory.Build.props files. Individual projects can override the inherited setting:
Solution/
├── Directory.Build.props # Sets NeedlrBreadcrumbLevel=Minimal (inherited by all)
├── src/
│ ├── MyApp/
│ │ └── MyApp.csproj # Inherits Minimal
│ └── MyApp.Debug/
│ └── MyApp.Debug.csproj # Can override to Verbose
Output Examples¶
None Level¶
// <auto-generated/>
#nullable enable
using System;
using System.Collections.Generic;
namespace MyApp.Generated
{
public static class TypeRegistry
{
private static readonly InjectableTypeInfo[] _types = new InjectableTypeInfo[]
{
new InjectableTypeInfo(typeof(global::MyApp.MyService), new Type[] { typeof(global::MyApp.IMyService) }, InjectableLifetime.Singleton),
};
// ... rest of generated code
}
}
Minimal Level (Default)¶
// <auto-generated/>
#nullable enable
using System;
using System.Collections.Generic;
namespace MyApp.Generated
{
public static class TypeRegistry
{
// Injectable types discovered in this assembly
private static readonly InjectableTypeInfo[] _types = new InjectableTypeInfo[]
{
// MyService -> IMyService (Singleton)
new InjectableTypeInfo(typeof(global::MyApp.MyService), new Type[] { typeof(global::MyApp.IMyService) }, InjectableLifetime.Singleton),
};
// ... rest of generated code
}
}
Verbose Level¶
// <auto-generated/>
// ═══════════════════════════════════════════════════════════════════════════════
// Needlr Type Registry
// Assembly: MyApp
// Breadcrumb Level: Verbose
// ═══════════════════════════════════════════════════════════════════════════════
#nullable enable
using System;
using System.Collections.Generic;
namespace MyApp.Generated
{
public static class TypeRegistry
{
// ┌─────────────────────────────────────────────────────────────────────────┐
// │ Injectable Types │
// │ Total types discovered: 1 │
// └─────────────────────────────────────────────────────────────────────────┘
private static readonly InjectableTypeInfo[] _types = new InjectableTypeInfo[]
{
// ┌─────────────────────────────────────────────────────────────────────────┐
// │ Type: MyService │
// │ Source: Services/MyService.cs │
// │ Implements: IMyService │
// │ Lifetime: Singleton │
// └─────────────────────────────────────────────────────────────────────────┘
new InjectableTypeInfo(typeof(global::MyApp.MyService), new Type[] { typeof(global::MyApp.IMyService) }, InjectableLifetime.Singleton),
};
// ... rest of generated code
}
}
Interceptor Proxy Documentation¶
When using interceptors, verbose breadcrumbs provide detailed information about proxy classes:
// ┌─────────────────────────────────────────────────────────────────────────┐
// │ Interceptor Proxy: OrderService │
// │ Source: Services/OrderService.cs │
// │ Target Interface: IOrderService │
// │ Interceptors (execution order): │
// │ 1. LoggingInterceptor │
// │ 2. CachingInterceptor │
// │ 3. ValidationInterceptor │
// │ Methods proxied: GetOrder, ProcessOrder, GetOrderAsync │
// │ Methods forwarded: ToString, GetHashCode │
// └─────────────────────────────────────────────────────────────────────────┘
public sealed class OrderService_InterceptorProxy : IOrderService
{
// ... generated proxy implementation
}
Source Path Resolution¶
Verbose breadcrumbs show source file paths relative to the project directory:
| Scenario | Path Display |
|---|---|
| File in project | Services/MyService.cs |
| File in subfolder | Domain/Entities/Order.cs |
| External assembly | [AssemblyName] |
| Unknown location | [unknown] |
Best Practices¶
- Development: Use
Minimal(default) for day-to-day development - Debugging: Switch to
Verbosewhen troubleshooting generated code issues - CI/CD: Consider
Nonefor release builds to minimize generated file size - Learning: Use
Verbosewhen first learning how Needlr's generator works
Configuration in CI/CD¶
You can set the breadcrumb level based on build configuration:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<NeedlrBreadcrumbLevel>None</NeedlrBreadcrumbLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<NeedlrBreadcrumbLevel>Verbose</NeedlrBreadcrumbLevel>
</PropertyGroup>
Troubleshooting¶
Breadcrumbs Not Appearing¶
- Ensure
NexusLabs.Needlr.Generatorspackage is referenced - Verify the
.propsfile is being imported (check build output) - Rebuild the project (breadcrumb level is read at generation time)
Case Sensitivity¶
The breadcrumb level value is case-insensitive:
Verbose,verbose,VERBOSE,VeRbOsEall work the same
Invalid Values¶
If an invalid value is specified, Needlr defaults to Minimal:
InvalidLevel→ defaults toMinimal- Empty string → defaults to
Minimal - Whitespace only → defaults to
Minimal
Compile-Time Diagnostics¶
In addition to inline breadcrumbs, Needlr can generate separate diagnostic files at build time. These files provide higher-level views of your dependency injection configuration without cluttering your generated code.
Enabling Diagnostics¶
Add the NeedlrDiagnostics property to your project:
This generates three markdown files in your build output folder (e.g., bin/Debug/net10.0/NeedlrDiagnostics/):
| File | Description |
|---|---|
DependencyGraph.md |
Mermaid diagram showing service dependencies |
LifetimeSummary.md |
Breakdown of Singleton/Scoped/Transient registrations |
RegistrationIndex.md |
Complete index of all services, decorators, and plugins |
Custom Output Path¶
Specify a custom output directory (relative paths are resolved from the project directory):
<PropertyGroup>
<NeedlrDiagnostics>true</NeedlrDiagnostics>
<NeedlrDiagnosticsPath>docs/diagnostics</NeedlrDiagnosticsPath>
</PropertyGroup>
Filtering Types¶
Filter diagnostics to specific types (comma-separated fully qualified names):
<PropertyGroup>
<NeedlrDiagnostics>true</NeedlrDiagnostics>
<NeedlrDiagnosticsFilter>MyApp.OrderService,MyApp.PaymentService</NeedlrDiagnosticsFilter>
</PropertyGroup>
Example Output¶
DependencyGraph.md¶
The dependency graph now includes rich visualization sections:
# Needlr Dependency Graph
Generated: 2026-01-25 10:00:00 UTC
Assembly: MyApp
## Referenced Plugin Assemblies
Types from referenced assemblies with `[GenerateTypeRegistry]`:
### MyApp.Plugins
\`\`\`mermaid
graph TD
subgraph Singleton["MyApp.Plugins - Singleton"]
OrderHandler["OrderHandler"]
PaymentHandler["PaymentHandler"]
Connection{{"Connection"}}
ConnectionFactory["ConnectionFactory"]
end
PaymentHandler --> OrderHandler
ConnectionFactory -.->|produces| Connection
\`\`\`
| Service | Lifetime | Interfaces |
|---------|----------|------------|
| OrderHandler | Singleton | IHandler<Order> |
| PaymentHandler | Singleton | IHandler<Payment> |
| Connection | Singleton | IConnection |
| ConnectionFactory | Singleton | IConnectionFactory |
## Service Dependencies
\`\`\`mermaid
graph TD
subgraph Singleton
Logger["Logger"]
ConfigService["ConfigService"]
end
subgraph Scoped
OrderService["OrderService"]
end
OrderService --> Logger
OrderService --> ConfigService
\`\`\`
## Decorator Chains
Shows decorator wrapping order from outermost to innermost:
\`\`\`mermaid
graph LR
CachingDecorator[["CachingDecorator"]] --> LoggingDecorator[["LoggingDecorator"]] --> OrderService["OrderService"]
\`\`\`
## Keyed Services
Groups services by their `[Keyed]` attribute values:
\`\`\`mermaid
graph TD
subgraph key_redis["redis"]
RedisCache["RedisCache"]
end
subgraph key_memory["memory"]
MemoryCache["MemoryCache"]
end
\`\`\`
## Plugin Assemblies
Shows plugins grouped by their source assembly:
\`\`\`mermaid
graph TD
subgraph asm_MyApp_Plugins["MyApp.Plugins"]
OrderPlugin(["OrderPlugin"])
PaymentPlugin(["PaymentPlugin"])
end
\`\`\`
## Factory Services
Shows all factory→product relationships from host and referenced assemblies:
\`\`\`mermaid
graph LR
ConnectionFactory["ConnectionFactory"]
Connection{{"Connection"}}
ConnectionFactory -.->|produces| Connection
HttpClientFactory["HttpClientFactory"]
HttpClient{{"HttpClient"}}
HttpClientFactory -.->|produces| HttpClient
\`\`\`
> Note: This section aggregates factories from the host assembly and all referenced plugin assemblies
> with `[GenerateTypeRegistry]`. Hexagon shapes indicate factory-produced types.
## Interface Mapping
Shows interface-to-implementation relationships with dotted edges:
\`\`\`mermaid
graph LR
IOrderService(("IOrderService")) -.-> OrderService["OrderService"]
ILogger(("ILogger")) -.-> Logger["Logger"]
\`\`\`
## Complexity Metrics
| Metric | Value |
|--------|-------|
| Total Services | 15 |
| Max Dependency Depth | 4 |
| Hub Services (≥3 dependents) | 2 |
**Hub Services:** Logger (8), ConfigService (5)
## Dependency Details
| Service | Lifetime | Dependencies |
|---------|----------|--------------|
| Logger | Singleton | - |
| OrderService | Scoped | ILogger, IConfigService |
LifetimeSummary.md¶
# Needlr Lifetime Summary
Generated: 2026-01-25 10:00:00 UTC
Assembly: MyApp
| Lifetime | Count | Percentage |
|----------|-------|------------|
| Singleton | 5 | 50.0% |
| Scoped | 3 | 30.0% |
| Transient | 2 | 20.0% |
| **Total** | **10** | **100%** |
RegistrationIndex.md¶
# Needlr Registration Index
Generated: 2026-01-25 10:00:00 UTC
Assembly: MyApp
## Services (10)
| # | Interface | Implementation | Lifetime | Source |
|---|-----------|----------------|----------|--------|
| 1 | ILogger | Logger | Singleton | Services/Logger.cs |
| 2 | IOrderService | OrderService | Scoped | Services/OrderService.cs |
## Decorators (2)
| Service | Decorator Chain |
|---------|-----------------|
| IOrderService | LoggingDecorator → CachingDecorator |
Configuration Properties Summary¶
| Property | Default | Description |
|---|---|---|
NeedlrDiagnostics |
false |
Enable diagnostic file generation |
NeedlrDiagnosticsPath |
$(OutputPath)NeedlrDiagnostics |
Output directory (defaults to bin folder) |
NeedlrDiagnosticsFilter |
(all) | Comma-separated type names to include |
Analyzer Status¶
When diagnostics are enabled, Needlr also generates AnalyzerStatus.md showing which analyzers are active and their current severity. This provides a single place to understand what compile-time protection is enabled for your project.
Example AnalyzerStatus.md¶
# Needlr Analyzer Status
Generated: 2026-01-25 10:00:00 UTC
## Active Analyzers
| ID | Name | Status | Default Severity | Description |
|:---|:-----|:-------|:-----------------|:------------|
| NDLRCOR001 | Reflection in AOT | ⚪ Conditional | Error | Detects reflection APIs in AOT projects |
| NDLRCOR005 | Lifetime Mismatch | ✅ Active | Warning | Detects captive dependencies |
| NDLRCOR009 | Lazy Resolution | ✅ Active | Info | Lazy<T> references undiscovered type |
| NDLRCOR010 | Collection Resolution | ✅ Active | Info | IEnumerable<T> has no implementations |
## Mode
**Source Generation**: Enabled (GenerateTypeRegistry detected)
## Configuration
Analyzer severity can be configured via `.editorconfig`:
\`\`\`ini
# Example: Suppress Lazy resolution warnings
dotnet_diagnostic.NDLRCOR009.severity = none
# Example: Promote to warning
dotnet_diagnostic.NDLRCOR009.severity = warning
\`\`\`
Understanding Analyzer Status¶
| Status | Meaning |
|---|---|
| ✅ Active | Analyzer is running and checking your code |
| ⚪ Conditional | Analyzer only activates under certain conditions (e.g., AOT projects) |
| ❌ Disabled | Analyzer is disabled via configuration |
Configuring Analyzers¶
Use .editorconfig to adjust analyzer severity:
# .editorconfig in your project root
[*.cs]
# Disable resolution validation (if using reflection)
dotnet_diagnostic.NDLRCOR009.severity = none
dotnet_diagnostic.NDLRCOR010.severity = none
# Promote lifetime mismatch to error
dotnet_diagnostic.NDLRCOR005.severity = error
See Analyzers Documentation for the complete list of available analyzers.