MintPlayer.SourceGenerators
10.21.0
dotnet add package MintPlayer.SourceGenerators --version 10.21.0
NuGet\Install-Package MintPlayer.SourceGenerators -Version 10.21.0
<PackageReference Include="MintPlayer.SourceGenerators" Version="10.21.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="MintPlayer.SourceGenerators" Version="10.21.0" />
<PackageReference Include="MintPlayer.SourceGenerators"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add MintPlayer.SourceGenerators --version 10.21.0
#r "nuget: MintPlayer.SourceGenerators, 10.21.0"
#:package MintPlayer.SourceGenerators@10.21.0
#addin nuget:?package=MintPlayer.SourceGenerators&version=10.21.0
#tool nuget:?package=MintPlayer.SourceGenerators&version=10.21.0
Dependency-Injection generators
This library contains source-generators to simplify dependency-injection in your application
Getting started
You need to install both MintPlayer.SourceGenerators and MintPlayer.SourceGenerators.Attributes packages in your project.
Service-registration
Place this class in your abstractions library:
public interface ICustomerService { }
Place this class in your implementation library:
[Register(typeof(ICustomerService), ServiceLifetime.Scoped)]
internal class CustomerService : ICustomerService { }
Now you get an extension method generated for you, which will register all services for you:
var services = new ServiceCollection()
.AddMyCompanyServices() // Method name derived from assembly name
.BuildServiceProvider();
Method Name Resolution
The generated method name follows this precedence:
- Explicit hint on
[Register]attribute (e.g.,"CoreServices") →AddCoreServices() - Assembly-level configuration via
[assembly: ServiceRegistrationConfiguration] - Assembly name (default) - sanitized and prefixed with "Add" (e.g.,
MyCompany.Services→AddMyCompanyServices())
Assembly-Level Configuration
You can configure the default method name and accessibility at the assembly level:
using MintPlayer.SourceGenerators.Attributes;
[assembly: ServiceRegistrationConfiguration(
DefaultMethodName = "MyServices",
DefaultAccessibility = EGeneratedAccessibility.Internal
)]
This will generate AddMyServices() as an internal method for all services without an explicit method hint.
Explicit Method Hints
You can still specify a method hint on individual registrations to group services:
[Register(typeof(ICustomerService), ServiceLifetime.Scoped, "DemoServices")]
internal class CustomerService : ICustomerService { }
[Register(typeof(IProductService), ServiceLifetime.Scoped, "DemoServices")]
internal class ProductService : IProductService { }
This generates:
var services = new ServiceCollection()
.AddDemoServices() // Contains CustomerService and ProductService
.BuildServiceProvider();
Registering Third-Party Types
You can register types from NuGet packages or external libraries at the assembly level:
using MintPlayer.SourceGenerators.Attributes;
// Self-registration (implementation = service type)
[assembly: Register(typeof(ThirdPartyClass), ServiceLifetime.Singleton)]
// Interface + implementation registration
[assembly: Register(typeof(IExternalService), typeof(ExternalServiceImpl), ServiceLifetime.Scoped)]
This generates:
public static IServiceCollection AddMyAssembly(this IServiceCollection services)
{
return services
.AddSingleton<ThirdPartyClass>()
.AddScoped<IExternalService, ExternalServiceImpl>();
}
Registration Patterns Summary
| Pattern | Target | Example |
|---|---|---|
| Self-registration | Class | [Register(ServiceLifetime.Scoped)] |
| Interface registration | Class | [Register(typeof(IService), ServiceLifetime.Scoped)] |
| Third-party self-registration | Assembly | [assembly: Register(typeof(Impl), ServiceLifetime.Scoped)] |
| Third-party interface registration | Assembly | [assembly: Register(typeof(IService), typeof(Impl), ServiceLifetime.Scoped)] |
Registration Diagnostics
| Rule ID | Severity | Description |
|---|---|---|
| REGISTER001 | Error | Assembly-level [Register] requires at least the implementation type |
| REGISTER002 | Error | Class-level [Register] should not specify implementation type |
Dependency Injection
Inject a registered service anywhere:
public partial class CustomerController {
[Inject] private readonly ICustomerService customerService;
}
The source-generator will generate the constructor for you. It supports DI when using inheritance too.
Post-Construction Initialization
If you need to run initialization logic after all dependencies are injected, use the [PostConstruct] attribute:
public partial class CustomerController {
[Inject] private readonly ICustomerService customerService;
[Inject] private readonly ILogger<CustomerController> logger;
[PostConstruct]
private void OnInitialized()
{
logger.LogInformation("CustomerController initialized with {Service}", customerService.GetType().Name);
}
}
The [PostConstruct] method will be called automatically at the end of the generated constructor, after all field assignments are complete.
Rules:
- The method must be parameterless
- Only one
[PostConstruct]method is allowed per class - The method cannot be static
- Each class in an inheritance hierarchy can have its own
[PostConstruct]method (base class method runs first) - Nested classes each have their own scope for
[PostConstruct]
Diagnostics:
| Rule ID | Severity | Description |
|---------|----------|-------------|
| INJECT001 | Error | PostConstruct method must be parameterless |
| INJECT002 | Error | Only one PostConstruct method allowed per class |
| INJECT003 | Error | PostConstruct method cannot be static |
| INJECT004 | Warning | PostConstruct method in class without [Inject] members |
Configuration Binding
Simplify reading configuration values with [Config], [ConnectionString], and [Options] attributes:
public partial class DatabaseService
{
// Required config values (throws if missing)
[Config("Database:Type")]
private readonly DatabaseType databaseType;
// Optional with default value
[Config("Database:MaxRetries", DefaultValue = 3)]
private readonly int maxRetries;
// TimeSpan parsing
[Config("Database:Timeout")]
private readonly TimeSpan timeout;
// Connection string
[ConnectionString("DefaultConnection")]
private readonly string connectionString;
// Nullable = optional (won't throw if missing)
[ConnectionString("OptionalDb")]
private readonly string? optionalConnection;
}
IOptions Pattern
public partial class EmailService
{
[Options("Email")]
private readonly IOptions<EmailSettings> emailOptions;
[Options("Customer")]
private readonly IOptionsSnapshot<CustomerConfig> customerOptions;
[Options("Features")]
private readonly IOptionsMonitor<FeatureFlags> featureFlags;
}
IConfiguration Deduplication
When you inject IConfiguration explicitly, the generator reuses it:
public partial class ConfigAwareService
{
[Inject] private readonly IConfiguration configuration;
[Config("App:Name")]
private readonly string appName; // Uses 'configuration' field
public string GetCustomValue(string key) => configuration[key];
}
Supported Types
| Category | Types |
|---|---|
| Primitives | string, bool, char, byte, short, int, long, float, double, decimal |
| Nullable | int?, bool?, string?, etc. |
| Enums | Any enum type |
| Date/Time | DateTime, DateTimeOffset, TimeSpan, DateOnly, TimeOnly |
| Other | Guid, Uri |
| Complex | POCO classes, arrays, List<T> |
Configuration Diagnostics
| Rule ID | Severity | Description |
|---|---|---|
| CONFIG001 | Error | Class must be partial |
| CONFIG002 | Error | Empty configuration key |
| CONFIG003 | Error | Unsupported field type |
| CONFIG006 | Error | Conflicting [Config] and [ConnectionString] |
| CONFIG008 | Error | Conflicting [Config] and [Inject] |
| CONNSTR001 | Error | Empty connection string name |
| CONNSTR002 | Error | [ConnectionString] requires string type |
| OPTIONS001 | Error | Invalid options type |
| OPTIONS003 | Error | Conflicting [Options] and [Inject] |
Interface Implementation
There's also an analyzer that will check if all public class members are known on the implemented interface. The analyzer provides a code-fix to add the missing members.
public interface ICustomerService { }
[Register(typeof(ICustomerService), ServiceLifetime.Scoped)]
internal class CustomerService : ICustomerService {
public Task<Customer> GetCustomer(int id) => throw new NotImplementedException();
}
This also works when the interface resides in an abstractions-library and the class resides in an implementation-library. Which is why this analyzer is so powerful.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- MintPlayer.SourceGenerators.Attributes (>= 10.20.1)
- MintPlayer.ValueComparerGenerator.Attributes (>= 10.20.1)
- MintPlayer.ValueComparers.NewtonsoftJson (>= 10.20.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.21.0 | 60 | 8/27/2026 |
| 10.20.1 | 57 | 8/27/2026 |
| 10.20.0 | 412 | 6/5/2026 |
| 10.19.0 | 778 | 4/3/2026 |
| 10.18.0 | 163 | 3/26/2026 |
| 10.17.0 | 650 | 2/14/2026 |
| 10.16.1 | 283 | 1/25/2026 |
| 10.16.0 | 128 | 1/25/2026 |
| 10.15.0 | 135 | 1/24/2026 |
| 10.14.1 | 125 | 1/20/2026 |
| 10.14.0 | 132 | 1/19/2026 |
| 10.13.0 | 220 | 1/19/2026 |
| 10.12.0 | 132 | 1/19/2026 |
| 10.11.1 | 139 | 1/18/2026 |
| 10.11.0 | 128 | 1/18/2026 |
| 10.10.1 | 145 | 1/12/2026 |
| 10.10.0 | 138 | 1/12/2026 |
| 10.9.0 | 138 | 1/12/2026 |
| 10.8.0 | 213 | 12/24/2025 |
| 10.7.0 | 312 | 11/13/2025 |