Skip to content

Latest commit

 

History

History
204 lines (173 loc) · 4.51 KB

File metadata and controls

204 lines (173 loc) · 4.51 KB

Tracking async disposable instances per a composition root

Demonstrates how async disposable instances are tracked per composition root and disposed asynchronously when the composition is disposed.

using Shouldly;
using Pure.DI;

var composition = new Composition();
// Creates two independent roots (queries), each with its own dependency graph
var query1 = composition.Query;
var query2 = composition.Query;

// Disposes of the second query
await query2.DisposeAsync();

// Checks that the connection associated with the second query has been closed
query2.Value.Connection.IsDisposed.ShouldBeTrue();

// At the same time, the connection of the first query remains active
query1.Value.Connection.IsDisposed.ShouldBeFalse();

// Disposes of the first query
await query1.DisposeAsync();

// Now the first connection is also closed
query1.Value.Connection.IsDisposed.ShouldBeTrue();

// Interface for a resource requiring asynchronous disposal (e.g., DB)
interface IDbConnection
{
    bool IsDisposed { get; }
}

class DbConnection : IDbConnection, IAsyncDisposable
{
    public bool IsDisposed { get; private set; }

    public ValueTask DisposeAsync()
    {
        IsDisposed = true;
        return ValueTask.CompletedTask;
    }
}

interface IQuery
{
    public IDbConnection Connection { get; }
}

class Query(IDbConnection connection) : IQuery
{
    public IDbConnection Connection { get; } = connection;
}

partial class Composition
{
    static void Setup() =>

        DI.Setup()
            .Bind().To<DbConnection>()
            .Bind().To<Query>()

            // A special composition root 'Owned' that allows
            // managing the lifetime of IQuery and its dependencies
            .Root<Owned<IQuery>>("Query");
}
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

Note

Async disposable tracking ensures proper async 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<IQuery> Query
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      var perBlockOwned156 = new Owned();
      Owned<IQuery> perBlockOwned155;
      // Creates the owner of an instance
      Owned transientOwned157;
      Owned localOwned3 = perBlockOwned156;
      transientOwned157 = localOwned3;
      lock (_lock)
      {
        perBlockOwned156.Add(transientOwned157);
      }

      IOwned localOwned2 = transientOwned157;
      var transientDbConnection159 = new DbConnection();
      lock (_lock)
      {
        perBlockOwned156.Add(transientDbConnection159);
      }

      IQuery localValue7 = new Query(transientDbConnection159);
      perBlockOwned155 = new Owned<IQuery>(localValue7, localOwned2);
      lock (_lock)
      {
        perBlockOwned156.Add(perBlockOwned155);
      }

      return perBlockOwned155;
    }
  }
}

Class diagram:

---
 config:
  maxTextSize: 2147483647
  maxEdges: 2147483647
  class:
   hideEmptyMembersBox: true
---
classDiagram
	Owned --|> IOwned
	DbConnection --|> IDbConnection
	DbConnection --|> IAsyncDisposable
	Query --|> IQuery
	Composition ..> OwnedᐸIQueryᐳ : OwnedᐸIQueryᐳ Query
	Query *--  DbConnection : IDbConnection
	OwnedᐸIQueryᐳ *--  Owned : IOwned
	OwnedᐸIQueryᐳ *--  Query : IQuery
	namespace Pure.DI {
		class IOwned {
			<<interface>>
		}
		class Owned {
				<<class>>
		}
		class OwnedᐸIQueryᐳ {
				<<struct>>
		}
	}
	namespace Pure.DI.UsageTests.Advanced.TrackingAsyncDisposableScenario {
		class Composition {
		<<partial>>
		+OwnedᐸIQueryᐳ Query
		}
		class DbConnection {
				<<class>>
			+DbConnection()
		}
		class IDbConnection {
			<<interface>>
		}
		class IQuery {
			<<interface>>
		}
		class Query {
				<<class>>
			+Query(IDbConnection connection)
		}
	}
	namespace System {
		class IAsyncDisposable {
			<<interface>>
		}
	}
Loading