Demonstrates how disposable instances are tracked per composition root and disposed when the composition is disposed.
using Shouldly;
using Pure.DI;
var composition = new Composition();
var orderProcessingService1 = composition.OrderProcessingService;
var orderProcessingService2 = composition.OrderProcessingService;
orderProcessingService2.Dispose();
// Checks that the disposable instances
// associated with orderProcessingService2 have been disposed of
orderProcessingService2.Value.DbConnection.IsDisposed.ShouldBeTrue();
// Checks that the disposable instances
// associated with orderProcessingService1 have not been disposed of
orderProcessingService1.Value.DbConnection.IsDisposed.ShouldBeFalse();
orderProcessingService1.Dispose();
// Checks that the disposable instances
// associated with orderProcessingService1 have been disposed of
orderProcessingService1.Value.DbConnection.IsDisposed.ShouldBeTrue();
interface IDbConnection
{
bool IsDisposed { get; }
}
class DbConnection : IDbConnection, IDisposable
{
public bool IsDisposed { get; private set; }
public void Dispose() => IsDisposed = true;
}
interface IOrderProcessingService
{
public IDbConnection DbConnection { get; }
}
class OrderProcessingService(IDbConnection dbConnection) : IOrderProcessingService
{
public IDbConnection DbConnection { get; } = dbConnection;
}
partial class Composition
{
static void Setup() =>
DI.Setup()
.Bind().To<DbConnection>()
.Bind().To<OrderProcessingService>()
// A special composition root
// that allows to manage disposable dependencies
.Root<Owned<IOrderProcessingService>>("OrderProcessingService");
}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
Disposable tracking ensures proper cleanup of all disposable instances within a composition scope.
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 Owned<IOrderProcessingService> OrderProcessingService
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
var perBlockOwned167 = new Owned();
Owned<IOrderProcessingService> perBlockOwned166;
// Creates the owner of an instance
Owned transientOwned168;
Owned localOwned7 = perBlockOwned167;
transientOwned168 = localOwned7;
lock (_lock)
{
perBlockOwned167.Add(transientOwned168);
}
IOwned localOwned6 = transientOwned168;
var transientDbConnection170 = new DbConnection();
lock (_lock)
{
perBlockOwned167.Add(transientDbConnection170);
}
IOrderProcessingService localValue10 = new OrderProcessingService(transientDbConnection170);
perBlockOwned166 = new Owned<IOrderProcessingService>(localValue10, localOwned6);
lock (_lock)
{
perBlockOwned167.Add(perBlockOwned166);
}
return perBlockOwned166;
}
}
}Class diagram:
---
config:
maxTextSize: 2147483647
maxEdges: 2147483647
class:
hideEmptyMembersBox: true
---
classDiagram
Owned --|> IOwned
DbConnection --|> IDbConnection
OrderProcessingService --|> IOrderProcessingService
Composition ..> OwnedᐸIOrderProcessingServiceᐳ : OwnedᐸIOrderProcessingServiceᐳ OrderProcessingService
OrderProcessingService *-- DbConnection : IDbConnection
OwnedᐸIOrderProcessingServiceᐳ *-- Owned : IOwned
OwnedᐸIOrderProcessingServiceᐳ *-- OrderProcessingService : IOrderProcessingService
namespace Pure.DI {
class IOwned {
<<interface>>
}
class Owned {
<<class>>
}
class OwnedᐸIOrderProcessingServiceᐳ {
<<struct>>
}
}
namespace Pure.DI.UsageTests.Advanced.TrackingDisposableScenario {
class Composition {
<<partial>>
+OwnedᐸIOrderProcessingServiceᐳ OrderProcessingService
}
class DbConnection {
<<class>>
+DbConnection()
}
class IDbConnection {
<<interface>>
}
class IOrderProcessingService {
<<interface>>
}
class OrderProcessingService {
<<class>>
+OrderProcessingService(IDbConnection dbConnection)
}
}