From bd9bb2de6999029912dbae5778dc82121cc77d4f Mon Sep 17 00:00:00 2001 From: dadhi Date: Mon, 24 Apr 2023 22:47:48 +0200 Subject: [PATCH] added: custom ScopeName.Of and usage test for #565 --- src/DryIoc/Container.cs | 30 ++++++--- ...ur_when_also_set_to_openResolutionScope.cs | 65 +++++++++++++++++-- 2 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/DryIoc/Container.cs b/src/DryIoc/Container.cs index 96542ac6..4470fd2a 100644 --- a/src/DryIoc/Container.cs +++ b/src/DryIoc/Container.cs @@ -13661,6 +13661,21 @@ public interface IScopeName bool Match(object scopeName); } + /// Custom name matcher via the provided function. + /// It may be used as a negative check, e.g. to avoid cirtain scopes and proceed to search for the specific parent scope. + public sealed class ScopeName : IScopeName + { + /// Constucts the scope name matches based on the user-provided predicate + public static ScopeName Of(Func matchPredicate) => new ScopeName(matchPredicate); + + /// The match precicate + public readonly Func MatchPredicate; + private ScopeName(Func matchPredicate) => MatchPredicate = matchPredicate; + + /// + public bool Match(object scopeName) => MatchPredicate(scopeName); + } + /// Represents multiple names public sealed class CompositeScopeName : IScopeName { @@ -13714,16 +13729,11 @@ private ResolutionScopeName(Type serviceType, object serviceKey) } /// - public bool Match(object scopeName) - { - var name = scopeName as ResolutionScopeName; - return name != null && - (ServiceType == null || - name.ServiceType.IsAssignableTo(ServiceType) || - ServiceType.IsOpenGeneric() && - name.ServiceType.GetGenericDefinitionOrNull().IsAssignableTo(ServiceType)) && - (ServiceKey == null || ServiceKey.Equals(name.ServiceKey)); - } + public bool Match(object scopeName) => + scopeName is ResolutionScopeName name && + (ServiceType == null || name.ServiceType.IsAssignableTo(ServiceType) || + ServiceType.IsOpenGeneric() && name.ServiceType.GetGenericDefinitionOrNull().IsAssignableTo(ServiceType)) && + (ServiceKey == null || ServiceKey.Equals(name.ServiceKey)); /// String representation for easy debugging and understood error messages. public override string ToString() diff --git a/test/DryIoc.IssuesTests/GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope.cs b/test/DryIoc.IssuesTests/GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope.cs index a9767e4d..b2108289 100644 --- a/test/DryIoc.IssuesTests/GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope.cs +++ b/test/DryIoc.IssuesTests/GHIssue565_Is_ignoring_ReuseScoped_setting_expected_behaviour_when_also_set_to_openResolutionScope.cs @@ -10,8 +10,9 @@ public int Run() { TestScope_Zero(); TestScope_One(); + TestScope_One_WithScopeNameOf(); TestScope_Two(); - return 3; + return 4; } [Test] @@ -61,10 +62,10 @@ public void TestScope_Zero() }); } - public sealed class ToAnyScopeExceptResolutionScopeOf : IScopeName + public sealed class AnyScopeExceptResolutionScopeOf : IScopeName { - public static readonly IScopeName Instance = new ToAnyScopeExceptResolutionScopeOf(); - private ToAnyScopeExceptResolutionScopeOf() {} + public static readonly IScopeName Instance = new AnyScopeExceptResolutionScopeOf(); + private AnyScopeExceptResolutionScopeOf() {} public bool Match(object scopeName) => scopeName is not ResolutionScopeName rn || rn.ServiceType != typeof(T); } @@ -78,7 +79,61 @@ public void TestScope_One() /// even though it has opened a new scope internally. var container = new Container(); container.Register(setup: Setup.With(openResolutionScope: true), reuse: Reuse.Transient); - container.Register(setup: Setup.With(openResolutionScope: true), reuse: Reuse.ScopedTo(ToAnyScopeExceptResolutionScopeOf.Instance)); + container.Register(setup: Setup.With(openResolutionScope: true), reuse: Reuse.ScopedTo(AnyScopeExceptResolutionScopeOf.Instance)); + container.Register(Reuse.Singleton); + container.Resolve().GetId(); + var car = container.Resolve(); + // Get guid id for each class and guid store array + var iCarId = (nameof(ICar), car.GetId()); + var iBarId = (nameof(IBar), car.GetBarId()); + var iFooId = (nameof(IFoo), car.GetFooId()); + var IdArray = car.GetIdArray(); + Assert.Multiple(() => + { + // Assert that each name id pair matches with that stored + // in the first three elements of the array + Assert.That(IdArray[0], Is.EqualTo(iCarId)); + Assert.That(IdArray[1], Is.EqualTo(iBarId)); + Assert.That(IdArray[2], Is.EqualTo(iFooId)); + // Assert that each name id pair matches with each again. + // IBar and IFoo have been resolved from container in ICar class. + // IBar should be the same: Fail!! + Assert.That(IdArray[3], Is.EqualTo(iCarId)); + Assert.That(IdArray[4], Is.EqualTo(iBarId)); + Assert.That(IdArray[5], Is.EqualTo(iFooId)); + // New Scope is opened in ICar. Check if ICar and IFoo is the Same + // but IBar should be new ID. + Assert.That(IdArray[6], Is.EqualTo(iCarId)); + Assert.That(IdArray[7], !Is.EqualTo(iBarId)); + Assert.That(IdArray[8], Is.EqualTo(iFooId)); + // Assert that each name id pair matches with each again. + // IBar should still be new ID. but should resolve twice + // and be the same as the id from Array index 7 : Fail!! + Assert.That(IdArray[9], Is.EqualTo(iCarId)); + Assert.That(IdArray[10], !Is.EqualTo(iBarId)); + Assert.That(IdArray[10], Is.EqualTo(IdArray[7])); + Assert.That(IdArray[11], Is.EqualTo(iFooId)); + // Assert that each name id pair matches with each again. + // IBar Should match the original Ibar Id. Fail!! + Assert.That(IdArray[12], Is.EqualTo(iCarId)); + Assert.That(IdArray[13], Is.EqualTo(iBarId)); + Assert.That(IdArray[14], Is.EqualTo(iFooId)); + }); + } + + [Test] + public void TestScope_One_WithScopeNameOf() + { + /// This Test Fails when IBar is registered with openResolutionScope: true. + /// When iBar is Resolved it is resolved with a new instance each time. + /// My expectation is that is is still scoped to which ever scope it was resolved to + /// even though it has opened a new scope internally. + var container = new Container(); + container.Register(setup: Setup.With(openResolutionScope: true), reuse: Reuse.Transient); + + var anyScopeExceptBar = ScopeName.Of(n => n is not ResolutionScopeName rn || rn.ServiceType != typeof(IBar)); + container.Register(setup: Setup.With(openResolutionScope: true), reuse: Reuse.ScopedTo(anyScopeExceptBar)); + container.Register(Reuse.Singleton); container.Resolve().GetId(); var car = container.Resolve();