RonSijm.Syringe
1.0.0
Prefix Reserved
dotnet add package RonSijm.Syringe --version 1.0.0
NuGet\Install-Package RonSijm.Syringe -Version 1.0.0
<PackageReference Include="RonSijm.Syringe" Version="1.0.0" />
<PackageVersion Include="RonSijm.Syringe" Version="1.0.0" />
<PackageReference Include="RonSijm.Syringe" />
paket add RonSijm.Syringe --version 1.0.0
#r "nuget: RonSijm.Syringe, 1.0.0"
#:package RonSijm.Syringe@1.0.0
#addin nuget:?package=RonSijm.Syringe&version=1.0.0
#tool nuget:?package=RonSijm.Syringe&version=1.0.0
RonSijm.Syringe
A powerful dependency injection library for .NET that extends Microsoft's built-in DI container with advanced features like implicit wiring, dynamic service registration, optional dependencies, and extensive extensibility points.
Get it? A Syringe is used to inject things. Yes jokes are better when you have to explain them...
Features Overview
- Implicit Wiring - Automatically register all classes in an assembly with a single line of code
- Dynamic Service Registration - Add services at runtime after the container is built
- Optional Dependencies -
Optional<T>wrapper for dependencies that may not be available - Lazy Resolution -
Lazy<T>support for deferred service instantiation - Attribute-Based Registration - Control registration behavior with attributes like
[DontRegister]and[Singleton] - Configuration-Based Registration - Configure service registration via
appsettings.json - Assembly Bootstrapping -
IBootstrapperinterface for library initialization - Extensibility Points - Hook into service resolution and build events
- Keyed Services - Full support for .NET 8+ keyed services
- Scoped Services - Proper scope management with
CreateScope()
Installation
dotnet add package RonSijm.Syringe
Quick Start
The simplest way to use Syringe is with implicit wiring:
var services = new ServiceCollection();
services.WireImplicit<MyClass>(); // Registers all classes in the assembly containing MyClass
var provider = services.BuildServiceProvider();
Default ServiceCollection Extensions:
<a id='snippet-CodeExample-DefaultServiceCollection-WireByType'></a>
var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit(typeof(ClassA));
var serviceProvider = serviceCollection.BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_Type.cs#L10-L16' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByType' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-DefaultServiceCollection-WireByTypeGeneric'></a>
var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>();
var serviceProvider = serviceCollection.BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_GenericType.cs#L10-L16' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByTypeGeneric' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-DefaultServiceCollection-WireByAssembly'></a>
var serviceCollection = new ServiceCollection();
var assembly = typeof(ClassA).Assembly;
serviceCollection.WireImplicit(assembly, ServiceLifetime.Transient, []);
var serviceProvider = serviceCollection.BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/InitiationMethods/TestWireImplicit_Assembly.cs#L10-L15' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultServiceCollection-WireByAssembly' title='Start of snippet'>anchor</a></sup>
Scoped: (default)
<a id='snippet-CodeExample-DefaultScope'></a>
return typeof(ClassWithGuidA).WireImplicit().BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Scoped.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DefaultScope' title='Start of snippet'>anchor</a></sup>
Transient:
<a id='snippet-CodeExample-TransientScope'></a>
return typeof(ClassWithGuidA).WireImplicit().WithLifetime(ServiceLifetime.Transient).BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Transient.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-TransientScope' title='Start of snippet'>anchor</a></sup>
Singleton:
<a id='snippet-CodeExample-SingletonScope'></a>
return typeof(ClassWithGuidA).WireImplicit().WithLifetime(ServiceLifetime.Singleton).BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Attributes/LifetimeTests/LifetimeTests_Singleton.cs#L10-L12' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-SingletonScope' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-Parameters-registerAsTypesOnly'></a>
var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>(registerAsTypesOnly: [typeof(ClassWithInterfaceA)]);
var serviceProvider = serviceCollection.BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/Parameters/TestWireImplicit_RegisterAsTypesOnly_ClassWithInterface.cs#L11-L15' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-Parameters-registerAsTypesOnly' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-Parameters-registerBothTypeAndInterfaces'></a>
var serviceCollection = new ServiceCollection();
serviceCollection.WireImplicit<ClassA>(registerBothTypeAndInterfaces: [typeof(ClassWithInterfaceA)]);
var serviceProvider = serviceCollection.BuildServiceProvider();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/DefaultServiceCollection/Parameters/TestWireImplicit_RegisterBothTypeAndInterfaces_ClassWithInterface.cs#L10-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-Parameters-registerBothTypeAndInterfaces' title='Start of snippet'>anchor</a></sup>
Extension Methods
By doing a WireImplicit - The extension will return a SyringeServiceCollectionAndRegistration object.
This is an object that keeps track of the last registion that you've made, allowing you to append extra configuration to that specific registration:
<a id='snippet-CodeExample-DontRegisterTypesExtension'></a>
var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().DontRegisterTypes(typeof(ClassA));
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_DontRegisterAsTypes_Class.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DontRegisterTypesExtension' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-DontRegisterTypesWithInterfaces'></a>
var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().DontRegisterTypesWithInterfaces(typeof(InterfaceFor_ClassWithInterfaceA));
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_DontRegisterTypesWithInterface_InterfaceFor_ClassWithInterface.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-DontRegisterTypesWithInterfaces' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-RegisterAsTypesOnly'></a>
var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().RegisterAsTypesOnly(typeof(ClassWithInterfaceA));
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_RegisterAsTypesOnly_ClassWithInterface.cs#L11-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-RegisterAsTypesOnly' title='Start of snippet'>anchor</a></sup>
<a id='snippet-CodeExample-RegisterBothTypeAndInterfaces'></a>
var serviceCollection = new SyringeServiceCollection();
serviceCollection.WireImplicit<ClassA>().RegisterBothTypeAndInterfaces(typeof(ClassWithInterfaceA));
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/CustomCollection/Parameters/TestWireImplicit_RegisterBothTypeAndInterfaces_ClassWithInterface.cs#L10-L13' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-RegisterBothTypeAndInterfaces' title='Start of snippet'>anchor</a></sup>
Registration Extensions
A SyringeServiceCollection contains a internal List<IWireSyringeServiceDescriptorExtension> Extensions { get; set; } = []; - which lets you add your own BeforeBuildServiceProvider Extensions
Example
You can put these kind of configurations in your appsettings.json:
Don't register classes with this interface:
Usecase:
Imagine you have an IProcessor (Interface), DatabaseProcessor and APIProcessor that implement this interface - In your appsettings.json you can register or unregister a specific implementation for this interface.
<a id='snippet-CodeExample-ConfigurationNotWithInterfaces'></a>
var appsetting = """
{
"DependencyInjection": {
"Assembly:RonSijm.Syringe.ExamplesC": {
"Type:ExampleCInterface": "None"
}
}
}
""".ToConfiguration();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Config/ExamplesC_InterfaceRegistrationNone.cs#L11-L23' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-ConfigurationNotWithInterfaces' title='Start of snippet'>anchor</a></sup>
Don't register these specific classes:
Usecase:
Imagine you have an IProcessor (Interface), DatabaseProcessor and APIProcessor and UserProcessor - In your appsettings.json you can register or unregister a specific implementations for this interface.
When your consumer takes an IEnumerable<IProcessor> - you can register or unregister specific processors, and only run selected processors.
<a id='snippet-CodeExample-ConfigurationNotSpecificClass'></a>
var appsetting = """
{
"DependencyInjection": {
"Assembly:RonSijm.Syringe.ExamplesC": {
"Type:ExampleC2": "None",
"Type:ExampleC3": "None",
}
}
}
""".ToConfiguration();
<sup><a href='/src/Tests/RonSijm.Syringe.Tests/Features/Tests/MicrosoftServiceProvider/Registration/Config/ExamplesC_TypeRegistrationNone.cs#L11-L24' title='Snippet source file'>snippet source</a> | <a href='#snippet-CodeExample-ConfigurationNotSpecificClass' title='Start of snippet'>anchor</a></sup>
SyringeServiceProvider
The SyringeServiceProvider is an enhanced service provider that wraps Microsoft's DI container and adds powerful features like dynamic service registration at runtime.
Basic Usage
// Create with options
var provider = new SyringeServiceProvider(options =>
{
options.BuildOnConstruct = true; // Build immediately (default)
options.Services.WireImplicit<MyClass>();
});
// Or create from an existing service collection
var services = new ServiceCollection();
services.AddSingleton<IMyService, MyService>();
var provider = new SyringeServiceProvider(services);
Dynamic Service Registration
One of the most powerful features is the ability to add services after the container is built:
var provider = new SyringeServiceProvider();
// Later, dynamically load more services
var newServices = new ServiceCollection();
newServices.AddSingleton<INewService, NewService>();
await provider.LoadServiceDescriptors(newServices);
provider.Build(); // Rebuild to include new services
This is particularly useful for plugin architectures or lazy-loading scenarios.
Optional Dependencies
The Optional<T> wrapper allows you to inject dependencies that may not be registered. This is especially useful in Blazor where injecting null throws an exception.
public class MyComponent
{
private readonly Optional<IOptionalService> _optionalService;
public MyComponent(Optional<IOptionalService> optionalService)
{
_optionalService = optionalService;
}
public void DoWork()
{
if (_optionalService.HasValue)
{
_optionalService.Value.DoSomething();
}
}
}
Optional<T> is automatically registered when using SyringeServiceProvider.
Lazy Resolution
Syringe supports Lazy<T> for deferred service instantiation:
public class MyService
{
private readonly Lazy<IExpensiveService> _expensiveService;
public MyService(Lazy<IExpensiveService> expensiveService)
{
_expensiveService = expensiveService;
}
public void DoWork()
{
// Service is only created when first accessed
_expensiveService.Value.Process();
}
}
Attribute-Based Registration
Control how classes are registered using attributes:
Don't Register Attribute
Prevent a class from being automatically registered:
[Registration.DontRegister]
public class InternalHelper
{
// This class won't be registered by WireImplicit
}
Lifetime Attributes
Specify the service lifetime:
[Lifetime.Singleton]
public class MySingletonService : IMyService
{
// Will be registered as singleton
}
IBootstrapper Interface
The IBootstrapper interface allows libraries to define their own service registrations that are automatically discovered and executed:
public class MyLibraryBootstrapper : IBootstrapper
{
public Task<IEnumerable<ServiceDescriptor>> Bootstrap()
{
var services = new ServiceCollection();
services.AddSingleton<IMyLibraryService, MyLibraryService>();
services.AddScoped<IRepository, Repository>();
return Task.FromResult<IEnumerable<ServiceDescriptor>>(services);
}
}
When an assembly is loaded, Syringe automatically discovers and executes all IBootstrapper implementations.
ILoadAfterExtension
Hook into the assembly loading process with ILoadAfterExtension:
public class MyLoadExtension : BaseLoadAfterExtension
{
public override void AssembliesLoaded(List<Assembly> loadedAssemblies)
{
// Called after assemblies are loaded
Console.WriteLine($"Loaded {loadedAssemblies.Count} assemblies");
}
public override void DescriptorsLoaded(List<ServiceDescriptor> loadedDescriptors)
{
// Called after service descriptors are registered
Console.WriteLine($"Registered {loadedDescriptors.Count} services");
}
}
Extensibility Points
ISyringeAfterBuildExtension
Execute code after the service provider is built:
public class MyAfterBuildExtension : ISyringeAfterBuildExtension
{
private SyringeServiceProvider _provider;
public void SetReference(SyringeServiceProvider serviceProvider)
{
_provider = serviceProvider;
}
public void Process(List<ServiceDescriptor> loadedDescriptors, bool isInitialBuild)
{
// Called after Build() is called
}
}
// Register the extension
var provider = new SyringeServiceProvider(options =>
{
options.WithAfterBuildExtension<MyAfterBuildExtension>();
});
ISyringeServiceProviderAfterServiceExtension
Decorate or modify services after they are resolved:
public class MyAfterServiceExtension : ISyringeServiceProviderAfterServiceExtension
{
public void SetReference(SyringeServiceProvider serviceProvider) { }
public void Decorate(Type serviceType, object service)
{
// Called after every service resolution
}
}
// Register the extension
var provider = new SyringeServiceProvider(options =>
{
options.WithAfterGetService<MyAfterServiceExtension>();
});
Keyed Services
Syringe fully supports .NET 8+ keyed services:
var provider = new SyringeServiceProvider(services =>
{
services.AddKeyedSingleton<IMyService, ServiceA>("A");
services.AddKeyedSingleton<IMyService, ServiceB>("B");
});
var serviceA = provider.GetKeyedService<IMyService>("A");
var serviceB = provider.GetKeyedService<IMyService>("B");
Scoped Services
Create and manage scopes properly:
var provider = new SyringeServiceProvider();
using (var scope = provider.CreateScope())
{
var scopedService = scope.ServiceProvider.GetService<IMyScopedService>();
// Use scoped service
}
// Scope is disposed, scoped services are cleaned up
Global Settings
Configure default behavior globally:
// Set default service lifetime (default is Scoped)
SyringeGlobalSettings.DefaultServiceLifetime = ServiceLifetime.Transient;
// Control whether types with interfaces are also registered as themselves
SyringeGlobalSettings.RegisterAsTypeWhenTypeHasInterfaces = true;
Fluxor Integration
Syringe includes optional Fluxor integration for state management:
var provider = new SyringeServiceProvider(options =>
{
options.UseFluxor(fluxorOptions =>
{
fluxorOptions.ScanAssemblies(typeof(Program).Assembly);
});
});
Install the Fluxor integration package:
dotnet add package RonSijm.Syringe.Fluxor
Related Projects
- RonSijm.Blazyload - Lazy-loading for Blazor WebAssembly using Syringe
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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 is compatible. 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 is compatible. 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. |
-
net10.0
- Microsoft.Extensions.Configuration (>= 10.0.3)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.3)
- Microsoft.Extensions.Configuration.Json (>= 10.0.3)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.3)
- RonSijm.Syringe.Lib (>= 1.0.0)
-
net8.0
- Microsoft.Extensions.Configuration (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.2)
- Microsoft.Extensions.Configuration.Json (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
- RonSijm.Syringe.Lib (>= 1.0.0)
-
net9.0
- Microsoft.Extensions.Configuration (>= 9.0.13)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.13)
- Microsoft.Extensions.Configuration.Json (>= 9.0.13)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.13)
- RonSijm.Syringe.Lib (>= 1.0.0)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on RonSijm.Syringe:
| Package | Downloads |
|---|---|
|
RonSijm.Blazyload
A C# Blazor library to effortlessly implement Lazy Loading and Dependency Injection |
|
|
RonSijm.Syringe.Fluxor
A Fluxor library for to make it easier to do dependency injection. |
|
|
RonSijm.Blazyload.Hosting
A C# Blazor library to effortlessly implement Lazy Loading and Dependency Injection |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 287 | 2/11/2026 |
| 1.0.0-RC1.7 | 98 | 2/1/2026 |
| 1.0.0-RC1.6 | 57 | 1/30/2026 |
| 1.0.0-Alpha1.5 | 529 | 4/9/2025 |
| 1.0.0-Alpha1.4 | 236 | 4/7/2025 |
| 1.0.0-Alpha1.3 | 170 | 4/7/2025 |
| 1.0.0-Alpha1.2 | 176 | 4/7/2025 |
| 1.0.0-Alpha1.1 | 168 | 4/6/2025 |
| 1.0.0-Alpha1 | 292 | 3/31/2025 |
| 0.2.1 | 984 | 3/4/2025 |
| 0.2.0 | 245 | 2/18/2025 |
| 0.1.1 | 226 | 2/4/2025 |
| 0.0.3 | 277 | 12/10/2024 |
| 0.0.2 | 165 | 12/10/2024 |
| 0.0.1 | 185 | 12/10/2024 |