From e9d6c47458618323235d57f15f67a23b44600815 Mon Sep 17 00:00:00 2001 From: Yohan Bainier Date: Thu, 8 Jun 2017 15:00:24 +0200 Subject: [PATCH] Fix resolution through more than one nested container --- Hypodermic.Tests/NestedContainerTests.cpp | 36 ++++++++++++++ Hypodermic/IRegistrationScope.h | 3 +- Hypodermic/NestedRegistrationScope.h | 29 ++++++------ Hypodermic/RegistrationScope.h | 58 ++++++++++++++++++----- 4 files changed, 99 insertions(+), 27 deletions(-) diff --git a/Hypodermic.Tests/NestedContainerTests.cpp b/Hypodermic.Tests/NestedContainerTests.cpp index 0ebb961..7503bdb 100644 --- a/Hypodermic.Tests/NestedContainerTests.cpp +++ b/Hypodermic.Tests/NestedContainerTests.cpp @@ -54,6 +54,42 @@ namespace Testing BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber); } + BOOST_AUTO_TEST_CASE(should_resolve_dependency_in_nested_of_nested_container) + { + // Arrange + int expectedNumber1 = 42; + int expectedNumber2 = 43; + + ContainerBuilder builder; + builder.registerType< TopLevelConstructor >(); + + auto container = builder.build(); + + ContainerBuilder nestedContainerBuilder1; + nestedContainerBuilder1.registerInstance(std::make_shared< NestedDependency >(expectedNumber1)); + auto nestedContainer1 = nestedContainerBuilder1.buildNestedContainerFrom(*container); + + // Act + ContainerBuilder nestedContainerBuilder2; + nestedContainerBuilder2.registerInstance(std::make_shared< NestedDependency >(expectedNumber2)); + auto nestedContainer2 = nestedContainerBuilder2.buildNestedContainerFrom(*nestedContainer1); + + // Assert + auto instance = container->resolve< TopLevelConstructor >(); + BOOST_REQUIRE(instance != nullptr); + BOOST_CHECK(instance->dependency == nullptr); + + instance = nestedContainer1->resolve< TopLevelConstructor >(); + BOOST_REQUIRE(instance != nullptr); + BOOST_REQUIRE(instance->dependency != nullptr); + BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber1); + + instance = nestedContainer2->resolve< TopLevelConstructor >(); + BOOST_REQUIRE(instance != nullptr); + BOOST_REQUIRE(instance->dependency != nullptr); + BOOST_CHECK_EQUAL(instance->dependency->i, expectedNumber2); + } + BOOST_AUTO_TEST_CASE(should_keep_reference_on_top_level_registrations) { // Arrange diff --git a/Hypodermic/IRegistrationScope.h b/Hypodermic/IRegistrationScope.h index 39cfc29..bd19fd8 100644 --- a/Hypodermic/IRegistrationScope.h +++ b/Hypodermic/IRegistrationScope.h @@ -20,7 +20,8 @@ namespace Hypodermic virtual bool tryGetRegistrations(const TypeAliasKey& typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >& registrationContexts) const = 0; - virtual std::shared_ptr< IRegistrationScope > clone() = 0; + virtual void copyTo(IRegistrationScope& other) const = 0; + virtual void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) = 0; }; } // namespace Hypodermic \ No newline at end of file diff --git a/Hypodermic/NestedRegistrationScope.h b/Hypodermic/NestedRegistrationScope.h index 434977a..004aa18 100644 --- a/Hypodermic/NestedRegistrationScope.h +++ b/Hypodermic/NestedRegistrationScope.h @@ -1,9 +1,10 @@ #pragma once +#include +#include #include #include "Hypodermic/IRegistration.h" -#include "Hypodermic/IRegistrationScope.h" #include "Hypodermic/RegistrationScope.h" #include "Hypodermic/TypeAliasKey.h" @@ -11,15 +12,9 @@ namespace Hypodermic { - class NestedRegistrationScope : public IRegistrationScope + class NestedRegistrationScope : public IRegistrationScope, public std::enable_shared_from_this< NestedRegistrationScope > { - struct PrivateTag {}; - public: - explicit NestedRegistrationScope(PrivateTag) - { - } - explicit NestedRegistrationScope(const std::shared_ptr< IRegistrationScope >& parentScope) : m_parentScope(parentScope) { @@ -35,15 +30,16 @@ namespace Hypodermic return scope().tryGetRegistrations(typeAliasKey, registrationContexts); } - std::shared_ptr< IRegistrationScope > clone() override + void copyTo(IRegistrationScope& other) const override { std::lock_guard< decltype(m_mutex) > lock(m_mutex); - auto scopeClone = std::make_shared< NestedRegistrationScope >(PrivateTag()); - scopeClone->m_parentScope = m_parentScope; - scopeClone->m_scope = m_scope; + scope().copyTo(other); + } - return scopeClone; + void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) override + { + writeScope().addRegistrationContext(registrationContext); } private: @@ -62,14 +58,17 @@ namespace Hypodermic std::lock_guard< decltype(m_mutex) > lock(m_mutex); if (m_scope == nullptr) - m_scope = m_parentScope->clone(); + { + m_scope = std::make_shared< RegistrationScope >(); + m_parentScope->copyTo(*m_scope); + } return *m_scope; } private: std::shared_ptr< IRegistrationScope > m_parentScope; - std::shared_ptr< IRegistrationScope > m_scope; + std::shared_ptr< RegistrationScope > m_scope; mutable std::recursive_mutex m_mutex; }; diff --git a/Hypodermic/RegistrationScope.h b/Hypodermic/RegistrationScope.h index 901a716..bf99013 100644 --- a/Hypodermic/RegistrationScope.h +++ b/Hypodermic/RegistrationScope.h @@ -55,40 +55,76 @@ namespace Hypodermic return true; } - std::shared_ptr< IRegistrationScope > clone() override + void copyTo(IRegistrationScope& other) const override { std::lock_guard< decltype(m_mutex) > lock(m_mutex); - auto scopeClone = std::make_shared< RegistrationScope >(); - scopeClone->m_registrationContextsByBaseTypes = m_registrationContextsByBaseTypes; - scopeClone->m_fallbackRegistrationContextsByBaseTypes = m_fallbackRegistrationContextsByBaseTypes; + copyRegistrationContextsTo(other); + copyFallbackRegistrationContextsTo(other); + } - return scopeClone; + void addRegistrationContext(const std::shared_ptr< RegistrationContext >& registrationContext) override + { + if (registrationContext->registration()->typeAliases().empty()) + { + addRegistrationContext(createKeyForType(registrationContext->registration()->instanceType()), registrationContext); + return; + } + + for (auto&& x : registrationContext->registration()->typeAliases()) + addRegistrationContext(x.first, registrationContext); } private: void addRegistration(const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration) { - std::lock_guard< decltype(m_mutex) > lock(m_mutex); + addRegistrationContext(typeAliasKey, std::make_shared< RegistrationContext >(m_resolutionContainer, registration)); + } - if (registration->isFallback()) + void addRegistrationContext(const TypeAliasKey& typeAliasKey, const std::shared_ptr< RegistrationContext >& registrationContext) + { + if (registrationContext->registration()->isFallback()) { - addRegistration(m_fallbackRegistrationContextsByBaseTypes, typeAliasKey, registration); + addRegistrationContext(m_fallbackRegistrationContextsByBaseTypes, typeAliasKey, registrationContext); } else { - addRegistration(m_registrationContextsByBaseTypes, typeAliasKey, registration); + addRegistrationContext(m_registrationContextsByBaseTypes, typeAliasKey, registrationContext); } } template - void addRegistration(TRegistrationContextsByBaseTypes& container, const TypeAliasKey& typeAliasKey, const std::shared_ptr< IRegistration >& registration) + void addRegistrationContext(TRegistrationContextsByBaseTypes& container, const TypeAliasKey& typeAliasKey, const std::shared_ptr< RegistrationContext >& registrationContext) { + std::lock_guard< decltype(m_mutex) > lock(m_mutex); + auto it = container.find(typeAliasKey); if (it == std::end(container)) it = container.insert(std::make_pair(typeAliasKey, std::vector< std::shared_ptr< RegistrationContext > >())).first; - it->second.push_back(std::make_shared< RegistrationContext >(m_resolutionContainer, registration)); + it->second.push_back(registrationContext); + } + + void copyRegistrationContextsTo(IRegistrationScope& other) const + { + for (auto& x : m_registrationContextsByBaseTypes) + { + for (auto& registrationContext : x.second) + { + other.addRegistrationContext(registrationContext); + } + } + } + + void copyFallbackRegistrationContextsTo(IRegistrationScope& other) const + { + for (auto& x : m_fallbackRegistrationContextsByBaseTypes) + { + for (auto& registrationContext : x.second) + { + other.addRegistrationContext(registrationContext); + } + } } private: