Skip to main content

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 the DocumentationManager, 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.
  • 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
All of this information is extracted from the compiled assembly’s metadata, not from source code.
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
XML tags preserve their inner XML structure, allowing transformers to process them later.
The AssemblyManager creates a Roslyn Compilation object to access symbol information:
This gives DotNetDocs full access to type hierarchies, member relationships, and semantic information.
AssemblyManager implements smart caching to avoid redundant processing:
  • Tracks assembly file LastModified timestamp
  • Compares IncludedMembers accessibility levels
  • Only rebuilds the model when assembly changes or configuration changes
  • Reuses existing DocAssembly model when possible
Output: A DocAssembly object containing DocNamespaceDocTypeDocMember 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.
The DocumentationManager scans the configured ConceptualPath directory, looking for .mdz files organized by namespace/type/member:
The pipeline walks this hierarchy, loading content for each API element.
Seven conceptual sections can be loaded at different levels:Each file contains Markdown content that renderers can format appropriately.
The pipeline can generate placeholder files for missing conceptual content and control their visibility:Placeholder Generation: When ConceptualDocsEnabled = true, renderers create placeholder .mdz files marked with:
Placeholder Loading: The ShowPlaceholders property controls whether placeholders appear in final documentation:
  • ShowPlaceholders = true (default): Load all conceptual content including placeholders
  • ShowPlaceholders = false: Skip files containing the TODO marker
This allows you to see documentation gaps during development but hide them in production builds.
Conceptual content loading happens in parallel per assembly for performance:
Each assembly has its own conceptual directory subtree, so parallel loading is safe.
Output: The 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.
Output: A single merged 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.
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
Each enricher runs independently and can modify any property on any entity.
Enrichers implement the IDocEnricher interface:
The pipeline calls each enricher with the root DocAssembly entity, allowing enrichers to traverse the entire model:
Enrichers execute sequentially in registration order:
This allows later enrichers to build on the work of earlier ones.
Output: The 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.
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
Transformers prepare the model for the specific needs of each renderer.
Transformers implement the IDocTransformer interface:
Like enrichers, transformers receive the root entity and can traverse the entire model:
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
This transformer is automatically registered when using UseMarkdownRenderer().
Transformers execute sequentially in registration order:
Transformer order matters when transformations build on each other.
Output: The final transformed 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.
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.json for Mintlify)
  • Include frontmatter, metadata, and formatting
  • Create index files and cross-references
  • Generate supplementary files (sidebars, TOCs, etc.)
Each renderer has complete control over the output format and structure.
Renderers implement the IDocRenderer interface:
  • RenderAsync(): Generate final documentation output
  • RenderPlaceholdersAsync(): Generate placeholder conceptual files
Example renderer:
DotNetDocs Core includes three built-in renderers:MarkdownRenderer: Generates clean Markdown files
  • Uses FileNamingOptions for file organization
  • Supports both File and Folder namespace modes
  • Includes frontmatter and metadata
  • Cross-references between types
JsonRenderer: Generates JSON representation
  • Configurable via JsonRendererOptions
  • Clean serialization with camelCase naming
  • Null value handling
  • Full model export for custom processing
YamlRenderer: Generates YAML representation
  • Human-readable structured format
  • Suitable for static site generators
  • Preserves model hierarchy
You can register multiple renderers to generate multiple output formats simultaneously:
Each renderer runs independently and generates its own output files.
Output: Documentation files written to disk in one or more formats.

Configuration

Using ProjectContext

The ProjectContext class configures the entire pipeline. You can configure it either in code or declaratively using a .docsproj file with the DotNetDocs SDK:

Dependency Injection Setup

DotNetDocs integrates seamlessly with .NET dependency injection:
Registers:
  • ProjectContext as Singleton
  • DocumentationManager as Scoped
  • All built-in renderers (Markdown, JSON, YAML)
  • MarkdownXmlTransformer

File Naming Options

Control how documentation files are organized:
Result: Namespace hierarchy as folders
Best for: Sites with many types, hierarchical navigation

Pipeline Execution

Basic Usage

Process a single assembly:

Multi-Assembly Processing

Process multiple assemblies together:
Assemblies are merged into a unified model after conceptual loading.

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

Problem: Assembly processes but has no XML documentationSolution:
  • Ensure XML file path is correct
  • Check that GenerateDocumentationFile is enabled in your .csproj
  • Verify XML file is copied to output directory
  • Check the Errors collection on AssemblyManager for warnings
Problem: Internal types don’t appear in documentationSolution:
Make sure your assembly has [assembly: InternalsVisibleTo("DotNetDocs")].
Problem: Conceptual .mdz files aren’t appearing in outputSolution:
  • Verify ConceptualDocsEnabled = true
  • Check that ConceptualPath points to the correct directory
  • Ensure .mdz files are in the correct namespace/type/member hierarchy
  • If using ShowPlaceholders = false, remove the TODO comment from files
Problem: Renderers overwriting each other’s outputSolution: Configure different output paths for each renderer:
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 DocumentationManager after each assembly
  • Reduce IncludedMembers to only public members

See Also

Conceptual Documentation

Learn about the conceptual documentation system

Mintlify Provider

Use the Mintlify renderer for beautiful docs

ProjectContext Reference

Complete ProjectContext API reference

DocumentationManager Reference

Complete DocumentationManager API reference