diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
index 4664edcab3c1297089865ec2d80b8d605a269fba..de0fb88b0efc2f0d017b70a95be5a06179f819ef 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerFlatFile.php
@@ -68,7 +68,7 @@ class MetaDataStorageHandlerFlatFile extends MetaDataStorageSource
      *
      * @return array|null An associative array with the metadata, or null if we are unable to load metadata from the given
      *     file.
-     * @throws Exception If the metadata set cannot be loaded.
+     * @throws \Exception If the metadata set cannot be loaded.
      */
     private function load($set)
     {
@@ -113,38 +113,10 @@ class MetaDataStorageHandlerFlatFile extends MetaDataStorageSource
 
         // add the entity id of an entry to each entry in the metadata
         foreach ($metadataSet as $entityId => &$entry) {
-            if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
-                $entry['entityid'] = $this->generateDynamicHostedEntityID($set);
-            } else {
-                $entry['entityid'] = $entityId;
-            }
+            $entry = $this->updateEntityID($set, $entityId, $entry);
         }
 
         $this->cachedMetadata[$set] = $metadataSet;
         return $metadataSet;
     }
-
-
-    /**
-     * @param string $set
-     * @throws \Exception
-     * @return string
-     */
-    private function generateDynamicHostedEntityID($set)
-    {
-        // get the configuration
-        $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
-
-        if ($set === 'saml20-idp-hosted') {
-            return $baseurl.'saml2/idp/metadata.php';
-        } elseif ($set === 'shib13-idp-hosted') {
-            return $baseurl.'shib13/idp/metadata.php';
-        } elseif ($set === 'wsfed-sp-hosted') {
-            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost();
-        } elseif ($set === 'adfs-idp-hosted') {
-            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost().':idp';
-        } else {
-            throw new \Exception('Can not generate dynamic EntityID for metadata of this type: ['.$set.']');
-        }
-    }
 }
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
index f33f8757089d104189474dd55130e674f1457912..4c26cec764fb36ee4b6ccf12f52b8fd961b2483e 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandlerPdo.php
@@ -75,7 +75,7 @@ class MetaDataStorageHandlerPdo extends MetaDataStorageSource
      * @return array|null $metadata Associative array with the metadata, or NULL if we are unable to load
      *     metadata from the given file.
      *
-     * @throws Exception If a database error occurs.
+     * @throws \Exception If a database error occurs.
      * @throws \SimpleSAML\Error\Exception If the metadata can be retrieved from the database, but cannot be decoded.
      */
     private function load($set)
@@ -129,14 +129,10 @@ class MetaDataStorageHandlerPdo extends MetaDataStorageSource
         if ($metadataSet === null) {
             $metadataSet = [];
         }
-        /** @var array $metadataSet */
 
+        /** @var array $metadataSet */
         foreach ($metadataSet as $entityId => &$entry) {
-            if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
-                $entry['entityid'] = $this->generateDynamicHostedEntityID($set);
-            } else {
-                $entry['entityid'] = $entityId;
-            }
+            $entry = $this->updateEntityID($set, $entityId, $entry);
         }
 
         $this->cachedMetadata[$set] = $metadataSet;
@@ -157,68 +153,61 @@ class MetaDataStorageHandlerPdo extends MetaDataStorageSource
         assert(is_string($entityId));
         assert(is_string($set));
 
-        $tableName = $this->getTableName($set);
-
+        // validate the metadata set is valid
         if (!in_array($set, $this->supportedSets, true)) {
             return null;
         }
 
-        $stmt = $this->db->read(
-            "SELECT entity_id, entity_data FROM $tableName WHERE entity_id=:entityId",
-            ['entityId' => $entityId]
-        );
-        if ($stmt->execute()) {
-            $rowCount = 0;
-            $data = null;
+        // support caching
+        if (isset($this->cachedMetadata[$entityId][$set])) {
+            return $this->cachedMetadata[$entityId][$set];
+        }
 
-            while ($d = $stmt->fetch()) {
-                if (++$rowCount > 1) {
-                    \SimpleSAML\Logger::warning("Duplicate match for $entityId in set $set");
-                    break;
-                }
-                $data = json_decode($d['entity_data'], true);
-                if ($data === null) {
-                    throw new \SimpleSAML\Error\Exception("Cannot decode metadata for entity '${d['entity_id']}'");
-                }
-                if (!array_key_exists('entityid', $data)) {
-                    $data['entityid'] = $d['entity_id'];
-                }
-            }
-            return $data;
-        } else {
+        $tableName = $this->getTableName($set);
+
+        // according to the docs, it looks like *-idp-hosted metadata are the types
+        // that allow the __DYNAMIC:*__ entity id.  with the current table design
+        // we need to lookup the specific metadata entry but also we need to lookup
+        // any dynamic entries to see if the dynamic hosted entity id matches
+        if (substr($set, -10) == 'idp-hosted') {
+            $stmt = $this->db->read(
+                "SELECT entity_id, entity_data FROM {$tableName} WHERE (entity_id LIKE :dynamicId OR entity_id = :entityId)",
+                ['dynamicId' => '__DYNAMIC%', 'entityId' => $entityId]
+            );
+        }
+        // other metadata types should be able to match on entity id
+        else {
+            $stmt = $this->db->read(
+                "SELECT entity_id, entity_data FROM {$tableName} WHERE entity_id = :entityId",
+                ['entityId' => $entityId]
+            );
+        }
+
+        // throw pdo exception upon execution failure
+        if (!$stmt->execute()) {
             throw new \Exception('PDO metadata handler: Database error: '.var_export($this->db->getLastError(), true));
         }
-    }
 
