Skip to content

Class XmlDocumentationService

Namespace: Momentum.Extensions.XmlDocs
Assembly: Momentum.Extensions.XmlDocs.dll

Provides services for loading and retrieving XML documentation comments from compiled assemblies.

csharp
public class XmlDocumentationService : IXmlDocumentationService

Inheritance

objectXmlDocumentationService

Implements

IXmlDocumentationService

Inherited Members

object.GetType(), object.MemberwiseClone(), object.ToString(), object.Equals(object?), object.Equals(object?, object?), object.ReferenceEquals(object?, object?), object.GetHashCode()

Remarks

XML Documentation Service

Service Overview

This service parses XML documentation files generated during compilation and provides methods to retrieve documentation for types, methods, properties, and other members. Documentation is cached for performance after initial loading.

Core Functionality

Documentation Loading

csharp
public async Task<bool> LoadDocumentationAsync(string xmlFilePath)
{
    if (!File.Exists(xmlFilePath))
    {
        logger.LogWarning("XML documentation file not found: {FilePath}", xmlFilePath);
        return false;
    }

    try
    {
        await using var fileStream = new FileStream(xmlFilePath,
            FileMode.Open,
            FileAccess.Read,
            FileShare.Read,
            bufferSize: 8192,
            useAsync: true);

        using var xmlReader = XmlReader.Create(fileStream, new XmlReaderSettings
        {
            Async = true,
            IgnoreComments = true,
            IgnoreWhitespace = false,
            ConformanceLevel = ConformanceLevel.Document
        });

        await ParseXmlDocumentationAsync(xmlReader);

        logger.LogInformation("Loaded XML documentation from {FilePath} with {Count} entries",
            xmlFilePath, _documentationCache.Count);

        return true;
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to load XML documentation from {FilePath}", xmlFilePath);
        return false;
    }
}

Performance Optimizations

Asynchronous File I/O

csharp
await using var fileStream = new FileStream(xmlFilePath,
    FileMode.Open,
    FileAccess.Read,
    FileShare.Read,
    bufferSize: 8192,
    useAsync: true);

Efficient XML Reading

csharp
using var xmlReader = XmlReader.Create(fileStream, new XmlReaderSettings
{
    Async = true,
    IgnoreComments = true,
    IgnoreWhitespace = false,
    ConformanceLevel = ConformanceLevel.Document
});

Documentation Parsing

Member Discovery

csharp
private async Task ParseXmlDocumentationAsync(XmlReader reader)
{
    while (await reader.ReadAsync())
    {
        if (reader is not { NodeType: XmlNodeType.Element, Name: "member" })
            continue;

        var nameAttribute = reader.GetAttribute("name");

        if (!string.IsNullOrEmpty(nameAttribute))
        {
            var docInfo = await ParseMemberDocumentationAsync(reader);

            if (docInfo is not null)
            {
                _documentationCache.TryAdd(nameAttribute, docInfo);
            }
        }
    }
}

Element Processing

csharp
private static async Task<XmlDocumentationInfo?> ParseMemberDocumentationAsync(XmlReader reader)
{
    var docInfo = new XmlDocumentationInfo();
    var hasContent = false;

    if (reader.IsEmptyElement)
        return null;

    while (await reader.ReadAsync())
    {
        if (reader is { NodeType: XmlNodeType.EndElement, Name: "member" })
            break;

        if (reader.NodeType == XmlNodeType.Element)
        {
            switch (reader.Name.ToLowerInvariant())
            {
                case "summary":
                    docInfo.Summary = await ReadElementContentAsync(reader);
                    hasContent = true;
                    break;
                case "remarks":
                    docInfo.Remarks = await ReadElementContentAsync(reader);
                    hasContent = true;
                    break;
                // ... other elements
            }
        }
    }

    return hasContent ? docInfo : null;
}

Caching Strategy

Thread-Safe Caching

csharp
private readonly ConcurrentDictionary<string, XmlDocumentationInfo> _documentationCache = new();

