accumulators.md

July 22, 2026 · View on GitHub

Accumulators

Accumulators allow you to accumulate instances of certain types and lifetimes. Use this when you need an aggregated view of created dependencies (for diagnostics, telemetry, or registries).

using Shouldly;
using Pure.DI;
using static Pure.DI.Lifetime;

DI.Setup(nameof(Composition))
    // Accumulates all instances implementing ITelemetrySource
    // into a collection of type TelemetryRegistry.
    // The accumulation applies to Transient and Singleton lifetimes.
    .Accumulate<ITelemetrySource, TelemetryRegistry>(Transient, Singleton)

    // Infrastructure bindings
    .Bind<IDataSource>().As(PerBlock).To<SqlDataSource>()
    .Bind<IDataSource>(Tag.Type).To<SqlDataSource>()
    .Bind<IDataSource>(Tag.Type).As(Singleton).To<NetworkDataSource>()
    .Bind<IDashboard>().To<Dashboard>()

    // Composition root
    .Root<(IDashboard dashboard, TelemetryRegistry registry)>("Root");

var composition = new Composition();
var (dashboard, registry) = composition.Root;

// Checks that all telemetry sources have been collected
registry.Count.ShouldBe(3);
// The order of accumulation depends on the order of object creation in the graph
registry[0].ShouldBeOfType<NetworkDataSource>();
registry[1].ShouldBeOfType<SqlDataSource>();
registry[2].ShouldBeOfType<Dashboard>();

// Represents a component that produces telemetry data
interface ITelemetrySource;

// Accumulator for collecting all telemetry sources in the object graph
class TelemetryRegistry : List<ITelemetrySource>;

// Abstract data source interface
interface IDataSource;

// SQL database implementation acting as a telemetry source
class SqlDataSource : IDataSource, ITelemetrySource;

// Network data source implementation acting as a telemetry source
class NetworkDataSource : IDataSource, ITelemetrySource;

// Dashboard interface
interface IDashboard;

// Dashboard implementation aggregating data from sources
class Dashboard(
    [Tag(typeof(SqlDataSource))] IDataSource primaryDb,
    [Tag(typeof(NetworkDataSource))] IDataSource externalApi,
    IDataSource fallbackDb)
    : IDashboard, ITelemetrySource;
Running this code sample locally
dotnet --list-sdk
  • Create a net10.0 (or later) console application
dotnet new console -n Sample
dotnet 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 run

Limitations: accumulation order depends on object creation order in the graph, so do not treat it as a stable business ordering. See also: Enumerable, Lifetimes.

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 NetworkDataSource? _singletonNetworkDataSource;

  public (IDashboard dashboard, TelemetryRegistry registry) Root
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      var perBlockTelemetryRegistry = new TelemetryRegistry();
      var perBlockSqlDataSource = new SqlDataSource();
      if (_singletonNetworkDataSource is null)
        lock (_lock)
          if (_singletonNetworkDataSource is null)
          {
            NetworkDataSource _singletonNetworkDataSourceTemp;
            _singletonNetworkDataSourceTemp = new NetworkDataSource();
            perBlockTelemetryRegistry.Add(_singletonNetworkDataSourceTemp);
            Thread.MemoryBarrier();
            _singletonNetworkDataSource = _singletonNetworkDataSourceTemp;
          }

      var transientSqlDataSource = new SqlDataSource();
      lock (_lock)
      {
        perBlockTelemetryRegistry.Add(transientSqlDataSource);
      }

      var transientDashboard = new Dashboard(transientSqlDataSource, _singletonNetworkDataSource, perBlockSqlDataSource);
      lock (_lock)
      {
        perBlockTelemetryRegistry.Add(transientDashboard);
      }

      return (transientDashboard, perBlockTelemetryRegistry);
    }
  }
}

Class diagram:

---
 config:
  class:
   hideEmptyMembersBox: true
---
classDiagram
	SqlDataSource --|> IDataSource : typeof(Pure.DI.UsageTests.Advanced.AccumulatorScenario.SqlDataSource)
	NetworkDataSource --|> IDataSource : typeof(Pure.DI.UsageTests.Advanced.AccumulatorScenario.NetworkDataSource)
	Dashboard --|> IDashboard
	Composition ..> ValueTupleᐸIDashboardˏTelemetryRegistryᐳ : ValueTupleᐸIDashboardˏTelemetryRegistryᐳ Root
	Dashboard o-- "PerBlock" SqlDataSource : IDataSource
	Dashboard *-- SqlDataSource : typeof(Pure.DI.UsageTests.Advanced.AccumulatorScenario.SqlDataSource) IDataSource
	Dashboard o-- "Singleton" NetworkDataSource : typeof(Pure.DI.UsageTests.Advanced.AccumulatorScenario.NetworkDataSource) IDataSource
	ValueTupleᐸIDashboardˏTelemetryRegistryᐳ *-- Dashboard : IDashboard
	ValueTupleᐸIDashboardˏTelemetryRegistryᐳ o-- "PerBlock" TelemetryRegistry : TelemetryRegistry
	namespace Pure.DI.UsageTests.Advanced.AccumulatorScenario {
		class Composition {
		<<partial>>
		+ValueTupleᐸIDashboardˏTelemetryRegistryᐳ Root
		}
		class Dashboard {
				<<class>>
			+Dashboard(IDataSource primaryDb, IDataSource externalApi, IDataSource fallbackDb)
		}
		class IDashboard {
			<<interface>>
		}
		class IDataSource {
			<<interface>>
		}
		class NetworkDataSource {
				<<class>>
			+NetworkDataSource()
		}
		class SqlDataSource {
				<<class>>
			+SqlDataSource()
		}
		class TelemetryRegistry {
				<<class>>
		}
	}
	namespace System {
		class ValueTupleᐸIDashboardˏTelemetryRegistryᐳ {
				<<tuple>>
			+ValueTuple(IDashboard item1, TelemetryRegistry item2)
		}
	}

See also: