omy.Utils.DependencyInjection.Generators
1.2.1
dotnet add package omy.Utils.DependencyInjection.Generators --version 1.2.1
NuGet\Install-Package omy.Utils.DependencyInjection.Generators -Version 1.2.1
<PackageReference Include="omy.Utils.DependencyInjection.Generators" Version="1.2.1" />
<PackageVersion Include="omy.Utils.DependencyInjection.Generators" Version="1.2.1" />
<PackageReference Include="omy.Utils.DependencyInjection.Generators" />
paket add omy.Utils.DependencyInjection.Generators --version 1.2.1
#r "nuget: omy.Utils.DependencyInjection.Generators, 1.2.1"
#:package omy.Utils.DependencyInjection.Generators@1.2.1
#addin nuget:?package=omy.Utils.DependencyInjection.Generators&version=1.2.1
#tool nuget:?package=omy.Utils.DependencyInjection.Generators&version=1.2.1
Utils.DependencyInjection.Generators
Utils.DependencyInjection.Generators provides a Roslyn source generator that implements IServiceConfigurator classes marked
with the [StaticAuto] attribute. It targets .NET 9 and complements the Utils.DependencyInjection runtime library by wiring
attribute-based registrations at build time.
Features
- Scans the compilation for
IServiceConfiguratorimplementations decorated with[StaticAuto]. - Detects types annotated with
[Singleton],[Scoped], or[Transient]and generates the matching registration calls. - Supports keyed registrations when a domain is supplied to the lifetime attributes.
- Registers interfaces marked with
[Injectable]against their attributed implementations. - Emits partial class implementations that remain editable alongside generated code.
Getting started
- Add a project reference to Utils.DependencyInjection.Generators (as an analyzer) and to Utils.DependencyInjection (as a library).
- Decorate your services with lifetime attributes and mark exposed interfaces with
[Injectable]. - Create an
IServiceConfiguratorimplementation and mark it with[StaticAuto].
Examples
The following scenarios highlight the generator's capabilities. They can be mixed and matched inside the same project because the generated partial classes never overwrite hand-written logic.
Basic registration example
using System;
using Microsoft.Extensions.DependencyInjection;
using Utils.DependencyInjection;
[Injectable]
public interface IMessageFormatter
{
string Format(string message);
}
[Singleton]
public class UppercaseFormatter : IMessageFormatter
{
public string Format(string message) => message.ToUpperInvariant();
}
[StaticAuto]
public partial class FormatterConfigurator : IServiceConfigurator { }
var services = new ServiceCollection();
new FormatterConfigurator().ConfigureServices(services);
var provider = services.BuildServiceProvider();
var formatter = provider.GetRequiredService<IMessageFormatter>();
string result = formatter.Format("hello");
The generated ConfigureServices method automatically registers UppercaseFormatter as the
implementation for IMessageFormatter.
Multi-tenant keyed registrations
You can scope services to tenants or domains by supplying a key when decorating the service with lifetime attributes.
using Microsoft.Extensions.DependencyInjection;
using Utils.DependencyInjection;
[Injectable]
public interface ICalculator
{
int Add(int left, int right);
}
[Singleton("europe")]
public class VatCalculator : ICalculator
{
public int Add(int left, int right) => (int)Math.Round((left + right) * 1.2, MidpointRounding.AwayFromZero);
}
[Singleton("us")]
public class SalesTaxCalculator : ICalculator
{
public int Add(int left, int right) => (int)Math.Round((left + right) * 1.07, MidpointRounding.AwayFromZero);
}
[StaticAuto]
public partial class CalculatorConfigurator : IServiceConfigurator { }
var services = new ServiceCollection();
new CalculatorConfigurator().ConfigureServices(services);
var provider = services.BuildServiceProvider();
var europeanCalculator = provider.GetRequiredKeyedService<ICalculator>("europe");
var usCalculator = provider.GetRequiredKeyedService<ICalculator>("us");
The generator emits AddKeyedSingleton calls so that each calculator is registered with its
domain. Consumers resolve the correct implementation by providing the key.
Partial methods for fine-grained control
Because the generator emits partial classes, you can extend the configurator with custom logic without losing the automatic registrations.
using Microsoft.Extensions.DependencyInjection;
[StaticAuto]
public partial class AdvancedConfigurator : IServiceConfigurator
{
partial void OnAfterConfigureServices(IServiceCollection services)
{
services.AddLogging();
}
}
When the generator is executed it creates a matching partial class and invokes
OnAfterConfigureServices after wiring the detected services, allowing manual tweaks.
Module-based composition
Large solutions commonly split registrations into domain-specific modules. The generator can aggregate all of them without manual wiring.
using Microsoft.Extensions.DependencyInjection;
using Utils.DependencyInjection;
[StaticAuto]
public partial class AccountingModule : IServiceConfigurator { }
[StaticAuto]
public partial class NotificationsModule : IServiceConfigurator { }
[StaticAuto]
public partial class ApplicationConfigurator : IServiceConfigurator
{
partial void OnAfterConfigureServices(IServiceCollection services)
{
services.AddLogging();
}
}
var services = new ServiceCollection();
new ApplicationConfigurator().ConfigureServices(services);
Each generated partial class invokes the others through IServiceConfigurator so the final
ApplicationConfigurator can remain focused on cross-cutting concerns such as logging or
metrics.
Open generics and interfaces
The generator handles open-generic registrations when the implementation type declares a generic constraint.
using Utils.DependencyInjection;
[Injectable]
public interface IRepository<T>
where T : class
{
ValueTask<T?> GetAsync(Guid id, CancellationToken cancellationToken = default);
}
[Scoped]
public class SqlRepository<T> : IRepository<T>
where T : class
{
public ValueTask<T?> GetAsync(Guid id, CancellationToken cancellationToken = default)
{
// database lookup
return ValueTask.FromResult<T?>(default);
}
}
[StaticAuto]
public partial class DataConfigurator : IServiceConfigurator { }
At build time the generator emits services.AddScoped(typeof(IRepository<>), typeof(SqlRepository<>));
while preserving the generic constraints.
Conditional environments
Partial methods are also ideal for environment-based tweaks. The generator still outputs the default registrations while giving developers an override point.
using Microsoft.Extensions.DependencyInjection;
[StaticAuto]
public partial class EnvironmentConfigurator : IServiceConfigurator
{
partial void OnBeforeConfigureServices(IServiceCollection services)
{
if (OperatingSystem.IsWindows())
{
services.AddSingleton<IPathService, WindowsPathService>();
}
}
}
OnBeforeConfigureServices is emitted alongside ConfigureServices. It executes before the
automatic registrations which allows conditional replacements without disabling generation.
Mixing manual and generated registrations
Source generation remains opt-in for each configurator. Manual entries can remain untouched while the generator handles the repetitive parts.
using Microsoft.Extensions.DependencyInjection;
[StaticAuto]
public partial class MessagingConfigurator : IServiceConfigurator
{
partial void OnAfterConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMessageBus>(_ => new RabbitMqBus("amqp://localhost"));
}
}
Because the generator never rewrites the partial file, developers can evolve their manual registrations at their own pace while still benefiting from automated discovery for routine services.
| 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.