Skip to content

Field, Property, and Method Injection

SourceGen.Ioc generates factory method registration when [IocInject] attribute is used on fields, properties, methods, or constructor parameters.

NOTE

FieldInject is not enabled in the default SourceGenIocFeatures value. The default is Register,Container,PropertyInject,MethodInject. To use [IocInject] on fields, add FieldInject in your project configuration (see MSBuild Configuration):

xml
<PropertyGroup>
  <SourceGenIocFeatures>Register,Container,PropertyInject,FieldInject,MethodInject</SourceGenIocFeatures>
</PropertyGroup>

Property & Field Injection

Use [IocInject] to inject dependencies into properties or fields:

csharp
[IocRegister<IMyService>]
internal class MyService : IMyService
{
    [IocInject]
    public ILogger Logger { get; init; } = null!;

    [IocInject]
    internal IConfiguration configuration = null!;
}
Generated Code
csharp
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
    var s0_p0 = sp.GetRequiredService<global::MyNamespace.ILogger>();
    var s0_p1 = sp.GetRequiredService<global::MyNamespace.IConfiguration>();
    var s0 = new global::MyNamespace.MyService() { Logger = s0_p0, configuration = s0_p1 };
    return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());

Method Injection

Use [IocInject] on a method to call it after object creation:

csharp
[IocRegister<IMyService>]
internal class MyService : IMyService
{
    private ILogger logger = null!;
    private IConfiguration config = null!;

    // Must be void return type
    [IocInject]
    public void Initialize(ILogger logger, IConfiguration config)
    {
        this.logger = logger;
        this.config = config;
    }
}
Generated Code
csharp
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
    var s0_m0 = sp.GetRequiredService<global::MyNamespace.ILogger>();
    var s0_m1 = sp.GetRequiredService<global::MyNamespace.IConfiguration>();
    var s0 = new global::MyNamespace.MyService();
    s0.Initialize(s0_m0, s0_m1);
    return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());

Constructor Selection

Use [IocInject] on a constructor to specify which constructor to use for DI:

csharp
[IocRegister<IMyService>]
internal class MyService(IDependency1 dep1, IDependency2 dep2) : IMyService
{
    private readonly IDependency1 dep1 = dep1;
    private readonly IDependency2 dep2 = dep2;

    // Use this constructor instead of the primary constructor
    [IocInject]
    internal MyService(IDependency1 dep1)
        : this(dep1, new DefaultDependency2())
    {
    }
}
Generated Code
csharp
// <auto-generated/>
services.AddSingleton<global::MyNamespace.MyService>((global::System.IServiceProvider sp) =>
{
    var p0 = sp.GetRequiredService<global::MyNamespace.IDependency1>();
    var s0 = new global::MyNamespace.MyService(p0);
    return s0;
});
services.AddSingleton<global::MyNamespace.IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<global::MyNamespace.MyService>());

NOTE

If [IocInject] does not exist on any constructor, there are 2 situations:

  • If no need to generate factory method (no field/property/method injection or decorator), will let IServiceProvider select constructor.
  • If factory method generation is needed (due to field/property/method injection or decorator), will use primary constructor, then the constructor with the most parameters.

Async Method Injection

Use [IocInject] on a method that returns Task to perform async initialization after construction. The method is awaited after all synchronous injection steps.

WARNING

AsyncMethodInject is not enabled by default. Add it to SourceGenIocFeatures in your project file:

xml
<PropertyGroup>
  <SourceGenIocFeatures>Register,Container,PropertyInject,MethodInject,AsyncMethodInject</SourceGenIocFeatures>
</PropertyGroup>

AsyncMethodInject requires MethodInject to be enabled. If AsyncMethodInject is enabled without MethodInject, the analyzer reports SGIOC026.

Classification Rules

Return TypeClassification
voidSynchronous method injection (InjectionMemberType.Method)
Task (non-generic)Async method injection (InjectionMemberType.AsyncMethod)
Task<T>Not supported — not a valid injection method return type
ValueTask / ValueTask<T>Not supported — not a valid injection method return type

Injection Stage Order

The generator emits member injection in a fixed stage order. Source declaration order applies within each stage:

