Configuration¶
Verified by tests
ConfigurationUtilitiesTests, NamingConventionUtilitiesTests — library CI run #31657041675 (2026-08-13)
Whizbang source generators read configuration from MSBuild properties, enabling compile-time customization without code changes. This approach ensures configuration is available during source generation while maintaining AOT compatibility.
Overview¶
Configuration utilities provide a bridge between MSBuild project properties and the incremental source generator pipeline:
flowchart TD
Props[".csproj / Directory.Build.props<br/><br/><WhizbangStripTableNameSuffixes>true</...><br/><WhizbangTableNameSuffixesToStrip>...</...>"]
Utils["ConfigurationUtilities<br/><br/>Reads AnalyzerConfigOptions<br/>Returns TableNameConfig"]
Generators["Source Generators<br/><br/>Use config for table names, code generation"]
Props --> Utils
Utils --> Generators
class Props,Utils,Generators layer-infrastructure
ConfigurationUtilities¶
The ConfigurationUtilities class provides static methods for reading MSBuild properties from the analyzer configuration:
GetTableNameConfig¶
Reads table name configuration from MSBuild properties:
GetTableNameConfig
using Whizbang.Generators.Shared.Utilities;
public void Initialize(IncrementalGeneratorInitializationContext context) {
// Create a value provider for table name configuration
var tableNameConfig = context.AnalyzerConfigOptionsProvider.Select(
ConfigurationUtilities.SelectTableNameConfig
);
// Use in generator pipeline
var combined = perspectives.Combine(tableNameConfig);
context.RegisterSourceOutput(combined, (ctx, data) => {
var (perspectiveList, config) = data;
GenerateCode(ctx, perspectiveList, config);
});
}
MSBuild Properties¶
| Property | Type | Default | Description |
|---|---|---|---|
WhizbangStripTableNameSuffixes |
bool | true |
Enable/disable suffix stripping |
WhizbangTableNameSuffixesToStrip |
string | ReadModel,Model,Projection,Dto,View |
Comma-separated list of suffixes |
WhizbangMaxIdentifierLength |
int | provider default (63 for PostgreSQL) | Optional override for maximum database identifier length; read via ConfigurationUtilities.GetMaxIdentifierLengthOverride / SelectMaxIdentifierLengthOverride |
Configuration in .csproj¶
Configuration in .csproj
<PropertyGroup>
<!-- Disable suffix stripping entirely -->
<WhizbangStripTableNameSuffixes>false</WhizbangStripTableNameSuffixes>
<!-- Or customize which suffixes to strip -->
<WhizbangStripTableNameSuffixes>true</WhizbangStripTableNameSuffixes>
<WhizbangTableNameSuffixesToStrip>ReadModel,Projection,View</WhizbangTableNameSuffixesToStrip>
</PropertyGroup>
TableNameConfig¶
The TableNameConfig record holds the parsed configuration:
TableNameConfig
public sealed record TableNameConfig(
bool StripSuffixes,
string[] SuffixesToStrip
) {
/// <summary>
/// Default configuration: strip common suffixes (Model, Projection, ReadModel, Dto, View).
/// </summary>
public static TableNameConfig Default { get; } = new(
StripSuffixes: true,
SuffixesToStrip: ["ReadModel", "Model", "Projection", "Dto", "View"]
);
/// <summary>
/// Configuration that preserves all suffixes (no stripping).
/// </summary>
public static TableNameConfig NoStripping { get; } = new(
StripSuffixes: false,
SuffixesToStrip: []
);
}
Table Name Suffix Stripping¶
When StripSuffixes is enabled, perspective model type names are transformed for database table names via NamingConventionUtilities.GenerateTableName: the first matching suffix is stripped, the remainder is converted to snake_case, and the wh_per_ prefix is added (no pluralization):
| Model Type Name | Stripped Name | Table Name |
|---|---|---|
OrderReadModel |
Order |
wh_per_order |
ProductProjection |
Product |
wh_per_product |
CustomerDto |
Customer |
wh_per_customer |
InventoryView |
Inventory |
wh_per_inventory |
UserModel |
User |
wh_per_user |
AccountDetails |
AccountDetails |
wh_per_account_details |
Example: Table Name Suffix Stripping
// Perspective read-model type
public class OrderReadModel {
[StreamId]
public Guid OrderId { get; set; }
public string Status { get; set; } = "";
}
// Generated table name (with default suffix stripping):
// Table: "wh_per_order" (not "wh_per_order_read_model")
Usage in Generators¶
Pipeline Integration¶
Pipeline Integration
[Generator]
public class PerspectiveSchemaGenerator : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorInitializationContext context) {
// 1. Discover perspectives
var perspectives = context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (node, _) => node is ClassDeclarationSyntax,
transform: static (ctx, ct) => ExtractPerspective(ctx, ct)
).Where(static p => p is not null);
// 2. Get configuration
var config = context.AnalyzerConfigOptionsProvider.Select(
ConfigurationUtilities.SelectTableNameConfig
);
// 3. Combine and generate
var combined = perspectives.Collect().Combine(config);
context.RegisterSourceOutput(combined, static (ctx, data) => {
var (perspectiveList, tableConfig) = data;
GenerateSchema(ctx, perspectiveList!, tableConfig);
});
}
private static void GenerateSchema(
SourceProductionContext context,
ImmutableArray<PerspectiveInfo> perspectives,
TableNameConfig config) {
foreach (var perspective in perspectives) {
// Apply table name configuration
// e.g., "OrderProjection" -> "wh_per_order" with default config
var tableName = NamingConventionUtilities.GenerateTableName(
perspective.TableBaseName,
config
);
// Generate schema with configured table name...
}
}
}
Direct Access¶
For simpler scenarios, access configuration directly:
Direct Access
var config = ConfigurationUtilities.GetTableNameConfig(
context.AnalyzerConfigOptionsProvider.GlobalOptions
);
if (config.StripSuffixes) {
// Apply suffix stripping logic
}
Suffix Parsing¶
The ParseSuffixList method handles comma-separated suffix lists:
Suffix Parsing
// Parse from MSBuild property
var suffixes = ConfigurationUtilities.ParseSuffixList("ReadModel, Model, Dto");
// Result: ["ReadModel", "Model", "Dto"]
// Handles whitespace and empty entries
var suffixes = ConfigurationUtilities.ParseSuffixList(" Foo , , Bar , ");
// Result: ["Foo", "Bar"]
// Empty or null returns empty array
var suffixes = ConfigurationUtilities.ParseSuffixList("");
// Result: []
Best Practices¶
DO¶
- Use
Directory.Build.propsfor solution-wide settings - Provide sensible defaults (don't require configuration)
- Document available options in project README
- Use incremental pipeline with
Selectfor optimal caching
DON'T¶
- Require configuration for basic functionality
- Read configuration in predicates (performance impact)
- Ignore null/missing options (use defaults)
Troubleshooting¶
Configuration Not Applied¶
Symptoms: Generator ignores MSBuild property values.
Causes: 1. Property not available in analyzer config 2. Property name typo
Solution: Ensure property is in a <PropertyGroup> (not <ItemGroup>):
Configuration Not Applied
<!-- Correct -->
<PropertyGroup>
<WhizbangStripTableNameSuffixes>false</WhizbangStripTableNameSuffixes>
</PropertyGroup>
<!-- Wrong - ItemGroup -->
<ItemGroup>
<WhizbangStripTableNameSuffixes>false</WhizbangStripTableNameSuffixes>
</ItemGroup>
Suffix Not Stripped¶
Symptoms: Model suffix appears in generated table name (e.g., wh_per_order_view_model).
Causes: 1. Suffix not in list 2. Stripping disabled
Solution: Add suffix to list:
Suffix Not Stripped
<WhizbangTableNameSuffixesToStrip>ReadModel,Model,Projection,Dto,View,ViewModel</WhizbangTableNameSuffixesToStrip>
Related Topics¶
- Perspective Discovery - How perspectives are discovered
- Table Naming - Table naming conventions
- JSON Contexts - JSON serialization configuration
Version 1.0.0 - Foundation Release | Last Updated: 2024-12-12