Skip to content

Basic Usage ​

SourceGen.Ioc provides two main approaches for service registration:

  1. Non-intrusive — [IocRegisterFor] registers types externally, keeping your service code attribute-free.
  2. Direct annotation — [IocRegister] marks the class at its declaration (best for infrastructure types you own).

TIP

Prefer [IocRegisterFor] for domain/business types and [IocRegister] for infrastructure types. This keeps your domain layer free of DI framework concerns.

Registering External Types (Non-Intrusive) ​

Use [IocRegisterFor<T>] (generic) or [IocRegisterFor] (non-generic) to register types without modifying their source code:

csharp
// In a dedicated registration file — service types stay attribute-free
[assembly: IocRegisterFor<UserService>(ServiceLifetime.Scoped, ServiceTypes = [typeof(IUserService)])]
[assembly: IocRegisterFor<OrderService>(ServiceLifetime.Scoped, ServiceTypes = [typeof(IOrderService)])]

// Or on a marker class
[IocRegisterFor<UserService>(ServiceLifetime.Scoped, ServiceTypes = [typeof(IUserService)])]
[IocRegisterFor<OrderService>(ServiceLifetime.Scoped, ServiceTypes = [typeof(IOrderService)])]
public class RegistrationMarker;

NOTE

Generic attributes require C# 11+. If your project targets an older language version, use the non-generic form: [IocRegisterFor(typeof(UserService))].

IocRegisterFor supports the same options as IocRegister (Key, KeyType, Tags, Decorators, Factory, Instance, and lifetime settings).

Generated Code
csharp
services.AddScoped<UserService, UserService>();
services.AddScoped<IUserService>((global::System.IServiceProvider sp) => sp.GetRequiredService<UserService>());
services.AddScoped<OrderService, OrderService>();
services.AddScoped<IOrderService>((global::System.IServiceProvider sp) => sp.GetRequiredService<OrderService>());

Simple Registration (Direct Annotation) ​

Mark a class with [IocRegister] to register it directly:

csharp
public interface IMyService;

[IocRegister<IMyService>]
internal class MyService : IMyService;
// Or non-generic version
[IocRegister(typeof(IMyService))]
internal class MyService : IMyService;
Generated Code
csharp
services.AddTransient<MyService, MyService>();
services.AddTransient<IMyService>((global::System.IServiceProvider sp) => sp.GetRequiredService<MyService>());

Specifying Lifetime ​

csharp
// Transient (default)
[IocRegister<IService>]
internal class TransientService : IService;

// Singleton
[IocRegister<IService>(ServiceLifetime.Singleton)]
internal class SingletonService : IService;

// Scoped
[IocRegister<IService>(ServiceLifetime.Scoped)]
internal class ScopedService : IService;
Generated Code
csharp
services.AddTransient<TransientService, TransientService>();
services.AddTransient<IService>((global::System.IServiceProvider sp) => sp.GetRequiredService<TransientService>());

services.AddSingleton<SingletonService, SingletonService>();
services.AddSingleton<IService>((global::System.IServiceProvider sp) => sp.GetRequiredService<SingletonService>());

services.AddScoped<ScopedService, ScopedService>();
services.AddScoped<IService>((global::System.IServiceProvider sp) => sp.GetRequiredService<ScopedService>());

Multiple Service Types ​

Register a class under multiple service types:

csharp
// Using generic attribute parameters
[IocRegister<IService1, IService2>]
internal class MultiService : IService1, IService2;

// Or using params
[IocRegister(typeof(IService1), typeof(IService2))]
internal class MultiService : IService1, IService2;
Generated Code
csharp
services.AddTransient<MultiService, MultiService>();
services.AddTransient<IService1>((global::System.IServiceProvider sp) => sp.GetRequiredService<MultiService>());
services.AddTransient<IService2>((global::System.IServiceProvider sp) => sp.GetRequiredService<MultiService>());

Register All Interfaces/Base Classes ​

csharp
// Register all implemented interfaces
[IocRegister(RegisterAllInterfaces = true)]
internal class MyService : IService1, IService2, IDisposable;

// Register all base classes
[IocRegister(RegisterAllBaseClasses = true)]
internal class DerivedService : BaseService;
Generated Code
csharp
// RegisterAllInterfaces
services.AddTransient<MyService, MyService>();
services.AddTransient<IService1>((global::System.IServiceProvider sp) => sp.GetRequiredService<MyService>());
services.AddTransient<IService2>((global::System.IServiceProvider sp) => sp.GetRequiredService<MyService>());
services.AddTransient<IDisposable>((global::System.IServiceProvider sp) => sp.GetRequiredService<MyService>());

// RegisterAllBaseClasses
services.AddTransient<DerivedService, DerivedService>();
services.AddTransient<BaseService>((global::System.IServiceProvider sp) => sp.GetRequiredService<DerivedService>());

Usage ​

csharp
// In your startup/program
var services = new ServiceCollection();
services.AddMyProject(); // Generated extension method

Diagnostics ​

IDSeverityDescription
SGIOC001Error[IocRegister] or [IocRegisterFor] cannot be applied to private or abstract classes.
SGIOC011WarningDuplicated registration detected for the same implementation type, same key, and at least one matching tag.
SGIOC032WarningAn explicit service type is skipped because its generic shape does not match the implementation (an open generic implementation with a closed service type, or an open generic service type the implementation does not implement). A closed implementation with an open generic service type it implements, such as typeof(IRepository<>) on UserRepository : IRepository<User>, is registered as the closed form instead.

Custom Method Name ​

Use the SourceGenIocName MSBuild property to customize the generated method name:

xml
<PropertyGroup>
    <SourceGenIocName>MyApp</SourceGenIocName>
</PropertyGroup>

This generates:

csharp
// Generated extension method with custom name
services.AddMyApp();

// Pass tags to the same method to register tagged services
services.AddMyApp("Tag1");

See Tags for how tagged and untagged registrations are selected.

If not specified, the assembly name is used as the default method name.


← Back to Overview

Released under the MIT License.