This example uses a parameterized factory so dependencies can be created with runtime arguments. The service creates sensors with specific IDs at instantiation time. It is a type-safe way to combine DI-managed creation with runtime data.
using Shouldly;
using Pure.DI;
using System.Collections.Generic;
DI.Setup(nameof(Composition))
.Bind().To<Sensor>()
.Bind().To<SmartHome>()
// Composition root
.Root<ISmartHome>("SmartHome");
var composition = new Composition();
var smartHome = composition.SmartHome;
var sensors = smartHome.Sensors;
sensors.Count.ShouldBe(2);
sensors[0].Id.ShouldBe(101);
sensors[1].Id.ShouldBe(102);
interface ISensor
{
int Id { get; }
}
class Sensor(int id) : ISensor
{
public int Id { get; } = id;
}
interface ISmartHome
{
IReadOnlyList<ISensor> Sensors { get; }
}
class SmartHome(Func<int, ISensor> sensorFactory) : ISmartHome
{
public IReadOnlyList<ISensor> Sensors { get; } =
[
// Use the injected factory to create a sensor with ID 101
sensorFactory(101),
// Create another sensor with ID 102
sensorFactory(102)
];
}Running this code sample locally
- Make sure you have the .NET SDK 10.0 or later installed
dotnet --list-sdk- Create a net10.0 (or later) console application
dotnet new console -n Sampledotnet add package Pure.DI
dotnet add package Shouldly- Copy the example code into the Program.cs file
You are ready to run the example 🚀
dotnet runDelayed dependency instantiation:
- Injection of dependencies requiring runtime parameters
- Creation of distinct instances with different configurations
- Type-safe resolution of dependencies with constructor arguments Limitations: runtime arguments improve flexibility but can increase coupling between call sites and construction signatures. Common pitfalls:
- Passing infrastructure concerns as runtime arguments instead of normal dependencies.
- Duplicating argument validation logic across consumers. See also: Injection on demand, Root arguments.
The following partial class will be generated:
partial class Composition
{
#if NET9_0_OR_GREATER
private readonly Lock _lock = new Lock();
#else
private readonly Object _lock = new Object();
#endif
public ISmartHome SmartHome
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<int, ISensor> perBlockFunc299;
Func<int, ISensor> localFactory1 = new Func<int, ISensor>((int localArg1) =>
{
lock (_lock)
{
int overriddenInt32 = localArg1;
return new Sensor(overriddenInt32);
}
});
perBlockFunc299 = localFactory1;
return new SmartHome(perBlockFunc299);
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
Sensor --|> ISensor
SmartHome --|> ISmartHome
Composition ..> SmartHome : ISmartHome SmartHome
Sensor *-- Int32 : Int32
SmartHome o-- "PerBlock" FuncᐸInt32ˏISensorᐳ : FuncᐸInt32ˏISensorᐳ
FuncᐸInt32ˏISensorᐳ *-- Sensor : ISensor
namespace Pure.DI.UsageTests.Basics.InjectionOnDemandWithArgumentsScenario {
class Composition {
<<partial>>
+ISmartHome SmartHome
}
class ISensor {
<<interface>>
}
class ISmartHome {
<<interface>>
}
class Sensor {
<<class>>
+Sensor(Int32 id)
}
class SmartHome {
<<class>>
+SmartHome(FuncᐸInt32ˏISensorᐳ sensorFactory)
}
}
namespace System {
class FuncᐸInt32ˏISensorᐳ {
<<delegate>>
}
class Int32 {
<<struct>>
}
}