Cache Operations

csharp
public XmlDocumentationInfo? GetDocumentation(string memberName) =>
    _documentationCache.GetValueOrDefault(memberName);

public void ClearCache() => _documentationCache.Clear();

Member Name Resolution

Type Documentation

csharp
public XmlDocumentationInfo? GetTypeDocumentation(Type type)
{
    return GetDocumentation($"T:{type.FullName}");
}

Method Documentation

csharp
public XmlDocumentationInfo? GetMethodDocumentation(MethodInfo methodInfo)
{
    var memberName = GetMethodDocumentationName(methodInfo);
    return GetDocumentation(memberName);
}

Property Documentation

csharp
public XmlDocumentationInfo? GetPropertyDocumentation(PropertyInfo propertyInfo)
{
    return GetDocumentation($"P:{propertyInfo.DeclaringType?.FullName}.{propertyInfo.Name}");
}

Method Name Generation

Complex Method Signatures

csharp
private static string GetMethodDocumentationName(MethodInfo methodInfo)
{
    var sb = new StringBuilder();

    sb.Append("M:");
    sb.Append(methodInfo.DeclaringType?.FullName);
    sb.Append('.');
    sb.Append(methodInfo.Name);

    if (methodInfo.GetParameters().Length > 0)
    {
        sb.Append('(');

        var parameters = methodInfo.GetParameters();

        for (var i = 0; i < parameters.Length; i++)
        {
            if (i > 0) sb.Append(',');
            sb.Append(GetTypeName(parameters[i].ParameterType));
        }

        sb.Append(')');
    }

    return sb.ToString();
}

Generic Type Support

csharp
private static string GetTypeName(Type type)
{
    if (!type.IsGenericType)
        return type.FullName ?? type.Name;

    var genericTypeName = type.GetGenericTypeDefinition().FullName;
    var genericArgs = type.GetGenericArguments();

    var sb = new StringBuilder();

    sb.Append(genericTypeName?[..genericTypeName.IndexOf('`')]);
    sb.Append('{');

    for (var i = 0; i < genericArgs.Length; i++)
    {
        if (i > 0) sb.Append(',');
        sb.Append(GetTypeName(genericArgs[i]));
    }

    sb.Append('}');

    return sb.ToString();
}

Content Extraction

Element Content Reading