StageMembers
1Properties
2Fields
3Synchronous methods (void)
4Async methods (Task) — awaited last

Example

csharp
using System.Threading.Tasks;

public interface IMyService;
public interface IDependency1;
public interface IDependency2;
public interface IDependency3;

[IocRegister<IMyService>(ServiceLifetime.Singleton)]
internal class MyService : IMyService
{
    [IocInject]
    public IDependency1 Dep1 { get; set; } = default!;

    [IocInject]
    public void SyncInit(IDependency2 dep2)
    {
    }

    [IocInject]
    public async Task AsyncInit(IDependency3 dep3)
    {
        await Task.CompletedTask;
    }
}
Generated Code
csharp
// <auto-generated/>
services.AddSingleton<global::System.Threading.Tasks.Task<global::MyNamespace.MyService>>((global::System.IServiceProvider sp) =>
{
    async global::System.Threading.Tasks.Task<global::MyNamespace.MyService> Init()
    {
        var s0_p0 = sp.GetRequiredService<global::MyNamespace.IDependency1>();  // Stage 1: properties
        var s0_m1 = sp.GetRequiredService<global::MyNamespace.IDependency2>();
        var s0_m2 = sp.GetRequiredService<global::MyNamespace.IDependency3>();
        var s0 = new global::MyNamespace.MyService() { Dep1 = s0_p0 };
        s0.SyncInit(s0_m1);                                                     // Stage 3: sync methods
        await s0.AsyncInit(s0_m2);                                              // Stage 4: async methods
        return s0;
    }
    return Init();
});
// Forwarding registration: Task<IMyService> → Task<MyService>
services.AddSingleton<global::System.Threading.Tasks.Task<global::MyNamespace.IMyService>>(async (global::System.IServiceProvider sp) => await sp.GetRequiredService<global::System.Threading.Tasks.Task<global::MyNamespace.MyService>>());

NOTE

When a service has async inject methods, the registration type changes from T to Task<T>. Consumers that depend on this service should inject Task<T> and await the result. See Wrapper Types — Task<T> for consumer-side usage.

Diagnostics

IDSeverityDescription
SGIOC007ErrorInvalid [IocInject] usage. The attribute cannot be applied to static members, non-accessible members (private, protected, private protected — but protected internal is accepted), properties without a setter or with an inaccessible setter, readonly fields, generic methods, non-ordinary methods (e.g., constructors, operators), or methods with unsupported return types (only void and non-generic Task when AsyncMethodInject is enabled are accepted).
SGIOC022Warning[IocInject] is ignored when the corresponding feature (PropertyInject, FieldInject, MethodInject, or AsyncMethodInject) is disabled in SourceGenIocFeatures.
SGIOC026ErrorAsyncMethodInject feature requires MethodInject to be enabled.
SGIOC023ErrorAn element in the InjectMembers array is not in a recognized format. Each element must be nameof(member) or new object[] { nameof(member), key [, KeyType] }.
SGIOC024ErrorA member specified via InjectMembers is not injectable (e.g., static, non-accessible members (private, protected, private protected — but protected internal is accepted), no setter or inaccessible setter, readonly field, generic method, non-ordinary method, or method with unsupported return type).

InjectMembers: Attribute-Level Injection Without [IocInject]

When you cannot add [IocInject] directly to a type's members (e.g., a third-party type), use the InjectMembers property on [IocRegisterFor] to specify injection points from the registration site:

csharp
// Register ThirdPartyService without modifying it
[IocRegisterFor(typeof(ThirdPartyService),
    InjectMembers = [nameof(ThirdPartyService.Logger)])]
public static class ThirdPartyModule { }

Each element is one of:

FormatDescription
nameof(T.Member)Inject without a key (resolves T from the container)
new object[] { nameof(T.Member), "key" }Inject a keyed service
new object[] { nameof(T.Member), nameof(SomeKey), KeyType.Csharp }Inject using a C# expression key

NOTE

When the same member is specified in both InjectMembers and via [IocInject] on the member itself, [IocInject] takes priority.


← Back to Overview

Released under the MIT License.