From 25f7926cb6f58acbbb68e9c9ae07549376bba58d Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Mon, 18 Mar 2024 14:27:19 +0300 Subject: [PATCH] Fix partial method call when using ctx.Tag --- readme/tag-unique.md | 16 +-- ...osable-instances-per-a-composition-root.md | 134 ++++++++---------- .../Core/Code/CompositionBuilder.cs | 2 +- .../Core/Code/StatementCodeBuilder.cs | 6 +- .../PartialMethodsTests.cs | 83 +++++++++++ ...ckingDisposableInstancesPerRootScenario.cs | 85 ++++++----- 6 files changed, 201 insertions(+), 125 deletions(-) diff --git a/readme/tag-unique.md b/readme/tag-unique.md index 184fb1172..da5f87a47 100644 --- a/readme/tag-unique.md +++ b/readme/tag-unique.md @@ -46,11 +46,11 @@ classDiagram + object Resolve(Type type, object? tag) } class IEnumerableᐸIDependencyᐸStringᐳᐳ - AbcDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : fdf5b99a-63b1-4777-b63a-748d93ebc10e + AbcDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : cf28a5f5-c0c6-4d80-9869-561448e661e1 class AbcDependencyᐸStringᐳ { +AbcDependency() } - XyzDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : 46803952-91d4-43d1-8cda-8202864b3935 + XyzDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : 0c31c79e-0327-4b14-9fc1-166a5a5aa440 class XyzDependencyᐸStringᐳ { +XyzDependency() } @@ -64,8 +64,8 @@ classDiagram class IServiceᐸStringᐳ { <> } - IEnumerableᐸIDependencyᐸStringᐳᐳ *-- AbcDependencyᐸStringᐳ : fdf5b99a-63b1-4777-b63a-748d93ebc10e IDependencyᐸStringᐳ - IEnumerableᐸIDependencyᐸStringᐳᐳ *-- XyzDependencyᐸStringᐳ : 46803952-91d4-43d1-8cda-8202864b3935 IDependencyᐸStringᐳ + IEnumerableᐸIDependencyᐸStringᐳᐳ *-- AbcDependencyᐸStringᐳ : cf28a5f5-c0c6-4d80-9869-561448e661e1 IDependencyᐸStringᐳ + IEnumerableᐸIDependencyᐸStringᐳᐳ *-- XyzDependencyᐸStringᐳ : 0c31c79e-0327-4b14-9fc1-166a5a5aa440 IDependencyᐸStringᐳ Composition ..> ServiceᐸStringᐳ : IServiceᐸStringᐳ Root ServiceᐸStringᐳ o-- "PerBlock" IEnumerableᐸIDependencyᐸStringᐳᐳ : IEnumerableᐸIDependencyᐸStringᐳᐳ ``` @@ -157,11 +157,11 @@ partial class Composition " + object Resolve(Type type, object? tag)\n" + " }\n" + " class IEnumerableᐸIDependencyᐸStringᐳᐳ\n" + - " AbcDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : fdf5b99a-63b1-4777-b63a-748d93ebc10e \n" + + " AbcDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : cf28a5f5-c0c6-4d80-9869-561448e661e1 \n" + " class AbcDependencyᐸStringᐳ {\n" + " +AbcDependency()\n" + " }\n" + - " XyzDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : 46803952-91d4-43d1-8cda-8202864b3935 \n" + + " XyzDependencyᐸStringᐳ --|> IDependencyᐸStringᐳ : 0c31c79e-0327-4b14-9fc1-166a5a5aa440 \n" + " class XyzDependencyᐸStringᐳ {\n" + " +XyzDependency()\n" + " }\n" + @@ -175,8 +175,8 @@ partial class Composition " class IServiceᐸStringᐳ {\n" + " <>\n" + " }\n" + - " IEnumerableᐸIDependencyᐸStringᐳᐳ *-- AbcDependencyᐸStringᐳ : fdf5b99a-63b1-4777-b63a-748d93ebc10e IDependencyᐸStringᐳ\n" + - " IEnumerableᐸIDependencyᐸStringᐳᐳ *-- XyzDependencyᐸStringᐳ : 46803952-91d4-43d1-8cda-8202864b3935 IDependencyᐸStringᐳ\n" + + " IEnumerableᐸIDependencyᐸStringᐳᐳ *-- AbcDependencyᐸStringᐳ : cf28a5f5-c0c6-4d80-9869-561448e661e1 IDependencyᐸStringᐳ\n" + + " IEnumerableᐸIDependencyᐸStringᐳᐳ *-- XyzDependencyᐸStringᐳ : 0c31c79e-0327-4b14-9fc1-166a5a5aa440 IDependencyᐸStringᐳ\n" + " Composition ..> ServiceᐸStringᐳ : IServiceᐸStringᐳ Root\n" + " ServiceᐸStringᐳ o-- \"PerBlock\" IEnumerableᐸIDependencyᐸStringᐳᐳ : IEnumerableᐸIDependencyᐸStringᐳᐳ"; } diff --git a/readme/tracking-disposable-instances-per-a-composition-root.md b/readme/tracking-disposable-instances-per-a-composition-root.md index 5d67f01e4..3012b9e60 100644 --- a/readme/tracking-disposable-instances-per-a-composition-root.md +++ b/readme/tracking-disposable-instances-per-a-composition-root.md @@ -25,86 +25,85 @@ class Service(IDependency dependency) : IService public IDependency Dependency { get; } = dependency; } -internal static class Disposables +readonly struct Owned( + T value, + List disposables) + : IDisposable { - public static readonly IDisposable Empty = new EmptyDisposable(); + public T Value { get; } = value; - public static IDisposable Combine(this Stack disposables) => - new CombinedDisposable(disposables); - - private class EmptyDisposable : IDisposable + public void Dispose() { - public void Dispose() { } - } - - internal class CombinedDisposable(Stack disposables) - : IDisposable - { - public void Dispose() - { - while (disposables.TryPop(out var disposable)) - { - disposable.Dispose(); - } - } + disposables.Reverse(); + disposables.ForEach(i => i.Dispose()); } } partial class Composition { - private readonly ConcurrentDictionary> _disposables = new(); + private List _disposables = []; private void Setup() => DI.Setup(nameof(Composition)) - // Specifies to call a partial method - // named OnNewInstance when an instance is created + // Specifies to call the partial method OnNewInstance + // when an instance is created .Hint(Hint.OnNewInstance, "On") - // Specifies to call the partial method + // Specifies to call the partial method OnNewInstance // only for instances with lifetime // Transient, PerResolve and PerBlock .Hint( Hint.OnNewInstanceLifetimeRegularExpression, "Transient|PerResolve|PerBlock") + // Specifies to call the partial method OnNewInstance + // for instances other than Composition.Owned + .Hint( + Hint.OnNewInstanceImplementationTypeNameRegularExpression, + "^((?!Owned<).)*$") + + .Bind().To(ctx => + { + ctx.Inject(out TT value); + var disposables = _disposables; + _disposables = []; + return new Owned(value, disposables); + }) + .Bind().To() .Bind().To() - .Bind().To(_ => - _disposables.TryRemove(Environment.CurrentManagedThreadId, out var disposables) - ? disposables.Combine() - : Disposables.Empty) - - .Root<(IService service, IDisposable combinedDisposables)>("Root"); + .Root>("Root"); partial void OnNewInstance( ref T value, object? tag, Lifetime lifetime) { - if (value is IDisposable disposable && value is not Disposables.CombinedDisposable) - { - var disposables = _disposables.GetOrAdd( - Environment.CurrentManagedThreadId, - _ => new Stack()); - - disposables.Push(disposable); - } + if (value is not IDisposable disposable) return; + _disposables.Add(disposable); } } var composition = new Composition(); + var root1 = composition.Root; var root2 = composition.Root; -root2.combinedDisposables.Dispose(); +root2.Dispose(); // Checks that the disposable instances // associated with root1 have been disposed of -root2.service.Dependency.IsDisposed.ShouldBeTrue(); +root2.Value.Dependency.IsDisposed.ShouldBeTrue(); // Checks that the disposable instances // associated with root2 have not been disposed of -root1.service.Dependency.IsDisposed.ShouldBeFalse(); +root1.Value.Dependency.IsDisposed.ShouldBeFalse(); + +root1.Dispose(); + +// Checks that the disposable instances +// associated with root2 have been disposed of +root1.Value.Dependency.IsDisposed.ShouldBeTrue(); ```
@@ -113,16 +112,12 @@ root1.service.Dependency.IsDisposed.ShouldBeFalse(); ```mermaid classDiagram class Composition { - +ValueTupleᐸIServiceˏIDisposableᐳ Root + +OwnedᐸIServiceᐳ Root + T ResolveᐸTᐳ() + T ResolveᐸTᐳ(object? tag) + object Resolve(Type type) + object Resolve(Type type, object? tag) } - class ValueTupleᐸIServiceˏIDisposableᐳ { - +ValueTuple(IService item1, IDisposable item2) - } - class IDisposable Dependency --|> IDependency : class Dependency { +Dependency() @@ -137,10 +132,9 @@ classDiagram class IService { <> } - ValueTupleᐸIServiceˏIDisposableᐳ *-- Service : IService - ValueTupleᐸIServiceˏIDisposableᐳ *-- IDisposable : IDisposable Service *-- Dependency : IDependency - Composition ..> ValueTupleᐸIServiceˏIDisposableᐳ : ValueTupleᐸIServiceˏIDisposableᐳ Root + Composition ..> OwnedᐸIServiceᐳ : OwnedᐸIServiceᐳ Root + OwnedᐸIServiceᐳ *-- Service : IService ```
@@ -163,19 +157,22 @@ partial class Composition _rootM03D18di = baseComposition._rootM03D18di; } - public (Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables) Root + public Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned Root { get { - Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Dependency transientM03D18di3_Dependency = new Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Dependency(); - OnNewInstance(ref transientM03D18di3_Dependency, null, Pure.DI.Lifetime.Transient); - System.IDisposable transientM03D18di2_IDisposable = _disposables.TryRemove(Environment.CurrentManagedThreadId, out var disposables_M03D18di1) ? disposables_M03D18di1.Combine() : Disposables.Empty; - OnNewInstance(ref transientM03D18di2_IDisposable, null, Pure.DI.Lifetime.Transient); - Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Service transientM03D18di1_Service = new Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Service(transientM03D18di3_Dependency); + Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Dependency transientM03D18di2_Dependency = new Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Dependency(); + OnNewInstance(ref transientM03D18di2_Dependency, null, Pure.DI.Lifetime.Transient); + Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Service transientM03D18di1_Service = new Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Service(transientM03D18di2_Dependency); OnNewInstance(ref transientM03D18di1_Service, null, Pure.DI.Lifetime.Transient); - (Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables) transientM03D18di0_ValueTuple = (transientM03D18di1_Service, transientM03D18di2_IDisposable); - OnNewInstance<(Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables)>(ref transientM03D18di0_ValueTuple, null, Pure.DI.Lifetime.Transient); - return transientM03D18di0_ValueTuple; + Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned transientM03D18di0_Owned; + { + var value_M03D18di1 = transientM03D18di1_Service; + var disposables_M03D18di2 = _disposables; + _disposables = []; + transientM03D18di0_Owned = new Owned(value_M03D18di1, disposables_M03D18di2); + } + return transientM03D18di0_Owned; } } @@ -226,16 +223,12 @@ partial class Composition return "classDiagram\n" + " class Composition {\n" + - " +ValueTupleᐸIServiceˏIDisposableᐳ Root\n" + + " +OwnedᐸIServiceᐳ Root\n" + " + T ResolveᐸTᐳ()\n" + " + T ResolveᐸTᐳ(object? tag)\n" + " + object Resolve(Type type)\n" + " + object Resolve(Type type, object? tag)\n" + " }\n" + - " class ValueTupleᐸIServiceˏIDisposableᐳ {\n" + - " +ValueTuple(IService item1, IDisposable item2)\n" + - " }\n" + - " class IDisposable\n" + " Dependency --|> IDependency : \n" + " class Dependency {\n" + " +Dependency()\n" + @@ -250,10 +243,9 @@ partial class Composition " class IService {\n" + " <>\n" + " }\n" + - " ValueTupleᐸIServiceˏIDisposableᐳ *-- Service : IService\n" + - " ValueTupleᐸIServiceˏIDisposableᐳ *-- IDisposable : IDisposable\n" + " Service *-- Dependency : IDependency\n" + - " Composition ..> ValueTupleᐸIServiceˏIDisposableᐳ : ValueTupleᐸIServiceˏIDisposableᐳ Root"; + " Composition ..> OwnedᐸIServiceᐳ : OwnedᐸIServiceᐳ Root\n" + + " OwnedᐸIServiceᐳ *-- Service : IService"; } private readonly static int _bucketSizeM03D18di; @@ -262,13 +254,13 @@ partial class Composition static Composition() { var valResolverM03D18di_0000 = new ResolverM03D18di_0000(); - ResolverM03D18di<(Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables)>.Value = valResolverM03D18di_0000; + ResolverM03D18di>.Value = valResolverM03D18di_0000; _bucketsM03D18di = global::Pure.DI.Buckets>.Create( 1, out _bucketSizeM03D18di, new global::Pure.DI.Pair>[1] { - new global::Pure.DI.Pair>(typeof((Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables)), valResolverM03D18di_0000) + new global::Pure.DI.Pair>(typeof(Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned), valResolverM03D18di_0000) }); } @@ -287,21 +279,21 @@ partial class Composition } } - private sealed class ResolverM03D18di_0000: global::Pure.DI.IResolver, global::Pure.DI.IResolver + private sealed class ResolverM03D18di_0000: global::Pure.DI.IResolver>, global::Pure.DI.IResolver { - public (Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables) Resolve(Composition composition) + public Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned Resolve(Composition composition) { return composition.Root; } - public (Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables) ResolveByTag(Composition composition, object tag) + public Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned ResolveByTag(Composition composition, object tag) { switch (tag) { case null: return composition.Root; } - throw new global::System.InvalidOperationException($"Cannot resolve composition root \"{tag}\" of type (Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.IService service, System.IDisposable combinedDisposables)."); + throw new global::System.InvalidOperationException($"Cannot resolve composition root \"{tag}\" of type Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario.Owned."); } object global::Pure.DI.IResolver.Resolve(Composition composition) { diff --git a/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs b/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs index 63810d3f8..3362c780c 100644 --- a/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/CompositionBuilder.cs @@ -30,7 +30,7 @@ public CompositionCode Build(DependencyGraph graph) rootBlock.Current, new LinesBuilder(), new LinesBuilder(), - default, + root.Injection.Tag != MdTag.ContextTag ? root.Injection.Tag : default, default); foreach (var perResolveVar in map.GetPerResolves()) diff --git a/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs b/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs index 267811be3..2cb60fa00 100644 --- a/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs @@ -10,7 +10,11 @@ internal class StatementCodeBuilder( { public void Build(BuildContext ctx, in IStatement statement) { - ctx = ctx with { ContextTag = statement.Current.Injection.Tag }; + if (statement.Current.Injection.Tag != MdTag.ContextTag) + { + ctx = ctx with { ContextTag = statement.Current.Injection.Tag }; + } + switch (statement) { case Variable variable: diff --git a/tests/Pure.DI.IntegrationTests/PartialMethodsTests.cs b/tests/Pure.DI.IntegrationTests/PartialMethodsTests.cs index 08bf0fb7e..f56730d89 100644 --- a/tests/Pure.DI.IntegrationTests/PartialMethodsTests.cs +++ b/tests/Pure.DI.IntegrationTests/PartialMethodsTests.cs @@ -192,6 +192,89 @@ public static void Main() result.StdOut.ShouldBe(ImmutableArray.Create("System.Func`1[Sample.IDependency] '' PerResolve created", "Sample.Dependency '' Singleton created", "Sample.Service '' Transient created"), result); } + [Fact] + public async Task ShouldSupportCtxTag() + { + // Given + + // When + var result = await """ +using System; +using Pure.DI; + +namespace Sample +{ + interface IDependency {} + + class Dependency: IDependency {} + + interface IService + { + IDependency Dep { get; } + } + + class Service: IService + { + public Service(IDependency dep) + { + Dep = dep; + Console.WriteLine("Created"); + } + + public IDependency Dep { get; } + } + + internal partial class Composition + { + partial void OnNewInstance(ref T value, object? tag, Lifetime lifetime) + { + Console.WriteLine($"{typeof(T)} '{tag}' {lifetime} created"); + } + + private partial T OnDependencyInjection(in T value, object? tag, Lifetime lifetime) + { + return value; + } + + private static partial void OnNewRoot(global::Pure.DI.IResolver resolver, string name, object? tag, global::Pure.DI.Lifetime lifetime) + { + Console.WriteLine($"New composition root \"{name}\" {typeof(TContract)} -> {typeof(T)} '{tag}' {lifetime}"); + } + } + + static class Setup + { + private static void SetupComposition() + { + // OnDependencyInjection = On + DI.Setup("Composition") + .Hint(Hint.OnNewInstance, "On") + .Hint(Hint.OnNewRoot, "On") + .Bind(123).To(ctx => new Dependency()) + .Bind(123).To(ctx => { + ctx.Inject(ctx.Tag, out var dependency); + return new Service(dependency); + }) + .Root("Service", 123); + } + } + + public class Program + { + public static void Main() + { + var composition = new Composition(); + var service = composition.Service; + } + } +} +""".RunAsync(new Options(LanguageVersion.CSharp9)); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(ImmutableArray.Create("New composition root \"Service\" Sample.IService -> Sample.Service '123' Transient", "Sample.Dependency '123' Transient created", "Created", "Sample.Service '123' Transient created"), result); + } + [Fact] public async Task ShouldTrackInstanceInjection() { diff --git a/tests/Pure.DI.UsageTests/Hints/TrackingDisposableInstancesPerRootScenario.cs b/tests/Pure.DI.UsageTests/Hints/TrackingDisposableInstancesPerRootScenario.cs index ed21312b0..0299011a0 100644 --- a/tests/Pure.DI.UsageTests/Hints/TrackingDisposableInstancesPerRootScenario.cs +++ b/tests/Pure.DI.UsageTests/Hints/TrackingDisposableInstancesPerRootScenario.cs @@ -8,10 +8,8 @@ // ReSharper disable UnusedMember.Local // ReSharper disable UnusedParameterInPartialMethod // ReSharper disable ArrangeTypeModifiers -// ReSharper disable InvertIf namespace Pure.DI.UsageTests.Hints.TrackingDisposableInstancesPerRootScenario; -using System.Collections.Concurrent; using Xunit; // { @@ -37,70 +35,62 @@ class Service(IDependency dependency) : IService public IDependency Dependency { get; } = dependency; } -internal static class Disposables +readonly struct Owned( + T value, + List disposables) + : IDisposable { - public static readonly IDisposable Empty = new EmptyDisposable(); - - public static IDisposable Combine(this Stack disposables) => - new CombinedDisposable(disposables); + public T Value { get; } = value; - private class EmptyDisposable : IDisposable + public void Dispose() { - public void Dispose() { } - } - - internal class CombinedDisposable(Stack disposables) - : IDisposable - { - public void Dispose() - { - while (disposables.TryPop(out var disposable)) - { - disposable.Dispose(); - } - } + disposables.Reverse(); + disposables.ForEach(i => i.Dispose()); } } partial class Composition { - private readonly ConcurrentDictionary> _disposables = new(); - + private List _disposables = []; + private void Setup() => DI.Setup(nameof(Composition)) - // Specifies to call a partial method - // named OnNewInstance when an instance is created + // Specifies to call the partial method OnNewInstance + // when an instance is created .Hint(Hint.OnNewInstance, "On") - // Specifies to call the partial method + // Specifies to call the partial method OnNewInstance // only for instances with lifetime // Transient, PerResolve and PerBlock .Hint( Hint.OnNewInstanceLifetimeRegularExpression, "Transient|PerResolve|PerBlock") + + // Specifies to call the partial method OnNewInstance + // for instances other than Composition.Owned + .Hint( + Hint.OnNewInstanceImplementationTypeNameRegularExpression, + "^((?!Owned<).)*$") + + .Bind().To(ctx => + { + ctx.Inject(ctx.Tag, out TT value); + var disposables = _disposables; + _disposables = []; + return new Owned(value, disposables); + }) .Bind().To() .Bind().To() - .Bind().To(_ => - _disposables.TryRemove(Environment.CurrentManagedThreadId, out var disposables) - ? disposables.Combine() - : Disposables.Empty) - - .Root<(IService service, IDisposable combinedDisposables)>("Root"); - + .Root>("Root"); + partial void OnNewInstance( ref T value, object? tag, Lifetime lifetime) { - if (value is IDisposable disposable && value is not Disposables.CombinedDisposable) - { - var disposables = _disposables.GetOrAdd( - Environment.CurrentManagedThreadId, - _ => new Stack()); - - disposables.Push(disposable); - } + if (value is not IDisposable disposable) return; + _disposables.Add(disposable); } } // } @@ -112,18 +102,25 @@ public void Run() { // { var composition = new Composition(); + var root1 = composition.Root; var root2 = composition.Root; - root2.combinedDisposables.Dispose(); + root2.Dispose(); // Checks that the disposable instances // associated with root1 have been disposed of - root2.service.Dependency.IsDisposed.ShouldBeTrue(); + root2.Value.Dependency.IsDisposed.ShouldBeTrue(); // Checks that the disposable instances // associated with root2 have not been disposed of - root1.service.Dependency.IsDisposed.ShouldBeFalse(); + root1.Value.Dependency.IsDisposed.ShouldBeFalse(); + + root1.Dispose(); + + // Checks that the disposable instances + // associated with root2 have been disposed of + root1.Value.Dependency.IsDisposed.ShouldBeTrue(); // } new Composition().SaveClassDiagram(); }