Knara.SourceGenerators.DesignPatterns.Builder
1.0.0
See the version list below for details.
dotnet add package Knara.SourceGenerators.DesignPatterns.Builder --version 1.0.0
NuGet\Install-Package Knara.SourceGenerators.DesignPatterns.Builder -Version 1.0.0
<PackageReference Include="Knara.SourceGenerators.DesignPatterns.Builder" Version="1.0.0" />
<PackageVersion Include="Knara.SourceGenerators.DesignPatterns.Builder" Version="1.0.0" />
<PackageReference Include="Knara.SourceGenerators.DesignPatterns.Builder" />
paket add Knara.SourceGenerators.DesignPatterns.Builder --version 1.0.0
#r "nuget: Knara.SourceGenerators.DesignPatterns.Builder, 1.0.0"
#:package Knara.SourceGenerators.DesignPatterns.Builder@1.0.0
#addin nuget:?package=Knara.SourceGenerators.DesignPatterns.Builder&version=1.0.0
#tool nuget:?package=Knara.SourceGenerators.DesignPatterns.Builder&version=1.0.0
Builder Pattern Generator
A C# source generator that creates fluent builder classes for complex object construction in .NET Framework applications. Automatically generates type-safe builders with validation, required field checking, and collection handling.
Why This Generator Exists
Legacy Framework Reality: .NET Framework 4.x lacks modern language features like init properties, nullable reference types, and advanced object initializers. Creating immutable, validated objects requires verbose constructors or error-prone manual builder implementations.
Team Skill Constraints: Implementing correct builder patterns manually requires understanding of fluent interfaces, validation chains, and immutability patterns that inexperienced developers often get wrong.
Solution: Generate proven, consistent builder implementations that provide modern object construction patterns for legacy frameworks.
Quick Start
Add the source generator to your project:
<ItemGroup> <ProjectReference Include="path/to/Knara.SourceGenerators.DesignPatterns.Builder.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
Or via NuGet (when published):
dotnet add package Knara.SourceGenerators.DesignPatterns.Builder
If you are using the generator in .net 4.+ projects, refer to this guide for additional steps.
using Knara.SourceGenerators.DesignPatterns.Builder;
[GenerateBuilder]
public class User
{
[BuilderProperty(Required = true)]
public string FirstName { get; }
[BuilderProperty]
public string LastName { get; }
[BuilderProperty(ValidatorMethod = nameof(ValidateAge))]
public int Age { get; }
public User(string firstName, string lastName, int age)
{
FirstName = firstName;
LastName = lastName;
Age = age;
}
public static bool ValidateAge(int age) => age >= 0 && age <= 150;
}
// Usage
var user = UserBuilder.Create()
.WithFirstName("John")
.WithLastName("Doe")
.WithAge(30)
.Build();
Builder Configuration
Class-Level Attributes
[GenerateBuilder(
BuilderName = "CustomBuilder", // Override default name
ValidateOnBuild = true, // Check required fields
GenerateWithMethods = true, // Generate WithXxx methods
GenerateFromMethod = true, // Generate From() method
Accessibility = BuilderAccessibility.Internal // Control visibility
)]
Property-Level Attributes
[BuilderProperty(
Required = true, // Must be set before Build()
ValidatorMethod = "ValidateEmail", // Custom validation method
DefaultValue = "\"Unknown\"", // Default value
CustomSetterName = "WithEmailAddress", // Override method name
AllowNull = false, // Null checking
IgnoreInBuilder = true // Exclude from builder
)]
Collection Attributes
[BuilderCollection(
AddMethodName = "AddTag", // Single item method name
AddRangeMethodName = "AddTags", // Range method name
GenerateClearMethod = true, // Generate Clear method
GenerateCountProperty = true // Generate Count property
)]
Use Cases by Complexity
1. Simple Configuration Objects
When: Objects with optional parameters and defaults
[GenerateBuilder]
public class DatabaseConfig
{
[BuilderProperty(Required = true)]
public string ConnectionString { get; }
[BuilderProperty]
public TimeSpan Timeout { get; } = TimeSpan.FromSeconds(30);
[BuilderProperty]
public int MaxRetries { get; } = 3;
}
2. Domain Entities with Validation
When: Business objects needing validation before construction
[GenerateBuilder(ValidateOnBuild = true)]
public class Customer
{
[BuilderProperty(Required = true, ValidatorMethod = nameof(ValidateEmail))]
public string Email { get; }
[BuilderProperty(Required = true)]
public string Name { get; }
[BuilderProperty(ValidatorMethod = nameof(ValidateAge))]
public int Age { get; }
public static bool ValidateEmail(string email) => email.Contains("@");
public static bool ValidateAge(int age) => age >= 18;
}
3. Complex Objects with Collections
When: Objects with multiple collections and complex structure
[GenerateBuilder]
public class ProjectConfiguration
{
[BuilderProperty(Required = true)]
public string Name { get; }
[BuilderCollection(AddMethodName = "AddDependency")]
public IReadOnlyList<string> Dependencies { get; }
[BuilderCollection]
public List<string> Tags { get; }
[BuilderProperty]
public Dictionary<string, string> Metadata { get; }
}
4. API Configuration Objects
When: Service configurations with many optional parameters
[GenerateBuilder]
public class ApiClientConfig
{
[BuilderProperty(Required = true)]
public string BaseUrl { get; }
[BuilderProperty]
public TimeSpan Timeout { get; } = TimeSpan.FromSeconds(30);
[BuilderProperty]
public AuthenticationType AuthType { get; } = AuthenticationType.None;
[BuilderCollection]
public IReadOnlyList<string> DefaultHeaders { get; }
}
Generated Features
Core Builder Methods
Create()- Static factory methodWithXxx()- Fluent setters for each propertyBuild()- Creates the final objectFrom(existing)- Initialize from existing objectToBuilder()- Extension method for existing objects
Collection Support
AddXxx()- Add single itemsAddXxxs()- Add multiple itemsClearXxx()- Clear collectionsXxxCount- Get collection counts
Validation Features
- Required field checking
- Custom validation methods
- Null value validation
- Build-time validation with clear error messages
Legacy Framework Benefits
✅ Solves Legacy Problems
- Immutable objects without
initproperties - Validation without nullable reference types
- Fluent APIs without manual implementation
- Required fields without compiler support
- Collection building with type safety
✅ Team Safety Features
- Generated validation prevents runtime errors
- Consistent patterns across codebase
- Clear error messages for missing required fields
- Type-safe builders eliminate casting errors
Performance Considerations
Memory Usage
- Additional allocations: Builder instances and intermediate collections
- Garbage collection: More objects to collect during build process
Build Performance
- Compile-time generation: No runtime reflection overhead
- Direct method calls: Faster than dynamic construction
- Validation overhead: Custom validators run at build time
Best Practices
✅ Good Uses
- Configuration objects with many optional parameters
- Domain entities requiring validation
- API builders for complex integrations
- Data transfer objects needing immutability
- Test data builders for unit tests
❌ Avoid For
- Simple DTOs with 2-3 properties
- Performance-critical paths with high allocation rates
- Value objects better suited for constructors
- Objects changing frequently (maintenance overhead)
Design Guidelines
- Keep builders focused: One builder per aggregate root
- Use validation sparingly: Only for business rules, not basic null checks
- Prefer init over set: Use
{ get; }properties when possible - Group related properties: Use nested builders for complex hierarchies
Validation Patterns
Custom Validators
public static bool ValidateEmail(string email)
{
return !string.IsNullOrEmpty(email) && email.Contains("@");
}
public static bool ValidateAge(int age)
{
return age >= 0 && age <= 150;
}
Required Field Strategy
// Runtime validation
[BuilderProperty(Required = true)]
public string Name { get; }
// Usage - throws InvalidOperationException if Name not set
var obj = builder.Build();
Null Handling
// Strict null checking
[BuilderProperty(AllowNull = false)]
public string Name { get; }
// Nullable fields
[BuilderProperty(AllowNull = true)]
public string? Description { get; }
Migration Strategy
From Constructor Overloads
Before:
public DatabaseConfig(string connectionString)
: this(connectionString, TimeSpan.FromSeconds(30), 100) { }
public DatabaseConfig(string connectionString, TimeSpan timeout)
: this(connectionString, timeout, 100) { }
public DatabaseConfig(string connectionString, TimeSpan timeout, int poolSize)
{
// Implementation
}
After:
[GenerateBuilder]
public class DatabaseConfig
{
[BuilderProperty(Required = true)]
public string ConnectionString { get; }
// Builder handles all combinations
}
From Mutable Objects
Before:
var config = new DatabaseConfig();
config.ConnectionString = "..."; // Mutable, error-prone
config.Timeout = TimeSpan.FromSeconds(30);
After:
var config = DatabaseConfigBuilder.Create()
.WithConnectionString("...")
.WithTimeout(TimeSpan.FromSeconds(30))
.Build(); // Immutable result
Integration with Legacy Code
Gradual Adoption
- Start with new classes: Apply
[GenerateBuilder]to new domain objects - Migrate complex constructors: Replace parameter-heavy constructors
- Convert test builders: Replace manual test data builders
- Standardize configurations: Use for service configuration objects
Coexistence Patterns
// Support both patterns during transition
public class LegacyClass
{
// Keep existing constructors
public LegacyClass(string name) { Name = name; }
// Add builder support
[BuilderProperty]
public string Name { get; }
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.14.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.