This example shows advanced dependency injection techniques using Pure.DI's override mechanism to customize dependency instantiation with runtime arguments and tagged parameters. The implementation creates multiple IDependency instances with values manipulated through explicit overrides.
using Shouldly;
using Pure.DI;
using System.Collections.Immutable;
using System.Drawing;
DI.Setup(nameof(Composition))
.Bind(Tag.Red).To(() => Color.Red)
.Bind().As(Lifetime.Singleton).To<Clock>()
// The factory accepts the widget ID and the layer index
.Bind().To<Func<int, int, IWidget>>(ctx =>
(widgetId, layerIndex) => {
// Overrides the 'id' argument of the constructor with the first lambda argument
ctx.Override(widgetId);
// Overrides the 'layer' tagged argument of the constructor with the second lambda argument
ctx.Override(layerIndex, "layer");
// Overrides the 'name' argument with a formatted string
ctx.Override($"Widget {widgetId} on layer {layerIndex}");
// Resolves the 'Color' dependency tagged with 'Red'
ctx.Inject(Tag.Red, out Color color);
// Overrides the 'color' argument with the resolved value
ctx.Override(color);
// Creates the instance using the overridden values
ctx.Inject<Widget>(out var widget);
return widget;
})
.Bind().To<Dashboard>()
// Composition root
.Root<IDashboard>("Dashboard");
var composition = new Composition();
var dashboard = composition.Dashboard;
dashboard.Widgets.Length.ShouldBe(3);
dashboard.Widgets[0].Id.ShouldBe(0);
dashboard.Widgets[0].Layer.ShouldBe(99);
dashboard.Widgets[0].Name.ShouldBe("Widget 0 on layer 99");
dashboard.Widgets[1].Id.ShouldBe(1);
dashboard.Widgets[1].Name.ShouldBe("Widget 1 on layer 99");
dashboard.Widgets[2].Id.ShouldBe(2);
dashboard.Widgets[2].Name.ShouldBe("Widget 2 on layer 99");
interface IClock
{
DateTimeOffset Now { get; }
}
class Clock : IClock
{
public DateTimeOffset Now => DateTimeOffset.Now;
}
interface IWidget
{
string Name { get; }
int Id { get; }
int Layer { get; }
}
class Widget(
string name,
IClock clock,
int id,
[Tag("layer")] int layer,
Color color)
: IWidget
{
public string Name => name;
public int Id => id;
public int Layer => layer;
}
interface IDashboard
{
ImmutableArray<IWidget> Widgets { get; }
}
class Dashboard(Func<int, int, IWidget> widgetFactory) : IDashboard
{
public ImmutableArray<IWidget> Widgets { get; } =
[
widgetFactory(0, 99),
widgetFactory(1, 99),
widgetFactory(2, 99)
];
}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 runNote
Overrides provide fine-grained control over dependency resolution, allowing you to customize bindings at runtime or for specific scenarios.
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
private Clock? _singletonClock63;
public IDashboard Dashboard
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
Func<int, int, IWidget> transientFunc308 =
[MethodImpl(MethodImplOptions.AggressiveInlining)]
(localWidgetId, localLayerIndex) =>
{
// Overrides the 'id' argument of the constructor with the first lambda argument
// Overrides the 'layer' tagged argument of the constructor with the second lambda argument
// Overrides the 'name' argument with a formatted string
// Resolves the 'Color' dependency tagged with 'Red'
int overriddenInt32 = localWidgetId;
int overriddenInt324 = localLayerIndex;
string overriddenString2 = $"Widget {localWidgetId} on layer {localLayerIndex}";
Drawing.Color transientColor309 = Color.Red;
Drawing.Color localColor = transientColor309;
// Overrides the 'color' argument with the resolved value
// Creates the instance using the overridden values
Drawing.Color overriddenColor5 = localColor;
if (_singletonClock63 is null)
lock (_lock)
if (_singletonClock63 is null)
{
_singletonClock63 = new Clock();
}
return new Widget(overriddenString2, _singletonClock63, overriddenInt32, overriddenInt324, overriddenColor5);
};
return new Dashboard(transientFunc308);
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
Dashboard --|> IDashboard
Composition ..> Dashboard : IDashboard Dashboard
FuncᐸInt32ˏInt32ˏIWidgetᐳ *-- Color : "Red" Color
FuncᐸInt32ˏInt32ˏIWidgetᐳ *-- Widget : Widget
Dashboard *-- FuncᐸInt32ˏInt32ˏIWidgetᐳ : FuncᐸInt32ˏInt32ˏIWidgetᐳ
Widget o-- "Singleton" Clock : IClock
Widget *-- Int32 : Int32
Widget *-- Int32 : "layer" Int32
Widget *-- String : String
Widget *-- Color : Color
namespace Pure.DI.UsageTests.Basics.OverridesScenario {
class Clock {
<<class>>
}
class Composition {
<<partial>>
+IDashboard Dashboard
}
class Dashboard {
<<class>>
+Dashboard(FuncᐸInt32ˏInt32ˏIWidgetᐳ widgetFactory)
}
class IDashboard {
<<interface>>
}
class Widget {
<<class>>
+Widget(String name, IClock clock, Int32 id, Int32 layer, Color color)
}
}
namespace System {
class FuncᐸInt32ˏInt32ˏIWidgetᐳ {
<<delegate>>
}
class Int32 {
<<struct>>
}
class String {
<<class>>
}
}
namespace System.Drawing {
class Color {
<<struct>>
}
}