@@ -446,6 +446,153 @@ public static void Main()
446446 result . StdOut . ShouldBe ( [ "41" , "41" , "True" , "True" ] , result ) ;
447447 }
448448
449+ [ Fact ]
450+ public async Task ShouldShareSingletonAcrossNestedScopes ( )
451+ {
452+ // Given
453+
454+ // When
455+ var result = await """
456+ using System;
457+ using Pure.DI;
458+
459+ namespace Sample
460+ {
461+ interface ISingleton
462+ {
463+ Guid Id { get; }
464+ }
465+
466+ sealed class Singleton : ISingleton
467+ {
468+ public Guid Id { get; } = Guid.NewGuid();
469+ }
470+
471+ interface IService
472+ {
473+ ISingleton Singleton { get; }
474+ }
475+
476+ sealed class Service : IService
477+ {
478+ public Service(ISingleton singleton)
479+ {
480+ Singleton = singleton;
481+ }
482+
483+ public ISingleton Singleton { get; }
484+ }
485+
486+ partial class Composition
487+ {
488+ void Setup() => DI.Setup(nameof(Composition))
489+ .Hint(Hint.ScopeMethodName, "SetupScope")
490+ .Bind<ISingleton>().As(Lifetime.Singleton).To<Singleton>()
491+ .Bind<IService>().As(Lifetime.Scoped).To<Service>()
492+ .Root<IService>("Service");
493+ }
494+
495+ class Program
496+ {
497+ static void Main()
498+ {
499+ var composition = new Composition();
500+ var rootSingleton = composition.Service.Singleton;
501+ var scope1 = Composition.SetupScope(composition, new Composition());
502+ var scope1Singleton = scope1.Service.Singleton;
503+ var scope2 = Composition.SetupScope(scope1, new Composition());
504+ var scope2Singleton = scope2.Service.Singleton;
505+ Console.WriteLine(rootSingleton == scope1Singleton);
506+ Console.WriteLine(scope1Singleton == scope2Singleton);
507+ Console.WriteLine(rootSingleton == scope2Singleton);
508+ }
509+ }
510+ }
511+ """ . RunAsync ( new Options ( LanguageVersion : LanguageVersion . CSharp9 ) ) ;
512+
513+ // Then
514+ result . Success . ShouldBeTrue ( result ) ;
515+ result . Errors . Count . ShouldBe ( 0 , result ) ;
516+ result . Warnings . Count . ShouldBe ( 0 , result ) ;
517+ result . StdOut . ShouldBe ( [ "True" , "True" , "True" ] , result ) ;
518+ }
519+
520+ [ Fact ]
521+ public async Task ShouldKeepSingletonAvailableForNestedScopeAfterIntermediateScopeDispose ( )
522+ {
523+ // Given
524+
525+ // When
526+ var result = await """
527+ using System;
528+ using Pure.DI;
529+
530+ namespace Sample
531+ {
532+ interface ISingleton
533+ {
534+ Guid Id { get; }
535+ }
536+
537+ sealed class Singleton : ISingleton, IDisposable
538+ {
539+ public Guid Id { get; } = Guid.NewGuid();
540+
541+ public bool IsDisposed { get; private set; }
542+
543+ public void Dispose() => IsDisposed = true;
544+ }
545+
546+ interface IService
547+ {
548+ ISingleton Singleton { get; }
549+ }
550+
551+ sealed class Service : IService
552+ {
553+ public Service(ISingleton singleton)
554+ {
555+ Singleton = singleton;
556+ }
557+
558+ public ISingleton Singleton { get; }
559+ }
560+
561+ partial class Composition
562+ {
563+ void Setup() => DI.Setup(nameof(Composition))
564+ .Hint(Hint.ScopeMethodName, "SetupScope")
565+ .Bind<ISingleton>().As(Lifetime.Singleton).To<Singleton>()
566+ .Bind<IService>().As(Lifetime.Scoped).To<Service>()
567+ .Root<IService>("Service");
568+ }
569+
570+ class Program
571+ {
572+ static void Main()
573+ {
574+ var composition = new Composition();
575+ var scope1 = Composition.SetupScope(composition, new Composition());
576+ var scope1Singleton = (Singleton)scope1.Service.Singleton;
577+ var scope2 = Composition.SetupScope(scope1, new Composition());
578+ var scope2SingletonBeforeDispose = (Singleton)scope2.Service.Singleton;
579+ scope1.Dispose();
580+ var scope2SingletonAfterDispose = (Singleton)scope2.Service.Singleton;
581+ Console.WriteLine(scope1Singleton == scope2SingletonBeforeDispose);
582+ Console.WriteLine(scope2SingletonBeforeDispose == scope2SingletonAfterDispose);
583+ Console.WriteLine(scope2SingletonAfterDispose.IsDisposed);
584+ }
585+ }
586+ }
587+ """ . RunAsync ( new Options ( LanguageVersion : LanguageVersion . CSharp9 ) ) ;
588+
589+ // Then
590+ result . Success . ShouldBeTrue ( result ) ;
591+ result . Errors . Count . ShouldBe ( 0 , result ) ;
592+ result . Warnings . Count . ShouldBe ( 0 , result ) ;
593+ result . StdOut . ShouldBe ( [ "True" , "True" , "False" ] , result ) ;
594+ }
595+
449596 [ Fact ]
450597 public async Task ShouldSupportRootArgumentWithSimpleFactory ( )
451598 {
0 commit comments