Atc.SourceGenerators
0.1.5
See the version list below for details.
dotnet add package Atc.SourceGenerators --version 0.1.5
NuGet\Install-Package Atc.SourceGenerators -Version 0.1.5
<PackageReference Include="Atc.SourceGenerators" Version="0.1.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Atc.SourceGenerators" Version="0.1.5" />
<PackageReference Include="Atc.SourceGenerators"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Atc.SourceGenerators --version 0.1.5
#r "nuget: Atc.SourceGenerators, 0.1.5"
#:package Atc.SourceGenerators@0.1.5
#addin nuget:?package=Atc.SourceGenerators&version=0.1.5
#tool nuget:?package=Atc.SourceGenerators&version=0.1.5
π― Atc Source Generators
A collection of Roslyn C# source generators for .NET that eliminate boilerplate code and improve developer productivity. All generators are designed with Native AOT compatibility in focus, enabling faster startup times, smaller deployment sizes, and optimal performance for modern cloud-native applications.
π Source Generators
- β‘ DependencyRegistrationGenerator - Automatic DI service registration with attributes
- βοΈ OptionsBindingGenerator - Automatic configuration binding to strongly-typed options classes
- πΊοΈ MappingGenerator - Automatic object-to-object mapping with type safety
- π EnumMappingGenerator - Automatic enum-to-enum mapping with intelligent matching
π¦ Installation
All generators are distributed in a single NuGet package. Install once to use all features.
Required:
dotnet add package Atc.SourceGenerators
Optional (recommended for better IntelliSense):
dotnet add package Atc.SourceGenerators.Annotations
Or in your .csproj:
<ItemGroup>
<PackageReference Include="Atc.SourceGenerators" Version="1.0.0" />
<PackageReference Include="Atc.SourceGenerators.Annotations" Version="1.0.0" />
</ItemGroup>
Note: The generator emits fallback attributes automatically, so the Annotations package is optional. However, it provides better XML documentation and IntelliSense. If you include it, suppress the expected CS0436 warning: <NoWarn>$(NoWarn);CS0436</NoWarn>
β‘ DependencyRegistrationGenerator
Stop writing repetitive service registration code. Decorate your services with [Registration] and let the generator handle the rest.
π Documentation
- Complete Guide - In-depth documentation with examples
- Quick Start - PetStore 3-layer architecture tutorial
- Multi-Project Setup - Working with multiple projects
- Auto-Detection - Understanding automatic interface detection
- Sample Projects - Working code examples with architecture diagrams
π« From This
// Program.cs - Manual registration hell π«
services.AddScoped<IUserService, UserService>();
services.AddScoped<IOrderService, OrderService>();
services.AddScoped<IPetRepository, PetRepository>();
services.AddSingleton<ICacheService, CacheService>();
services.AddTransient<ILogger, Logger>();
services.AddScoped<IPetService, PetService>();
services.AddScoped<IEmailService, EmailService>();
// ... 50+ more lines of registration code
// ... spread across multiple files
// ... easy to forget or get wrong
β¨ To This
// Your services - Clean, declarative, self-documenting β¨
[Registration(Lifetime.Scoped)]
public class UserService : IUserService { }
[Registration(Lifetime.Scoped)]
public class OrderService : IOrderService { }
[Registration]
public class CacheService : ICacheService { }
// Program.cs - One line per project (with smart naming!)
using Atc.DependencyInjection;
builder.Services.AddDependencyRegistrationsFromApi();
builder.Services.AddDependencyRegistrationsFromDomain();
builder.Services.AddDependencyRegistrationsFromDataAccess();
β¨ Key Features
- π― Auto-Detection: Automatically registers against all implemented interfaces - no more
As = typeof(IService) - π§Ή Smart Filtering: System interfaces (IDisposable, etc.) are excluded automatically
- π Multi-Interface: Implementing multiple interfaces? Registers against all of them
- π Hosted Service Support: Automatically detects BackgroundService and IHostedService implementations and uses AddHostedService<T>()
- β¨ Smart Naming: Generates clean method names using suffixes when unique, full names when conflicts exist
- β‘ Zero Runtime Cost: All code generated at compile time
- π Native AOT Compatible: No reflection or runtime code generation - fully trimming-safe
- ποΈ Multi-Project: Works seamlessly across layered architectures
- π‘οΈ Type-Safe: Compile-time validation catches errors before runtime
- π¦ Flexible Lifetimes: Singleton (default), Scoped, and Transient support
π Quick Example
using Atc.DependencyInjection;
// That's it! Auto-detected as IUserService
[Registration(Lifetime.Scoped)]
public class UserService : IUserService
{
public void CreateUser(string name) { }
}
// Multiple interfaces? No problem - registers against ALL of them
[Registration]
public class EmailService : IEmailService, INotificationService { }
// Need both interface AND concrete type?
[Registration(AsSelf = true)]
public class ReportService : IReportService { }
π§ Service Lifetimes
[Registration] // Singleton (default)
[Registration(Lifetime.Singleton)] // Explicit singleton
[Registration(Lifetime.Scoped)] // Per-request (web apps)
[Registration(Lifetime.Transient)] // New instance every time
π‘οΈ Compile-Time Safety
Get errors at compile time, not runtime:
| ID | Description |
|---|---|
| ATCDIR001 | As parameter must be an interface type |
| ATCDIR002 | Class must implement the specified interface |
| ATCDIR003 | Duplicate registration with different lifetimes |
| ATCDIR004 | Hosted services must use Singleton lifetime |
βοΈ OptionsBindingGenerator
Eliminate boilerplate configuration binding code. Decorate your options classes with [OptionsBinding] and let the generator create type-safe configuration bindings automatically.
π Documentation
- Options Binding Guide - Full documentation with examples
- Sample Projects - Working examples with architecture diagrams
π« From This
// Manual options binding - repetitive and error-prone π«
services.AddOptions<DatabaseOptions>()
.Bind(configuration.GetSection("Database"))
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddOptions<ApiOptions>()
.Bind(configuration.GetSection("App:Api"))
.ValidateDataAnnotations()
.ValidateOnStart();
services.AddOptions<LoggingOptions>()
.Bind(configuration.GetSection("Logging"))
.ValidateOnStart();
// ... repeated for every options class
β¨ To This
// Your options classes - Clean and declarative β¨
[OptionsBinding("Database", ValidateDataAnnotations = true, ValidateOnStart = true)]
public partial class DatabaseOptions
{
[Required]
public string ConnectionString { get; set; }
}
[OptionsBinding("App:Api", ValidateDataAnnotations = true)]
public partial class ApiOptions
{
public string BaseUrl { get; set; }
}
[OptionsBinding] // Section name auto-inferred as "LoggingOptions"
public partial class LoggingOptions
{
public string Level { get; set; }
}
// Program.cs - One line binds all options (with smart naming!)
services.AddOptionsFromApp(configuration);
β¨ Key Features
- π― Auto-Inference: Section names automatically inferred from class names
- π Const Name Support: Use
public const string SectionName,NameTitle, orNamefor custom section names - π Built-in Validation: Data annotations and startup validation with simple properties
- π Nested Sections: Support for complex configuration paths like "App:Services:Email"
- β‘ Zero Runtime Cost: All binding code generated at compile time
- π Native AOT Compatible: No reflection or runtime code generation - fully trimming-safe
- π‘οΈ Type-Safe: Compile-time validation ensures configuration matches your classes
- β¨ Smart Naming: Clean method names (
AddOptionsFromDomain) for unique suffixes, full names for conflicts - π¦ Multi-Project Support: Each project generates its own extension method with smart naming
- β±οΈ Options Lifetimes: Control which options interface to use (IOptions, IOptionsSnapshot, IOptionsMonitor)
π Quick Example
using Atc.SourceGenerators.Annotations;
using System.ComponentModel.DataAnnotations;
// Automatic section name inference
[OptionsBinding] // Binds to "DatabaseOptions" section (uses full class name)
public partial class DatabaseOptions
{
public string ConnectionString { get; set; }
}
// Using const SectionName (2nd priority)
[OptionsBinding(ValidateDataAnnotations = true)]
public partial class CacheOptions
{
public const string SectionName = "ApplicationCache"; // Binds to "ApplicationCache"
[Range(1, 1000)]
public int MaxSize { get; set; }
}
// Using const Name (4th priority)
[OptionsBinding]
public partial class EmailOptions
{
public const string Name = "EmailConfiguration"; // Binds to "EmailConfiguration"
public string SmtpServer { get; set; }
}
// Full priority demonstration
[OptionsBinding]
public partial class LoggingOptions
{
public const string SectionName = "X1"; // 2nd prio - WINS
public const string NameTitle = "X2"; // 3rd prio
public const string Name = "X3"; // 4th prio
// Binds to "X1"
}
// Explicit section path (1st priority - highest)
[OptionsBinding("App:Email:Smtp")]
public partial class SmtpOptions
{
public string Host { get; set; }
public int Port { get; set; }
}
// Specify lifetime for different injection patterns
[OptionsBinding("Features", Lifetime = OptionsLifetime.Monitor)]
public partial class FeatureOptions
{
public bool EnableNewFeature { get; set; }
}
// Usage in your services:
public class MyService
{
public MyService(IOptions<DatabaseOptions> db) // Singleton
public MyService(IOptionsSnapshot<SmtpOptions> smtp) // Scoped (reloads per request)
public MyService(IOptionsMonitor<FeatureOptions> features) // Monitor (change notifications)
}
π‘οΈ Compile-Time Safety
| ID | Description |
|---|---|
| ATCOPT001 | Options class must be declared as partial |
| ATCOPT002 | Section name cannot be null or empty |
| ATCOPT003 | Invalid options binding configuration |
πΊοΈ MappingGenerator
Eliminate tedious object-to-object mapping code. Decorate your classes with [MapTo(typeof(TargetType))] and let the generator create type-safe mapping extension methods automatically.
π Documentation
- Object Mapping Guide - Full documentation with examples
- Quick Start - UserApp 3-layer architecture tutorial
- Advanced Scenarios - Enums, nested objects, multi-layer mapping
- Sample Projects - Working code examples with DataAccess β Domain β API
π« From This
// Manual mapping - tedious, repetitive, error-prone π«
public UserDto MapToDto(User user)
{
return new UserDto
{
Id = user.Id,
FirstName = user.FirstName,
LastName = user.LastName,
Email = user.Email,
Status = (UserStatusDto)user.Status,
Address = user.Address != null ? new AddressDto
{
Street = user.Address.Street,
City = user.Address.City,
State = user.Address.State,
PostalCode = user.Address.PostalCode,
Country = user.Address.Country
} : null,
CreatedAt = user.CreatedAt,
UpdatedAt = user.UpdatedAt
};
}
// ... repeat for every type
// ... across every layer
// ... easy to forget properties
β¨ To This
// Your domain models - Clean, declarative, self-documenting β¨
using Atc.SourceGenerators.Annotations;
[MapTo(typeof(UserDto))]
public partial class User
{
public Guid Id { get; init; }
public string FirstName { get; init; } = string.Empty;
public string LastName { get; init; } = string.Empty;
public string Email { get; init; } = string.Empty;
public UserStatus Status { get; init; }
public Address? Address { get; init; }
public DateTimeOffset CreatedAt { get; init; }
public DateTimeOffset? UpdatedAt { get; init; }
}
[MapTo(typeof(AddressDto))]
public partial class Address
{
public string Street { get; init; } = string.Empty;
public string City { get; init; } = string.Empty;
public string State { get; init; } = string.Empty;
public string PostalCode { get; init; } = string.Empty;
public string Country { get; init; } = string.Empty;
}
// Usage - One line per mapping
using Atc.Mapping;
var dto = user.MapToUserDto();
var dtos = users.Select(u => u.MapToUserDto()).ToList();
β¨ Key Features
- π Smart Enum Conversion:
- Uses safe EnumMapping extension methods when enums have
[MapTo]attributes - Falls back to casts for enums without attributes
- Supports special case handling (None β Unknown, etc.) via EnumMappingGenerator
- Uses safe EnumMapping extension methods when enums have
- πͺ Nested Object Mapping: Automatically chains mappings for nested properties
- π Multi-Layer Support: Build Entity β Domain β DTO mapping chains effortlessly
- β‘ Zero Runtime Cost: All code generated at compile time
- π Native AOT Compatible: No reflection or runtime code generation - fully trimming-safe
- π‘οΈ Type-Safe: Compile-time validation catches mapping errors before runtime
- π¦ Null Safety: Built-in null checking for nullable reference types
- π― Convention-Based: Maps properties by name - no configuration needed
π Quick Example
using Atc.SourceGenerators.Annotations;
using Atc.Mapping;
// Source with nested object and enum
[MapTo(typeof(PersonDto))]
public partial class Person
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public Status Status { get; set; }
public Address? Address { get; set; }
}
[MapTo(typeof(AddressDto))]
public partial class Address
{
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
}
public enum Status { Active = 0, Inactive = 1 }
// Target types
public class PersonDto
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public StatusDto Status { get; set; }
public AddressDto? Address { get; set; }
}
public class AddressDto
{
public string Street { get; set; } = string.Empty;
public string City { get; set; } = string.Empty;
}
public enum StatusDto { Active = 0, Inactive = 1 }
// β¨ Use generated extension methods
var person = new Person
{
Id = 1,
Name = "John Doe",
Status = Status.Active,
Address = new Address { Street = "123 Main St", City = "NYC" }
};
var dto = person.MapToPersonDto();
// β¨ Automatic enum conversion
// β¨ Automatic nested object mapping (Address β AddressDto)
// β¨ Null safety built-in
π Multi-Layer Architecture
Perfect for 3-layer architectures:
Database (Entities) β Domain (Models) β API (DTOs)
// Data Access Layer
[MapTo(typeof(Domain.Product))]
public partial class ProductEntity
{
public int DatabaseId { get; set; }
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public bool IsDeleted { get; set; } // DB-specific field
}
// Domain Layer
namespace Domain;
[MapTo(typeof(ProductDto))]
public partial class Product
{
public Guid Id { get; init; }
public string Name { get; init; } = string.Empty;
public decimal Price { get; init; }
}
public class ProductDto
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
}
// β¨ Complete mapping chain
var entity = repository.GetById(1);
var domain = entity.MapToProduct();
var dto = domain.MapToProductDto();
π‘οΈ Compile-Time Safety
Get errors at compile time, not runtime:
| ID | Description |
|---|---|
| ATCMAP001 | Mapping class must be declared as partial |
| ATCMAP002 | Target type must be a class or struct |
π EnumMappingGenerator
Eliminate manual enum conversions with intelligent enum-to-enum mapping. Decorate your enums with [MapTo(typeof(TargetEnum))] and let the generator create type-safe switch expression mappings with special case handling automatically.
π Documentation
- Enum Mapping Guide - Full documentation with examples
- Quick Start - PetStore enum mapping tutorial
- Special Case Mappings - None β Unknown, Active β Enabled, etc.
- Sample Projects - Working code examples with bidirectional mapping
π« From This
// Manual enum mapping - tedious, error-prone, inflexible π«
public PetStatusDto MapToDto(PetStatusEntity status)
{
return status switch
{
PetStatusEntity.None => PetStatusDto.Unknown,
PetStatusEntity.Pending => PetStatusDto.Pending,
PetStatusEntity.Available => PetStatusDto.Available,
PetStatusEntity.Adopted => PetStatusDto.Adopted,
_ => throw new ArgumentOutOfRangeException(nameof(status)),
};
}
public PetStatusEntity MapToEntity(PetStatusDto status)
{
return status switch
{
PetStatusDto.Unknown => PetStatusEntity.None,
PetStatusDto.Pending => PetStatusEntity.Pending,
PetStatusDto.Available => PetStatusEntity.Available,
PetStatusDto.Adopted => PetStatusEntity.Adopted,
_ => throw new ArgumentOutOfRangeException(nameof(status)),
};
}
// ... repeat for every enum pair
// ... across every layer
// ... easy to make mistakes
β¨ To This
// Your enums - Clean, declarative, self-documenting β¨
using Atc.SourceGenerators.Annotations;
// Database layer
[MapTo(typeof(PetStatusDto), Bidirectional = true)]
public enum PetStatusEntity
{
None, // β¨ Auto-maps to PetStatusDto.Unknown (special case)
Pending,
Available,
Adopted,
}
// API layer
public enum PetStatusDto
{
Unknown, // β¨ Auto-maps from PetStatusEntity.None
Available,
Pending,
Adopted,
}
// Usage - Generated extension methods
using Atc.Mapping;
var entity = PetStatusEntity.None;
var dto = entity.MapToPetStatusDto(); // PetStatusDto.Unknown
var back = dto.MapToPetStatusEntity(); // PetStatusEntity.None (bidirectional!)
β¨ Key Features
- π― Intelligent Name Matching: Maps enum values by name with case-insensitive support
- π Special Case Detection: Automatically handles "zero/empty/null" state equivalents:
NoneβUnknown,DefaultUnknownβNone,DefaultDefaultβNone,Unknown- Limited to just these three values to avoid unexpected mappings
- π Bidirectional Mapping: Generate both forward and reverse mappings with one attribute
- β‘ Zero Runtime Cost: Pure switch expressions, no reflection
- π‘οΈ Type-Safe: Compile-time validation with warnings for unmapped values
- π Native AOT Compatible: No reflection or runtime code generation - fully trimming-safe
- β οΈ Runtime Safety:
ArgumentOutOfRangeExceptionfor unmapped values
π Quick Example
using Atc.SourceGenerators.Annotations;
using Atc.Mapping;
// Database layer enum with special case mapping
[MapTo(typeof(StatusDto), Bidirectional = true)]
public enum StatusEntity
{
None, // β¨ Maps to StatusDto.Unknown (special case)
Active, // β¨ Exact name match
Inactive, // β¨ Exact name match
}
public enum StatusDto
{
Unknown, // β¨ Maps from StatusEntity.None (special case)
Active, // β¨ Exact name match
Inactive, // β¨ Exact name match
}
// β¨ Use generated extension methods
var entity = StatusEntity.None;
var dto = entity.MapToStatusDto(); // StatusDto.Unknown
var back = dto.MapToStatusEntity(); // StatusEntity.None (bidirectional!)
π‘οΈ Compile-Time Safety
Get errors and warnings at compile time, not runtime:
| ID | Description |
|---|---|
| ATCENUM001 | Target type must be an enum |
| ATCENUM002 | Enum value has no matching target value (Warning) |
π¨ Building
dotnet build
π§ͺ Testing
dotnet test
π Sample Projects
Working code examples demonstrating each generator in realistic scenarios:
β‘ DependencyRegistration Sample
Multi-project console app showing automatic DI registration across layers with auto-detection of interfaces.
cd sample/Atc.SourceGenerators.DependencyRegistration
dotnet run
βοΈ OptionsBinding Sample
Console app demonstrating type-safe configuration binding with validation and multiple options classes.
cd sample/Atc.SourceGenerators.OptionsBinding
dotnet run
πΊοΈ Mapping Sample
ASP.NET Core Minimal API showing 3-layer mapping (Entity β Domain β DTO) with automatic enum conversion and nested objects.
cd sample/Atc.SourceGenerators.Mapping
dotnet run
π EnumMapping Sample
Console app demonstrating intelligent enum-to-enum mapping with special case handling (None β Unknown, Active β Enabled), bidirectional mappings, and case-insensitive matching.
cd sample/Atc.SourceGenerators.EnumMapping
dotnet run
π― PetStore API - Complete Example
Full-featured ASP.NET Core application using all four generators together with OpenAPI/Scalar documentation. This demonstrates production-ready patterns for modern .NET applications.
cd sample/PetStore.Api
dotnet run
# Open https://localhost:42616/scalar/v1 for API documentation
π€ Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
π License
[License information here]
| 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
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.