diff --git a/includes/common.inc b/includes/common.inc
index 112b3bc5b03e6987bc859c9b129cdae762a99dc5..8d5f637d347bc8c1f65a83d7954d7dac668189cc 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -6484,6 +6484,46 @@ function entity_get_controller($entity_type) {
   return $controllers[$entity_type];
 }
 
+/**
+ * Invoke hook_entity_prepare_view().
+ *
+ * If adding a new entity similar to nodes, comments or users, you should
+ * invoke this function during the ENTITY_build_content() or
+ * ENTITY_build_multiple() phases of rendering to allow other modules to alter
+ * the objects during this phase. This is needed for situations where
+ * information needs to be loaded outside of ENTITY_load() - particularly
+ * when loading entities into one another - i.e. a user object into a node, due
+ * to the potential for unwanted side-effects such as caching and infinite
+ * recursion. By convention, entity_prepare_view() is called after
+ * field_attach_prepare_view() to allow entity level hooks to act on content
+ * loaded by field API.
+ * @see hook_entity_prepare_view()
+ *
+ * @param $entity_type
+ *   The type of entity, i.e. 'node', 'user'.
+ * @param $entities
+ *   The entity objects which are being prepared for view, keyed by object ID.
+ */
+function entity_prepare_view($entity_type, $entities) {
+  // To ensure hooks are only run once per entity, check for an
+  // entity_view_prepared flag and only process items without it.
+  // @todo: resolve this more generally for both entity and field level hooks.
+  $prepare = array();
+  foreach ($entities as $id => $entity) {
+    if (empty($entity->entity_view_prepared)) {
+      // Add this entity to the items to be prepared.
+      $prepare[$id] = $entity;
+
+      // Mark this item as prepared.
+      $entity->entity_view_prepared = TRUE;
+    }
+  }
+
+  if (!empty($prepare)) {
+    module_invoke_all('entity_prepare_view', $prepare, $entity_type);
+  }
+}
+
 /**
  * Performs one or more XML-RPC request(s).
  *
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index 1f24e44eb230ba64dfe20d079250ad42ab6cf008..3bbc3d9915b40e0ebbda8afdd1afdc261772c613 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -879,6 +879,7 @@ function comment_build_content($comment, $node, $view_mode = 'full') {
 
   // Build fields content.
   field_attach_prepare_view('comment', array($comment->cid => $comment), $view_mode);
+  entity_prepare_view('comment', array($comment->cid => $comment));
   $comment->content += field_attach_view('comment', $comment, $view_mode);
 
   // Prior to Drupal 7, the comment body was a simple text variable, but with
@@ -997,6 +998,7 @@ function comment_links($comment, $node) {
  */
 function comment_view_multiple($comments, $node, $view_mode = 'full', $weight = 0) {
   field_attach_prepare_view('comment', $comments, $view_mode);
+  entity_prepare_view('comment', $comments);
 
   $build = array(
     '#sorted' => TRUE,
diff --git a/modules/node/node.module b/modules/node/node.module
index b56199455b01786d32359a4df6ac4b8e71d341ab..32b107abe3132bd8face442f637f2af2e421ac30 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -1209,6 +1209,7 @@ function node_build_content($node, $view_mode = 'full') {
   // 'prepare_view' step. An internal flag prevents the operation from running
   // twice.
   field_attach_prepare_view('node', array($node->nid => $node), $view_mode);
+  entity_prepare_view('node', array($node->nid => $node));
   $node->content += field_attach_view('node', $node, $view_mode);
 
   // Always display a read more link on teasers because we have no way
@@ -2254,6 +2255,7 @@ function node_feed($nids = FALSE, $channel = array()) {
  */
 function node_view_multiple($nodes, $view_mode = 'teaser', $weight = 0) {
   field_attach_prepare_view('node', $nodes, $view_mode);
+  entity_prepare_view('node', $nodes);
   $build = array();
   foreach ($nodes as $node) {
     $build['nodes'][$node->nid] = node_view($node, $view_mode);
diff --git a/modules/rdf/rdf.module b/modules/rdf/rdf.module
index c25ae9323e26dff95aa66926d77bd7e167ddab33..31f3e2821322a25d4e39df020f66f14ed470305b 100644
--- a/modules/rdf/rdf.module
+++ b/modules/rdf/rdf.module
@@ -594,6 +594,25 @@ function rdf_field_attach_view_alter(&$output, $context) {
   }
 }
 
+/**
+ * Implements hook_entity_prepare_view().
+ */
+function rdf_entity_prepare_view($entities, $entity_type) {
+  $uids = array();
+  // In the case of both nodes and comments, the full $account object for the
+  // author is needed in rdf_preprocess_username(), however this is not
+  // available from node_load() or comment_load(). If the users are loaded
+  // for the first time in rdf_preprocess_username() this will issue an
+  // individual user_load() for each account, so pre-load the users needed
+  // here where we can take advantage of user_load_multiple().
+  if ($entity_type == 'node' || $entity_type == 'comment') {
+    foreach ($entities as $entity) {
+      $uids[$entity->uid] = $entity->uid;
+    }
+    user_load_multiple($uids);
+  }
+}
+
 /**
  * Wraps a template variable in an HTML element with the desired attributes.
  *
diff --git a/modules/system/system.api.php b/modules/system/system.api.php
index f78556c4ce23d55406839a56a3c73241cfbf3440..f8f20b8cca240f0f6b6e4ba9c99a5a254408c2e5 100644
--- a/modules/system/system.api.php
+++ b/modules/system/system.api.php
@@ -263,6 +263,28 @@ function hook_admin_paths_alter(&$paths) {
   $paths['node/add/forum'] = FALSE;
 }
 
+/**
+ * Act on entities as they are being prepared for view.
+ *
+ * Allows you to operate on multiple entities as they are being prepared for
+ * view. Only use this if attaching the data during the entity_load() phase
+ * is not appropriate, for example when attaching other 'entity' style objects.
+ *
+ * @param $entities
+ *   The entities keyed by entity ID.
+ * @param $type
+ *   The type of entities being loaded (i.e. node, user, comment).
+ */
+function hook_entity_prepare_view($entities, $type) {
+  // Load a specific node into the user object for later theming.
+  if ($type == 'user') {
+    $nodes = mymodule_get_user_nodes(array_keys($entities));
+    foreach ($entities as $uid => $entity) {
+      $entity->user_node = $nodes[$uid];
+    }
+  }
+}
+
 /**
  * Perform periodic actions.
  *
diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc
index e4987e52b9614a3efcab803244230bd7f7ab5c7b..712e8bc75932bae1f6817e7b07c20bf8f4e000b0 100644
--- a/modules/taxonomy/taxonomy.pages.inc
+++ b/modules/taxonomy/taxonomy.pages.inc
@@ -31,6 +31,7 @@ function taxonomy_term_page($term) {
   drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
 
   field_attach_prepare_view('taxonomy_term', array($term->tid => $term), 'full');
+  entity_prepare_view('taxonomy_term', array($term->tid => $term));
   $build = array();
   $build += field_attach_view('taxonomy_term', $term);
   $build['term_description'] = array(
diff --git a/modules/user/user.module b/modules/user/user.module
index 8db32ccd6f69b932ad1334f2587525fa36b3009a..313ff34a426e931eef93c3d244917da587064162 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -2158,6 +2158,7 @@ function user_build_content($account, $view_mode = 'full') {
 
   // Build fields content.
   field_attach_prepare_view('user', array($account->uid => $account), $view_mode);
+  entity_prepare_view('user', array($account->uid => $account));
   $account->content += field_attach_view('user', $account, $view_mode);
 
   // Populate $account->content with a render() array.