-    /**
-     * @param string $set
-     * @throws \Exception
-     * @return string
-     */
-    private function generateDynamicHostedEntityID($set)
-    {
-        assert(is_string($set));
+        // load the metadata into an array
+        $metadataSet = [];
+        while ($d = $stmt->fetch()) {
+            $data = json_decode($d['entity_data'], true);
+            if (json_last_error() != JSON_ERROR_NONE) {
+                throw new \SimpleSAML\Error\Exception("Cannot decode metadata for entity '${d['entity_id']}'");
+            }
 
-        // get the configuration
-        $baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
-
-        if ($set === 'saml20-idp-hosted') {
-            return $baseurl.'saml2/idp/metadata.php';
-        } elseif ($set === 'saml20-sp-hosted') {
-            return $baseurl.'saml2/sp/metadata.php';
-        } elseif ($set === 'shib13-idp-hosted') {
-            return $baseurl.'shib13/idp/metadata.php';
-        } elseif ($set === 'shib13-sp-hosted') {
-            return $baseurl.'shib13/sp/metadata.php';
-        } elseif ($set === 'wsfed-sp-hosted') {
-            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost();
-        } elseif ($set === 'adfs-idp-hosted') {
-            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost().':idp';
-        } else {
-            throw new \Exception('Can not generate dynamic EntityID for metadata of this type: ['.$set.']');
+            // update the entity id to either the key (if not dynamic or generate the dynamic hosted url)
+            $metadataSet[$d['entity_id']] = $this->updateEntityID($set, $entityId, $data);
+        }
+
+        $indexLookup = $this->lookupIndexFromEntityId($entityId, $metadataSet);
+        if (isset($indexLookup) && array_key_exists($indexLookup, $metadataSet)) {
+            $this->cachedMetadata[$indexLookup][$set] = $metadataSet[$indexLookup];
+            return $this->cachedMetadata[$indexLookup][$set];
         }
-    }
 
+        return null;
+    }
 
     /**
      * Add metadata to the configured database
diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
index ceb10ade551584d873be311773fb0950096b8b07..e6cdb5d15942f769897293e5cb66255e89157e21 100644
--- a/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
+++ b/lib/SimpleSAML/Metadata/MetaDataStorageSource.php
@@ -213,26 +213,59 @@ abstract class MetaDataStorageSource
 
 
     /**
-     * @param string $entityId
-     * @param string $set
-     * @return mixed|null
+     * This function retrieves metadata for the given entity id in the given set of metadata.
+     * It will return NULL if it is unable to locate the metadata.
+     *
+     * This class implements this function using the getMetadataSet-function. A subclass should
+     * override this function if it doesn't implement the getMetadataSet function, or if the
+     * implementation of getMetadataSet is slow.
+     *
+     * @param string $index The entityId or metaindex we are looking up.
+     * @param string $set The set we are looking for metadata in.
+     *
+     * @return array|null An associative array with metadata for the given entity, or NULL if we are unable to
+     *         locate the entity.
      */