csharp
private static async Task<string?> ReadElementContentAsync(XmlReader reader)
{
    if (reader.IsEmptyElement)
        return null;

    var content = new StringBuilder();

    while (await reader.ReadAsync())
    {
        if (reader.NodeType == XmlNodeType.EndElement)
            break;

        if (reader.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
        {
            content.Append(reader.Value);
        }
    }

    var result = content.ToString();

    // Preserve internal newlines but trim leading/trailing whitespace
    return result.Trim();
}

Supported XML Elements

Core Documentation Elements

  • summary: Main description of the member
  • remarks: Additional detailed information
  • returns: Description of return value
  • example: Usage examples

Parameter Documentation

csharp
case "param":
    var paramName = reader.GetAttribute("name");

    if (!string.IsNullOrEmpty(paramName))
    {
        var paramDoc = await ReadElementContentAsync(reader);
        var paramExample = reader.GetAttribute("example");

        docInfo.Parameters[paramName] = new XmlDocumentationInfo.ParameterInfo(paramDoc, paramExample);
        hasContent = true;
    }
    break;

Response Documentation

csharp
case "response":
    var responseCode = reader.GetAttribute("code");
    var responseDoc = await ReadElementContentAsync(reader);

    if (!string.IsNullOrEmpty(responseCode))
    {
        docInfo.Responses[responseCode] = responseDoc;
        hasContent = true;
    }
    break;

Error Handling

File Access Errors

csharp
if (!File.Exists(xmlFilePath))
{
    logger.LogWarning("XML documentation file not found: {FilePath}", xmlFilePath);
    return false;
}

XML Parsing Errors

csharp
catch (Exception ex)
{
    logger.LogError(ex, "Failed to load XML documentation from {FilePath}", xmlFilePath);
    return false;
}

Graceful Degradation

The service continues operation even when:

  • XML Files Missing: Returns null for documentation queries
  • Malformed XML: Skips problematic elements
  • Empty Elements: Ignores elements without content

Integration with OpenAPI

Service Registration

csharp
services.AddSingleton<IXmlDocumentationService, XmlDocumentationService>();

Usage in Transformers

csharp
var methodDoc = xmlDocumentationService.GetMethodDocumentation(methodInfo);
if (methodDoc?.Summary is not null)
{
    operation.Summary = methodDoc.Summary;
}

Performance Characteristics

Memory Usage

  • Efficient Caching: Documentation cached after first access
  • Memory Bounded: Cache size limited by assembly size
  • String Interning: Repeated strings optimized

I/O Performance

  • Async Operations: Non-blocking file access
  • Buffered Reading: 8KB buffer for optimal throughput
  • Single Load: Documentation loaded once per application lifecycle

Thread Safety

  • Concurrent Access: Thread-safe cache operations
  • Read-Heavy Optimization: Optimized for frequent reads
  • Lazy Loading: Documentation loaded on demand

Constructors

XmlDocumentationService(ILogger<XmlDocumentationService>)

Provides services for loading and retrieving XML documentation comments from compiled assemblies.

csharp
public XmlDocumentationService(ILogger<XmlDocumentationService> logger)

Parameters

logger ILogger<XmlDocumentationService>

Remarks

XML Documentation Service

Service Overview

This service parses XML documentation files generated during compilation and provides methods to retrieve documentation for types, methods, properties, and other members. Documentation is cached for performance after initial loading.

Core Functionality

Documentation Loading

csharp
public async Task<bool> LoadDocumentationAsync(string xmlFilePath)
{
    if (!File.Exists(xmlFilePath))
    {
        logger.LogWarning("XML documentation file not found: {FilePath}", xmlFilePath);
        return false;
    }

    try
    {
        await using var fileStream = new FileStream(xmlFilePath,
            FileMode.Open,
            FileAccess.Read,
            FileShare.Read,
            bufferSize: 8192,
            useAsync: true);

        using var xmlReader = XmlReader.Create(fileStream, new XmlReaderSettings
        {
            Async = true,
            IgnoreComments = true,
            IgnoreWhitespace = false,
            ConformanceLevel = ConformanceLevel.Document
        });

        await ParseXmlDocumentationAsync(xmlReader);

        logger.LogInformation("Loaded XML documentation from {FilePath} with {Count} entries",
            xmlFilePath, _documentationCache.Count);

        return true;
    }
    catch (Exception ex)
    {
        logger.LogError(ex, "Failed to load XML documentation from {FilePath}", xmlFilePath);
        return false;
    }
}

Performance Optimizations

Asynchronous File I/O

csharp
await using var fileStream = new FileStream(xmlFilePath,
    FileMode.Open,
    FileAccess.Read,
    FileShare.Read,
    bufferSize: 8192,
    useAsync: true);

Efficient XML Reading

csharp
using var xmlReader = XmlReader.Create(fileStream, new XmlReaderSettings
{
    Async = true,
    IgnoreComments = true,
    IgnoreWhitespace = false,
    ConformanceLevel = ConformanceLevel.Document
});

Documentation Parsing

Member Discovery

csharp
private async Task ParseXmlDocumentationAsync(XmlReader reader)
{
    while (await reader.ReadAsync())
    {
        if (reader is not { NodeType: XmlNodeType.Element, Name: "member" })
            continue;

        var nameAttribute = reader.GetAttribute("name");

        if (!string.IsNullOrEmpty(nameAttribute))
        {
            var docInfo = await ParseMemberDocumentationAsync(reader);

            if (docInfo is not null)
            {
                _documentationCache.TryAdd(nameAttribute, docInfo);
            }
        }
    }
}

Element Processing

csharp
private static async Task<XmlDocumentationInfo?> ParseMemberDocumentationAsync(XmlReader reader)
{
    var docInfo = new XmlDocumentationInfo();
    var hasContent = false;

    if (reader.IsEmptyElement)
        return null;

    while (await reader.ReadAsync())
    {
        if (reader is { NodeType: XmlNodeType.EndElement, Name: "member" })
            break;

        if (reader.NodeType == XmlNodeType.Element)
        {
            switch (reader.Name.ToLowerInvariant())
            {
                case "summary":
                    docInfo.Summary = await ReadElementContentAsync(reader);
                    hasContent = true;
                    break;
                case "remarks":
                    docInfo.Remarks = await ReadElementContentAsync(reader);
                    hasContent = true;
                    break;
                // ... other elements
            }
        }
    }

    return hasContent ? docInfo : null;
}

Caching Strategy

Thread-Safe Caching

csharp
private readonly ConcurrentDictionary<string, XmlDocumentationInfo> _documentationCache = new();

Cache Operations

csharp
public XmlDocumentationInfo? GetDocumentation(string memberName) =>
    _documentationCache.GetValueOrDefault(memberName);

public void ClearCache() => _documentationCache.Clear();

Member Name Resolution

Type Documentation

csharp
public XmlDocumentationInfo? GetTypeDocumentation(Type type)
{
    return GetDocumentation($"T:{type.FullName}");
}

Method Documentation

csharp
public XmlDocumentationInfo? GetMethodDocumentation(MethodInfo methodInfo)
{
    var memberName = GetMethodDocumentationName(methodInfo);
    return GetDocumentation(memberName);
}

Property Documentation

csharp
public XmlDocumentationInfo? GetPropertyDocumentation(PropertyInfo propertyInfo)
{
    return GetDocumentation($"P:{propertyInfo.DeclaringType?.FullName}.{propertyInfo.Name}");
}

Method Name Generation

Complex Method Signatures

csharp
private static string GetMethodDocumentationName(MethodInfo methodInfo)
{
    var sb = new StringBuilder();

    sb.Append("M:");
    sb.Append(methodInfo.DeclaringType?.FullName);
    sb.Append('.');
    sb.Append(methodInfo.Name);

    if (methodInfo.GetParameters().Length > 0)
    {
        sb.Append('(');

        var parameters = methodInfo.GetParameters();

        for (var i = 0; i < parameters.Length; i++)
        {
            if (i > 0) sb.Append(',');
            sb.Append(GetTypeName(parameters[i].ParameterType));
        }

        sb.Append(')');
    }

    return sb.ToString();
}

Generic Type Support

csharp
private static string GetTypeName(Type type)
{
    if (!type.IsGenericType)
        return type.FullName ?? type.Name;

    var genericTypeName = type.GetGenericTypeDefinition().FullName;
    var genericArgs = type.GetGenericArguments();

    var sb = new StringBuilder();

    sb.Append(genericTypeName?[..genericTypeName.IndexOf('`')]);
    sb.Append('{');

    for (var i = 0; i < genericArgs.Length; i++)
    {
        if (i > 0) sb.Append(',');
        sb.Append(GetTypeName(genericArgs[i]));
    }

    sb.Append('}');

    return sb.ToString();
}

Content Extraction

Element Content Reading

csharp
private static async Task<string?> ReadElementContentAsync(XmlReader reader)
{
    if (reader.IsEmptyElement)
        return null;

    var content = new StringBuilder();

    while (await reader.ReadAsync())
    {
        if (reader.NodeType == XmlNodeType.EndElement)
            break;

        if (reader.NodeType is XmlNodeType.Text or XmlNodeType.CDATA)
        {
            content.Append(reader.Value);
        }
    }

    var result = content.ToString();

    // Preserve internal newlines but trim leading/trailing whitespace
    return result.Trim();
}

Supported XML Elements

Core Documentation Elements

  • summary: Main description of the member
  • remarks: Additional detailed information
  • returns: Description of return value
  • example: Usage examples

Parameter Documentation

csharp
case "param":
    var paramName = reader.GetAttribute("name");

    if (!string.IsNullOrEmpty(paramName))
    {
        var paramDoc = await ReadElementContentAsync(reader);
        var paramExample = reader.GetAttribute("example");

        docInfo.Parameters[paramName] = new XmlDocumentationInfo.ParameterInfo(paramDoc, paramExample);
        hasContent = true;
    }
    break;

Response Documentation

csharp
case "response":
    var responseCode = reader.GetAttribute("code");
    var responseDoc = await ReadElementContentAsync(reader);

    if (!string.IsNullOrEmpty(responseCode))
    {
        docInfo.Responses[responseCode] = responseDoc;
        hasContent = true;
    }
    break;

Error Handling

File Access Errors

csharp
if (!File.Exists(xmlFilePath))
{
    logger.LogWarning("XML documentation file not found: {FilePath}", xmlFilePath);
    return false;
}

XML Parsing Errors

csharp
catch (Exception ex)
{
    logger.LogError(ex, "Failed to load XML documentation from {FilePath}", xmlFilePath);
    return false;
}

Graceful Degradation

The service continues operation even when:

  • XML Files Missing: Returns null for documentation queries
  • Malformed XML: Skips problematic elements
  • Empty Elements: Ignores elements without content

Integration with OpenAPI

Service Registration

csharp
services.AddSingleton<IXmlDocumentationService, XmlDocumentationService>();

Usage in Transformers

csharp
var methodDoc = xmlDocumentationService.GetMethodDocumentation(methodInfo);
if (methodDoc?.Summary is not null)
{
    operation.Summary = methodDoc.Summary;
}

Performance Characteristics

Memory Usage

  • Efficient Caching: Documentation cached after first access
  • Memory Bounded: Cache size limited by assembly size
  • String Interning: Repeated strings optimized

I/O Performance

  • Async Operations: Non-blocking file access
  • Buffered Reading: 8KB buffer for optimal throughput
  • Single Load: Documentation loaded once per application lifecycle

Thread Safety

  • Concurrent Access: Thread-safe cache operations
  • Read-Heavy Optimization: Optimized for frequent reads
  • Lazy Loading: Documentation loaded on demand

Methods

ClearCache()

Clears all cached documentation.

csharp
public void ClearCache()

GetDocumentation(string)

Gets the documentation for a specific member by its XML documentation name.

csharp
public XmlDocumentationInfo? GetDocumentation(string memberName)

Parameters

memberName string

The XML documentation member name (e.g., "T:Namespace.Type").

Returns

XmlDocumentationInfo?

The documentation information if found; otherwise, null.

GetMethodDocumentation(MethodInfo)

Gets the documentation for a method.

csharp
public XmlDocumentationInfo? GetMethodDocumentation(MethodInfo methodInfo)

Parameters

methodInfo MethodInfo

The method to get documentation for.

Returns

XmlDocumentationInfo?

The documentation information if found; otherwise, null.

GetPropertyDocumentation(PropertyInfo)

Gets the documentation for a property.

csharp
public XmlDocumentationInfo? GetPropertyDocumentation(PropertyInfo propertyInfo)

Parameters

propertyInfo PropertyInfo

The property to get documentation for.

Returns

XmlDocumentationInfo?

The documentation information if found; otherwise, null.

GetTypeDocumentation(Type)

Gets the documentation for a type.

csharp
public XmlDocumentationInfo? GetTypeDocumentation(Type type)

Parameters

type Type

The type to get documentation for.

Returns

XmlDocumentationInfo?

The documentation information if found; otherwise, null.

LoadDocumentationAsync(string)

Asynchronously loads XML documentation from the specified file.

csharp
public Task<bool> LoadDocumentationAsync(string xmlFilePath)

Parameters

xmlFilePath string

The path to the XML documentation file.

Returns

Task<bool>

A task that represents the asynchronous operation. The task result contains true if the documentation was loaded successfully; otherwise, false.