Overview
The DotNetDocs pipeline is a flexible, extensible system for transforming .NET assemblies and XML documentation into rich, multi-format documentation. At its core, the pipeline follows a linear flow through five distinct stages, each building on the previous one to create comprehensive documentation from your code. The pipeline is orchestrated by theDocumentationManager, which coordinates all stages and manages caching, parallel processing, and multi-assembly merging.
Pipeline Stages
Stage 1: Assembly Extraction
Purpose: Load .NET assemblies and extract API metadata using Roslyn Component:AssemblyManager
The first stage loads your compiled .NET assembly and uses Roslyn’s powerful code analysis APIs to extract comprehensive metadata about every type, member, parameter, and attribute in your code.
What Gets Extracted
What Gets Extracted
- Namespaces: Hierarchical organization of types
 - Types: Classes, interfaces, structs, enums, delegates with full metadata
 - Members: Methods, properties, fields, events, constructors
 - Parameters: Method/constructor parameters with types and defaults
 - Type Parameters: Generic type parameters with constraints
 - Attributes: Custom attributes applied to code elements
 - Signatures: Full method/property signatures with modifiers
 - Accessibility: Public, internal, protected, private visibility
 - XML Comments: Summary, remarks, examples, returns, exceptions, seealso
 
XML Documentation Processing
XML Documentation Processing
If an XML documentation file is available, 
AssemblyManager parses it to extract:<summary>: Brief description of what the API element IS<remarks>: Additional detailed information<example>: Code usage examples from XML<returns>: Method return value documentation<param>: Parameter descriptions<exception>: Exception types and conditions<typeparam>: Generic type parameter descriptions<value>: Property value descriptions<seealso>: Cross-references to related APIs
Roslyn Compilation
Roslyn Compilation
The This gives DotNetDocs full access to type hierarchies, member relationships, and semantic information.
AssemblyManager creates a Roslyn Compilation object to access symbol information:Incremental Processing
Incremental Processing
AssemblyManager implements smart caching to avoid redundant processing:- Tracks assembly file 
LastModifiedtimestamp - Compares 
IncludedMembersaccessibility levels - Only rebuilds the model when assembly changes or configuration changes
 - Reuses existing 
DocAssemblymodel when possible 
DocAssembly object containing DocNamespace → DocType → DocMember hierarchy with XML documentation.
Stage 2: Conceptual Content Loading
Purpose: Enrich API documentation with conceptual content from.mdz files
Component: DocumentationManager.LoadConceptualAsync()
After extracting API metadata, the pipeline loads conceptual documentation from the file system to add context-rich content that goes beyond XML comments.
Folder Structure Discovery
Folder Structure Discovery
The The pipeline walks this hierarchy, loading content for each API element.
DocumentationManager scans the configured ConceptualPath directory, looking for .mdz files organized by namespace/type/member:Conceptual Sections
Conceptual Sections
Seven conceptual sections can be loaded at different levels:
Each file contains Markdown content that renderers can format appropriately.
| Section | File | Namespace | Type | Member | 
|---|---|---|---|---|
| Summary | summary.mdz | ✅ | ❌ | ❌ | 
| Usage | usage.mdz | ✅ | ✅ | ✅ | 
| Examples | examples.mdz | ✅ | ✅ | ✅ | 
| Best Practices | best-practices.mdz | ✅ | ✅ | ✅ | 
| Patterns | patterns.mdz | ✅ | ✅ | ✅ | 
| Considerations | considerations.mdz | ✅ | ✅ | ✅ | 
| Related APIs | related-apis.mdz | ❌ | ✅ | ✅ | 
Placeholder Handling
Placeholder Handling
The pipeline can generate placeholder files for missing conceptual content and control their visibility:Placeholder Generation: When Placeholder Loading: The This allows you to see documentation gaps during development but hide them in production builds.
ConceptualDocsEnabled = true, renderers create placeholder .mdz files marked with:ShowPlaceholders property controls whether placeholders appear in final documentation:ShowPlaceholders = true(default): Load all conceptual content including placeholdersShowPlaceholders = false: Skip files containing the TODO marker
Parallel Loading
Parallel Loading
Conceptual content loading happens in parallel per assembly for performance:Each assembly has its own conceptual directory subtree, so parallel loading is safe.
DocAssembly model now has both XML documentation AND conceptual content in each entity’s properties.
Stage 3: Assembly Merging
Purpose: Combine multiple assemblies into a unified documentation model Component:DocumentationManager.MergeDocAssembliesAsync()
When documenting multiple assemblies together, the pipeline merges them into a single cohesive model before enrichment and transformation.
1
Namespace Merging
Namespaces with the same fully qualified name are merged:
2
Type Merging
Types with the same fully qualified name are merged, combining their members:
3
Member Merging
Members with the same symbol signature are deduplicated:
Merging happens AFTER conceptual content loading, so each assembly’s conceptual documentation is preserved during the merge. Conflicting documentation prefers the first assembly encountered.
DocAssembly containing all types and members from all processed assemblies.
Stage 4: Enrichment
Purpose: Add or enhance documentation content from external sources Component:IDocEnricher implementations
Enrichers augment the documentation model with additional content from sources beyond the assembly and conceptual files.
What Enrichers Do
What Enrichers Do
Enrichers can add any type of content to 
DocEntity objects:- Generate documentation from AI services (e.g., Azure OpenAI)
 - Pull examples from external code repositories
 - Add version history from git logs
 - Include performance benchmarks from test results
 - Cross-reference related documentation from other systems
 - Add compliance or security annotations
 - Generate diagrams or visualizations
 
