From ec48b65244191ebc293ecb6bddbc020989ff2a19 Mon Sep 17 00:00:00 2001 From: Steven Ryland Date: Fri, 20 Dec 2024 17:04:42 -0700 Subject: [PATCH 1/5] Adding IsRegisteredAtAnyLevel() to ObjectContainer --- Reqnroll/BoDi/IObjectContainer.cs | 20 +++++- Reqnroll/BoDi/ObjectContainer.cs | 20 ++++++ .../BoDi/IsRegisteredTests.cs | 70 +++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) diff --git a/Reqnroll/BoDi/IObjectContainer.cs b/Reqnroll/BoDi/IObjectContainer.cs index 578ea2cf1..f553f3a2e 100644 --- a/Reqnroll/BoDi/IObjectContainer.cs +++ b/Reqnroll/BoDi/IObjectContainer.cs @@ -109,7 +109,7 @@ public interface IObjectContainer : IDisposable IEnumerable ResolveAll() where T : class; /// - /// Determines whether the interface or type is registered optionally with the specified name. + /// Determines whether the interface or type is registered at the current level of the container hierarchy, optionally with the specified name. /// /// The interface or type. /// The name or null. @@ -117,10 +117,26 @@ public interface IObjectContainer : IDisposable bool IsRegistered(string name = null); /// - /// Determines whether the interface or type is registered with the specified name. + /// Determines whether the interface or type is registered at the current level of the container hierarchy, optionally with the specified name. /// /// The interface or type. /// The name. /// true if the interface or type is registered; otherwise false. bool IsRegistered(Type type, string name = null); + + /// + /// Determines whether the interface or type is registered at any level of the container hierarchy, optionally with the specified name. + /// + /// The interface or type. + /// The name or null. + /// true if the interface or type is registered; otherwise false. + bool IsRegisteredAtAnyLevel(string name = null); + + /// + /// Determines whether the interface or type is registered at any level of the container hierarchy, optionally with the specified name. + /// + /// The interface or type. + /// The name. + /// true if the interface or type is registered; otherwise false. + bool IsRegisteredAtAnyLevel(Type type, string name = null); } diff --git a/Reqnroll/BoDi/ObjectContainer.cs b/Reqnroll/BoDi/ObjectContainer.cs index 4b14d39b8..3d953db54 100644 --- a/Reqnroll/BoDi/ObjectContainer.cs +++ b/Reqnroll/BoDi/ObjectContainer.cs @@ -487,8 +487,10 @@ public IStrategyRegistration RegisterFactoryAs(Delegate factoryDelegate, Type in return factoryRegistration; } + /// public bool IsRegistered(string name = null) => IsRegistered(typeof(T), name); + /// public bool IsRegistered(Type type, string name = null) { var keyToResolve = new RegistrationKey(type, name); @@ -496,6 +498,24 @@ public bool IsRegistered(Type type, string name = null) return _registrations.ContainsKey(keyToResolve); } + /// + public bool IsRegisteredAtAnyLevel(string name = null) => IsRegisteredAtAnyLevel(typeof(T), name); + + /// + public bool IsRegisteredAtAnyLevel(Type type, string name = null) + { + IObjectContainer container = this; + do + { + if (container.IsRegistered(type, name)) + { + return true; + } + } while (container is ObjectContainer c && (container = c.BaseContainer) != null); + + return false; + } + // ReSharper disable once UnusedParameter.Local private void AssertNotResolved(RegistrationKey interfaceType) { diff --git a/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs b/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs index fab47c20e..9b60e5f5b 100644 --- a/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs +++ b/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs @@ -59,5 +59,75 @@ public void ShouldReturnTrueIfTypeRegistered() isRegistered.Should().BeTrue(); } + + [Fact] + public void AnyLevel_ShouldReturnTrueIfTypeRegisteredInParent() + { + // given + var parentContainer = new ObjectContainer(); + var container = new ObjectContainer(parentContainer); + + // when + parentContainer.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); + + // then + bool isRegistered = container.IsRegisteredAtAnyLevel(); + isRegistered.Should().BeTrue(); + + isRegistered = container.IsRegistered(); + isRegistered.Should().BeFalse(); + } + + [Fact] + public void AnyLevel_ShouldReturnTrueIfRegisteredInSelf() + { + // given + var parentContainer = new ObjectContainer(); + var container = new ObjectContainer(parentContainer); + + // when + container.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); + + // then + bool isRegistered = container.IsRegisteredAtAnyLevel(); + isRegistered.Should().BeTrue(); + + isRegistered = container.IsRegistered(); + isRegistered.Should().BeTrue(); + + isRegistered = parentContainer.IsRegisteredAtAnyLevel(); + isRegistered.Should().BeFalse(); + } + + [Fact] + public void AnyLevel_ShouldReturnTrueIfTypeRegisteredInGrandparent() + { + // given + var grandparentContainer = new ObjectContainer(); + var parentContainer = new ObjectContainer(grandparentContainer); + var container = new ObjectContainer(parentContainer); + + // when + grandparentContainer.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); + + // then + bool isRegistered = container.IsRegisteredAtAnyLevel(); + isRegistered.Should().BeTrue(); + + isRegistered = container.IsRegistered(); + isRegistered.Should().BeFalse(); + } + + [Fact] + public void AnyLevel_ShouldReturnFalseIfNotRegistered() + { + // given + var parentContainer = new ObjectContainer(); + var container = new ObjectContainer(parentContainer); + + // then + bool isRegistered = container.IsRegisteredAtAnyLevel(); + isRegistered.Should().BeFalse(); + } } } From 7f54aae5c218421e32c01bd5d6f55bde8f0d1152 Mon Sep 17 00:00:00 2001 From: Steven Ryland Date: Fri, 20 Dec 2024 17:10:02 -0700 Subject: [PATCH 2/5] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1408b2c41..871b04396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Enhance BoDi error handling to provide the name of the interface being registered when that interface has already been resolved (#324) * Improve code-behind feature file compilation speed (#336) * Improve parameter type naming for generic types (#343) +* Add an `IsRegisteredAtAnyLevel()` method to the `IObjectContainer` interface (#367) ## Bug fixes: From e937dc61e247a0e9e981212a0ffaaf272cedb77e Mon Sep 17 00:00:00 2001 From: Steven Ryland Date: Tue, 7 Jan 2025 15:11:56 -0700 Subject: [PATCH 3/5] Update registration method summaries in IObjectContainer --- Reqnroll/BoDi/IObjectContainer.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Reqnroll/BoDi/IObjectContainer.cs b/Reqnroll/BoDi/IObjectContainer.cs index f553f3a2e..be3bb506f 100644 --- a/Reqnroll/BoDi/IObjectContainer.cs +++ b/Reqnroll/BoDi/IObjectContainer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Reqnroll.BoDi; @@ -109,7 +109,7 @@ public interface IObjectContainer : IDisposable IEnumerable ResolveAll() where T : class; /// - /// Determines whether the interface or type is registered at the current level of the container hierarchy, optionally with the specified name. + /// Determines whether the interface or type is registered in the current container, optionally with the specified name. /// /// The interface or type. /// The name or null. @@ -117,7 +117,7 @@ public interface IObjectContainer : IDisposable bool IsRegistered(string name = null); /// - /// Determines whether the interface or type is registered at the current level of the container hierarchy, optionally with the specified name. + /// Determines whether the interface or type is registered in the current container, optionally with the specified name. /// /// The interface or type. /// The name. @@ -125,7 +125,7 @@ public interface IObjectContainer : IDisposable bool IsRegistered(Type type, string name = null); /// - /// Determines whether the interface or type is registered at any level of the container hierarchy, optionally with the specified name. + /// Determines whether the interface or type is registered in the current container or base container(s), optionally with the specified name. /// /// The interface or type. /// The name or null. @@ -133,7 +133,7 @@ public interface IObjectContainer : IDisposable bool IsRegisteredAtAnyLevel(string name = null); /// - /// Determines whether the interface or type is registered at any level of the container hierarchy, optionally with the specified name. + /// Determines whether the interface or type is registered in the current container or base container(s), optionally with the specified name. /// /// The interface or type. /// The name. From 7713d5b10b1acaa94e8d96fd355d9eaf0cfe6a55 Mon Sep 17 00:00:00 2001 From: Steven Ryland Date: Sat, 18 Jan 2025 13:00:04 -0700 Subject: [PATCH 4/5] PR feedback: Change behavior of IsRegistered() --- Reqnroll/BoDi/IObjectContainer.cs | 20 +----- Reqnroll/BoDi/ObjectContainer.cs | 25 +++----- .../BoDi/IsRegisteredTests.cs | 62 ++++++++++--------- 3 files changed, 44 insertions(+), 63 deletions(-) diff --git a/Reqnroll/BoDi/IObjectContainer.cs b/Reqnroll/BoDi/IObjectContainer.cs index be3bb506f..a9651afc0 100644 --- a/Reqnroll/BoDi/IObjectContainer.cs +++ b/Reqnroll/BoDi/IObjectContainer.cs @@ -109,7 +109,7 @@ public interface IObjectContainer : IDisposable IEnumerable ResolveAll() where T : class; /// - /// Determines whether the interface or type is registered in the current container, optionally with the specified name. + /// Determines whether the interface or type is registered in the container, optionally with the specified name. /// /// The interface or type. /// The name or null. @@ -117,26 +117,10 @@ public interface IObjectContainer : IDisposable bool IsRegistered(string name = null); /// - /// Determines whether the interface or type is registered in the current container, optionally with the specified name. + /// Determines whether the interface or type is registered in the container, optionally with the specified name. /// /// The interface or type. /// The name. /// true if the interface or type is registered; otherwise false. bool IsRegistered(Type type, string name = null); - - /// - /// Determines whether the interface or type is registered in the current container or base container(s), optionally with the specified name. - /// - /// The interface or type. - /// The name or null. - /// true if the interface or type is registered; otherwise false. - bool IsRegisteredAtAnyLevel(string name = null); - - /// - /// Determines whether the interface or type is registered in the current container or base container(s), optionally with the specified name. - /// - /// The interface or type. - /// The name. - /// true if the interface or type is registered; otherwise false. - bool IsRegisteredAtAnyLevel(Type type, string name = null); } diff --git a/Reqnroll/BoDi/ObjectContainer.cs b/Reqnroll/BoDi/ObjectContainer.cs index 3d953db54..2db9a25f0 100644 --- a/Reqnroll/BoDi/ObjectContainer.cs +++ b/Reqnroll/BoDi/ObjectContainer.cs @@ -495,24 +495,17 @@ public bool IsRegistered(Type type, string name = null) { var keyToResolve = new RegistrationKey(type, name); - return _registrations.ContainsKey(keyToResolve); - } - - /// - public bool IsRegisteredAtAnyLevel(string name = null) => IsRegisteredAtAnyLevel(typeof(T), name); - - /// - public bool IsRegisteredAtAnyLevel(Type type, string name = null) - { - IObjectContainer container = this; - do + if (_registrations.ContainsKey(keyToResolve)) { - if (container.IsRegistered(type, name)) - { - return true; - } - } while (container is ObjectContainer c && (container = c.BaseContainer) != null); + return true; + } + else if (BaseContainer != null) + { + // Recursively check the base container + return BaseContainer.IsRegistered(type, name); + } + // We are at the top of the container hierarchy and the registration is not found return false; } diff --git a/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs b/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs index 9b60e5f5b..8e83286c6 100644 --- a/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs +++ b/Tests/Reqnroll.RuntimeTests/BoDi/IsRegisteredTests.cs @@ -30,6 +30,18 @@ public void ShouldReturnFalseIfTypeNotRegistered() isRegistered.Should().BeFalse(); } + [Fact] + public void ShouldReturnFalseIfTypeNotRegisteredWithParent() + { + // given + var parentContainer = new ObjectContainer(); + var container = new ObjectContainer(parentContainer); + + // then + bool isRegistered = container.IsRegistered(); + isRegistered.Should().BeFalse(); + } + [Fact] public void ShouldReturnTrueIfInterfaceRegistered() { @@ -61,46 +73,29 @@ public void ShouldReturnTrueIfTypeRegistered() } [Fact] - public void AnyLevel_ShouldReturnTrueIfTypeRegisteredInParent() + public void ShouldReturnTrueIfTypeRegisteredInParent() { // given - var parentContainer = new ObjectContainer(); + var grandparentContainer = new ObjectContainer(); + var parentContainer = new ObjectContainer(grandparentContainer); var container = new ObjectContainer(parentContainer); // when parentContainer.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); // then - bool isRegistered = container.IsRegisteredAtAnyLevel(); - isRegistered.Should().BeTrue(); - - isRegistered = container.IsRegistered(); - isRegistered.Should().BeFalse(); - } - - [Fact] - public void AnyLevel_ShouldReturnTrueIfRegisteredInSelf() - { - // given - var parentContainer = new ObjectContainer(); - var container = new ObjectContainer(parentContainer); - - // when - container.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); - - // then - bool isRegistered = container.IsRegisteredAtAnyLevel(); + bool isRegistered = container.IsRegistered(); isRegistered.Should().BeTrue(); - isRegistered = container.IsRegistered(); + isRegistered = parentContainer.IsRegistered(); isRegistered.Should().BeTrue(); - isRegistered = parentContainer.IsRegisteredAtAnyLevel(); + isRegistered = grandparentContainer.IsRegistered(); isRegistered.Should().BeFalse(); } [Fact] - public void AnyLevel_ShouldReturnTrueIfTypeRegisteredInGrandparent() + public void ShouldReturnTrueIfTypeRegisteredInGrandparent() { // given var grandparentContainer = new ObjectContainer(); @@ -111,22 +106,31 @@ public void AnyLevel_ShouldReturnTrueIfTypeRegisteredInGrandparent() grandparentContainer.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); // then - bool isRegistered = container.IsRegisteredAtAnyLevel(); + bool isRegistered = container.IsRegistered(); isRegistered.Should().BeTrue(); - isRegistered = container.IsRegistered(); - isRegistered.Should().BeFalse(); + isRegistered = parentContainer.IsRegistered(); + isRegistered.Should().BeTrue(); + + isRegistered = grandparentContainer.IsRegistered(); + isRegistered.Should().BeTrue(); } [Fact] - public void AnyLevel_ShouldReturnFalseIfNotRegistered() + public void ShouldReturnTrueIfRegisteredInSelfButFalseInParent() { // given var parentContainer = new ObjectContainer(); var container = new ObjectContainer(parentContainer); + // when + container.RegisterInstanceAs(new SimpleClassWithDefaultCtor()); + // then - bool isRegistered = container.IsRegisteredAtAnyLevel(); + bool isRegistered = container.IsRegistered(); + isRegistered.Should().BeTrue(); + + isRegistered = parentContainer.IsRegistered(); isRegistered.Should().BeFalse(); } } From f2b2d0e337f2c892989dac4a33e6ba8619d9ffad Mon Sep 17 00:00:00 2001 From: Steven Ryland Date: Sat, 18 Jan 2025 15:01:22 -0700 Subject: [PATCH 5/5] Update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e00ed77..bfa272675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Improve code-behind feature file compilation speed (#336) * Improve parameter type naming for generic types (#343) * Reduced MsBuild log output and consistent use of [Reqnroll] prefix (#381) -* Add an `IsRegisteredAtAnyLevel()` method to the `IObjectContainer` interface (#367) +* Update behavior of `ObjectContainer.IsRegistered()` to check base container for registrations, to match `Resolve()` behavior (#367) ## Bug fixes: * MsTest: Only use TestContext for output and not Console.WriteLine (#368)