-    private function lookupIndexFromEntityId($entityId, $set)
+    public function getMetaData($index, $set)
     {
-        assert(is_string($entityId));
+
+        assert(is_string($index));
         assert(isset($set));
 
         $metadataSet = $this->getMetadataSet($set);
 
+        $indexLookup = $this->lookupIndexFromEntityId($index, $metadataSet);
+        if (isset($indexLookup) && array_key_exists($indexLookup, $metadataSet)) {
+            return $metadataSet[$indexLookup];
+        }
+
+        return null;
+    }
+
+    /**
+     * This method returns the full metadata set for a given entity id or null if the entity id cannot be found
+     * in the given metadata set.
+     *
+     * @param string $entityId
+     * @param array $metadataSet the already loaded metadata set
+     * @return mixed|null
+     */
+    protected function lookupIndexFromEntityId($entityId, array $metadataSet)
+    {
+        assert(is_string($entityId));
+        assert(is_array($metadataSet));
+
         // check for hostname
-        $currenthost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
+        $currentHost = \SimpleSAML\Utils\HTTP::getSelfHost(); // sp.example.org
 
         foreach ($metadataSet as $index => $entry) {
+            // explicit index match
             if ($index === $entityId) {
                 return $index;
             }
+
             if ($entry['entityid'] === $entityId) {
-                if ($entry['host'] === '__DEFAULT__' || $entry['host'] === $currenthost) {
+                if ($entry['host'] === '__DEFAULT__' || $entry['host'] === $currentHost) {
                     return $index;
                 }
             }
@@ -241,38 +274,70 @@ abstract class MetaDataStorageSource
         return null;
     }
 
+    /**
+     * @param string $set
+     * @throws \Exception
+     * @return string
+     */
+    private function getDynamicHostedUrl($set)
+    {
+        assert(is_string($set));
+
+        // get the configuration
+        $baseUrl = \SimpleSAML\Utils\HTTP::getBaseURL();
+
+        if ($set === 'saml20-idp-hosted') {
+            return $baseUrl.'saml2/idp/metadata.php';
+        }
+        else if ($set === 'saml20-sp-hosted') {
+            return $baseUrl.'saml2/sp/metadata.php';
+        }
+        else if ($set === 'shib13-idp-hosted') {
+            return $baseUrl.'shib13/idp/metadata.php';
+        }
+        else if ($set === 'shib13-sp-hosted') {
+            return $baseUrl.'shib13/sp/metadata.php';
+        }
+        else if ($set === 'wsfed-sp-hosted') {
+            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost();
+        }
+        else if ($set === 'adfs-idp-hosted') {
+            return 'urn:federation:'.\SimpleSAML\Utils\HTTP::getSelfHost().':idp';
+        }
+        else {
+            throw new \Exception('Can not generate dynamic EntityID for metadata of this type: ['.$set.']');
+        }
+    }
 
     /**
-     * This function retrieves metadata for the given entity id in the given set of metadata.
-     * It will return NULL if it is unable to locate the metadata.
+     * Updates the metadata entry's entity id and returns the modified array.  If the entity id is __DYNAMIC:*__ a
+     * the current url is assigned.  If it is explicit the entityid array key is updated to the entityId that was
+     * provided.
      *
-     * This class implements this function using the getMetadataSet-function. A subclass should
-     * override this function if it doesn't implement the getMetadataSet function, or if the
-     * implementation of getMetadataSet is slow.
-     *
-     * @param string $index The entityId or metaindex we are looking up.
-     * @param string $set The set we are looking for metadata in.
+     * @param string $metadataSet a metadata set (saml20-idp-hosted, saml20-sp-remote, etc)
+     * @param string $entityId the entity id we are modifying
+     * @param array $metadataEntry the fully populated metadata entry
+     * @return array modified metadata to include the valid entityid
      *
-     * @return array|null An associative array with metadata for the given entity, or NULL if we are unable to
-     *         locate the entity.
+     * @throws \Exception
      */
-    public function getMetaData($index, $set)
+    protected function updateEntityID($metadataSet, $entityId, array $metadataEntry)
     {
+        assert(is_string($metadataSet));
+        assert(is_string($entityId));
+        assert(is_array($metadataEntry));
 
-        assert(is_string($index));
-        assert(isset($set));
-
-        $metadataSet = $this->getMetadataSet($set);
+        $modifiedMetadataEntry = $metadataEntry;
 
-        if (array_key_exists($index, $metadataSet)) {
-            return $metadataSet[$index];
+        // generate a dynamic hosted url
+        if (preg_match('/__DYNAMIC(:[0-9]+)?__/', $entityId)) {
+            $modifiedMetadataEntry['entityid'] = $this->getDynamicHostedUrl($metadataSet);
         }
-
-        $indexlookup = $this->lookupIndexFromEntityId($index, $set);
-        if (isset($indexlookup) && array_key_exists($indexlookup, $metadataSet)) {
-            return $metadataSet[$indexlookup];
+        // set the entityid metadata array key to the provided entity id
+        else {
+            $modifiedMetadataEntry['entityid'] = $entityId;
         }
 
-        return null;
+        return $modifiedMetadataEntry;
     }
-}
+}
\ No newline at end of file