Enricher Interface
Enricher Interface
Enrichers implement the The pipeline calls each enricher with the root 
IDocEnricher interface:DocAssembly entity, allowing enrichers to traverse the entire model:Execution Order
Execution Order
Enrichers execute sequentially in registration order:This allows later enrichers to build on the work of earlier ones.
DocAssembly model enriched with additional content from all registered enrichers.
Stage 5: Transformation
Purpose: Modify the documentation model before rendering Component:IDocTransformer implementations
Transformers modify the documentation model structure or content, applying customizations and processing XML tags.
What Transformers Do
What Transformers Do
Transformers can modify any aspect of the model:
- Process XML documentation tags (e.g., 
<see cref="...">to links) - Implement inheritdoc behavior for inherited members
 - Apply documentation overrides or customizations
 - Reformat content for specific renderer requirements
 - Filter or reorganize the model structure
 - Apply naming conventions or style rules
 - Inject boilerplate content
 
Transformer Interface
Transformer Interface
Transformers implement the Like enrichers, transformers receive the root entity and can traverse the entire model:
IDocTransformer interface:Built-in Transformers
Built-in Transformers
DotNetDocs includes the 
MarkdownXmlTransformer for processing XML documentation tags:- Converts 
<see cref="T:Type"/>to Markdown links - Processes 
<code>and<c>tags to code formatting - Handles 
<para>,<list>, and other structural tags - Preserves semantic meaning while adapting to Markdown
 
UseMarkdownRenderer().Execution Order
Execution Order
Transformers execute sequentially in registration order:Transformer order matters when transformations build on each other.
DocAssembly model ready for rendering.
Stage 6: Rendering
Purpose: Generate output files in specific formats Component:IDocRenderer implementations
Renderers take the final documentation model and generate output files in various formats like Markdown, JSON, YAML, or custom formats.
What Renderers Do
What Renderers Do
Renderers traverse the documentation model and generate output files:
- Create file/folder structure based on 
FileNamingOptions - Format content appropriately for the target format
 - Generate navigation files (e.g., 
docs.jsonfor Mintlify) - Include frontmatter, metadata, and formatting
 - Create index files and cross-references
 - Generate supplementary files (sidebars, TOCs, etc.)
 
