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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="omy.Utils.DependencyInjection.Generators" Version="1.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="omy.Utils.DependencyInjection.Generators" Version="1.2.1" />
                    
Directory.Packages.props
<PackageReference Include="omy.Utils.DependencyInjection.Generators" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add omy.Utils.DependencyInjection.Generators --version 1.2.1
                    
#r "nuget: omy.Utils.DependencyInjection.Generators, 1.2.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package omy.Utils.DependencyInjection.Generators@1.2.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=omy.Utils.DependencyInjection.Generators&version=1.2.1
                    
Install as a Cake Addin
#tool nuget:?package=omy.Utils.DependencyInjection.Generators&version=1.2.1
                    
Install as a Cake Tool

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 IServiceConfigurator implementations 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

  1. Add a project reference to Utils.DependencyInjection.Generators (as an analyzer) and to Utils.DependencyInjection (as a library).
  2. Decorate your services with lifetime attributes and mark exposed interfaces with [Injectable].
  3. Create an IServiceConfigurator implementation 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .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.

Version Downloads Last Updated
1.2.1 216 11/3/2025
1.2.0 161 9/26/2025
1.1.1.1 191 8/20/2025