Renderer Interface
Renderer Interface
Renderers implement the 
IDocRenderer interface:RenderAsync(): Generate final documentation outputRenderPlaceholdersAsync(): Generate placeholder conceptual files
Built-in Renderers
Built-in Renderers
DotNetDocs Core includes three built-in renderers:MarkdownRenderer: Generates clean Markdown files
- Uses 
FileNamingOptionsfor file organization - Supports both File and Folder namespace modes
 - Includes frontmatter and metadata
 - Cross-references between types
 
- Configurable via 
JsonRendererOptions - Clean serialization with camelCase naming
 - Null value handling
 - Full model export for custom processing
 
- Human-readable structured format
 - Suitable for static site generators
 - Preserves model hierarchy
 
Multiple Renderers
Multiple Renderers
You can register multiple renderers to generate multiple output formats simultaneously:Each renderer runs independently and generates its own output files.
Configuration
Using ProjectContext
TheProjectContext class configures the entire pipeline. You can configure it either in code or declaratively using a .docsproj file with the DotNetDocs SDK:
- In Code
 - In .docsproj File
 
Dependency Injection Setup
DotNetDocs integrates seamlessly with .NET dependency injection:- Quick Start
 - Core Only
 - Fluent Builder
 - Custom Components
 
ProjectContextas SingletonDocumentationManageras Scoped- All built-in renderers (Markdown, JSON, YAML)
 MarkdownXmlTransformer
File Naming Options
Control how documentation files are organized:- Folder Mode
 - File Mode
 
Pipeline Execution
Basic Usage
Process a single assembly:Multi-Assembly Processing
Process multiple assemblies together:Manual Control
Separate conceptual placeholder generation from full processing:Direct Instantiation
Use the pipeline without dependency injection:Advanced Scenarios
Custom Enricher Example
Create an enricher that adds AI-generated best practices:Custom Transformer Example
Create a transformer that adds version badges:Custom Renderer Example
Create a renderer that generates HTML:Performance Considerations
Assembly Caching
AssemblyManager caches compiled assemblies and only rebuilds when files change or configuration changes. Reuse AssemblyManager instances for incremental builds.Parallel Processing
Conceptual content loading happens in parallel per assembly. Multiple assemblies process simultaneously without blocking.
Scoped Services
DocumentationManager, enrichers, transformers, and renderers are registered as Scoped to enable parallel processing in web scenarios.Memory Management
Dispose of 
DocumentationManager when done to release assembly manager caches and free memory. Use using statements or DI scope disposal.Troubleshooting
XML Documentation Not Found
XML Documentation Not Found
Problem: Assembly processes but has no XML documentationSolution:
- Ensure XML file path is correct
 - Check that 
GenerateDocumentationFileis enabled in your.csproj - Verify XML file is copied to output directory
 - Check the 
Errorscollection onAssemblyManagerfor warnings 
Internal Types Not Included
Internal Types Not Included
Problem: Internal types don’t appear in documentationSolution:Make sure your assembly has 
[assembly: InternalsVisibleTo("DotNetDocs")].Conceptual Content Not Loading
Conceptual Content Not Loading
Problem: Conceptual 
.mdz files aren’t appearing in outputSolution:- Verify 
ConceptualDocsEnabled = true - Check that 
ConceptualPathpoints to the correct directory - Ensure 
.mdzfiles are in the correct namespace/type/member hierarchy - If using 
ShowPlaceholders = false, remove the TODO comment from files 
Multiple Renderers Conflict
Multiple Renderers Conflict
Problem: Renderers overwriting each other’s outputSolution: Configure different output paths for each renderer:
Out of Memory with Large Assemblies
Out of Memory with Large Assemblies
Problem: Pipeline runs out of memory processing large assembliesSolution:
- Process assemblies one at a time instead of all together
 - Disable conceptual docs if not needed: 
ConceptualDocsEnabled = false - Dispose of 
DocumentationManagerafter each assembly - Reduce 
IncludedMembersto only public members