diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 91cfc34..03db2f5 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -161,6 +161,7 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       '#cache' => array(
         'tags' => Cache::mergeTags($this->getCacheTag(), $entity->getCacheTag()),
       ),
+      '#entity_type' => $this->entityTypeId,
     );
 
     // Cache the rendered output if permitted by the view mode and global entity
@@ -321,6 +322,30 @@ public function buildComponents(array &$build, array $entities, array $displays,
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getTemplateSuggestions(EntityInterface $entity, $view_mode = 'default') {
+    $entity_type_id = $entity->getEntityTypeId();
+    $bundle_name = $entity->bundle();
+    $entity_id = $entity->id();
+    $sanitized_view_mode = strtr($view_mode, '.', '_');
+
+    $suggestions = array();
+    $suggestions[] = $entity_type_id . '__' . $sanitized_view_mode;
+
+    // Add suggestions with bundle_name only if bundles exists on entity type.
+    if ($entity->getEntityType()->getKey('bundle')) {
+      $suggestions[] = $entity_type_id . '__' . $bundle_name;
+      $suggestions[] = $entity_type_id . '__' . $bundle_name . '__' . $sanitized_view_mode;
+    }
+
+    $suggestions[] = $entity_type_id . '__' . $entity_id;
+    $suggestions[] = $entity_type_id . '__' . $entity_id . '__' . $sanitized_view_mode;
+
+    return $suggestions;
+  }
+
+  /**
    * Specific per-entity building.
    *
    * @param array $build
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index eca5196..d1a7717 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -36,6 +36,19 @@
   public function buildComponents(array &$build, array $entities, array $displays, $view_mode, $langcode = NULL);
 
   /**
+   * Returns a list of template suggestions from an entity and the view mode.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to get the template suggestions.
+   * @param string $view_mode
+   *   The view mode to render the entity.
+   *
+   * @return array
+   *   The list of template suggestions.
+   */
+  public function getTemplateSuggestions(EntityInterface $entity, $view_mode = 'default');
+
+  /**
    * Returns the render array for the provided entity.
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index cbf56ab..4829f28 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -206,8 +206,9 @@ function block_rebuild() {
  */
 function block_theme_suggestions_block(array $variables) {
   $suggestions = array();
+  $provider = $variables['elements']['#configuration']['provider'];
 
-  $suggestions[] = 'block__' . $variables['elements']['#configuration']['provider'];
+  $suggestions[] = 'block__' . $provider;
   // Hyphens (-) and underscores (_) play a special role in theme suggestions.
   // Theme suggestions should only contain underscores, because within
   // drupal_find_theme_templates(), underscores are converted to hyphens to
@@ -217,16 +218,25 @@ function block_theme_suggestions_block(array $variables) {
   // and your function names won't be recognized. So, we need to convert
   // hyphens to underscores in block deltas for the theme suggestions.
 
-  // We can safely explode on : because we know the Block plugin type manager
-  // enforces that delimiter for all derivatives.
-  $parts = explode(':', $variables['elements']['#plugin_id']);
-  $suggestion = 'block';
-  while ($part = array_shift($parts)) {
-    $suggestions[] = $suggestion .= '__' . strtr($part, '-', '_');
+  if ($provider == 'block_content') {
+    $entity = $variables['elements']['content']['#block_content'];
+    $view_mode = $variables['elements']['#configuration']['block_content']['view_mode'];
+    $entity_suggestions = \Drupal::entityManager()->getViewBuilder('block_content')->getTemplateSuggestions($entity, $view_mode);
+
+    $suggestions = array_merge($suggestions, $entity_suggestions);
   }
+  else {
+    // We can safely explode on : because we know the Block plugin type manager
+    // enforces that delimiter for all derivatives.
+    $parts = explode(':', $variables['elements']['#plugin_id']);
+    $suggestion = 'block';
+    while ($part = array_shift($parts)) {
+      $suggestions[] = $suggestion .= '__' . strtr($part, '-', '_');
+    }
 
-  if (!empty($variables['elements']['#id'])) {
-    $suggestions[] = 'block__' . $variables['elements']['#id'];
+    if (!empty($variables['elements']['#id'])) {
+      $suggestions[] = 'block__' . $variables['elements']['#id'];
+    }
   }
 
   return $suggestions;
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index 32d75cf..7b00e4f 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -27,6 +27,12 @@ public function buildComponents(array &$build, array $entities, array $displays,
   /**
    * {@inheritdoc}
    */
+  public function getTemplateSuggestions(EntityInterface $entity, $view_mode = 'default') {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
     $build = $this->viewMultiple(array($entity), $view_mode, $langcode);
     return reset($build);
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 4e3bc46..2014449 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -554,23 +554,6 @@ function node_preprocess_block(&$variables) {
 }
 
 /**
- * Implements hook_theme_suggestions_HOOK().
- */
-function node_theme_suggestions_node(array $variables) {
-  $suggestions = array();
-  $node = $variables['elements']['#node'];
-  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
-
-  $suggestions[] = 'node__' . $sanitized_view_mode;
-  $suggestions[] = 'node__' . $node->bundle();
-  $suggestions[] = 'node__' . $node->bundle() . '__' . $sanitized_view_mode;
-  $suggestions[] = 'node__' . $node->id();
-  $suggestions[] = 'node__' . $node->id() . '__' . $sanitized_view_mode;
-
-  return $suggestions;
-}
-
-/**
  * Prepares variables for node templates.
  *
  * Default template: node.html.twig.
diff --git a/core/modules/node/src/Tests/NodeTemplateSuggestionsTest.php b/core/modules/node/src/Tests/NodeTemplateSuggestionsTest.php
index fef6aa0..9af0624 100644
--- a/core/modules/node/src/Tests/NodeTemplateSuggestionsTest.php
+++ b/core/modules/node/src/Tests/NodeTemplateSuggestionsTest.php
@@ -26,16 +26,19 @@ function testNodeThemeHookSuggestions() {
     $build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
 
     $variables['elements'] = $build;
-    $suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', array($variables));
+    $suggestions = array();
+    $hook = 'node';
+    \Drupal::moduleHandler()->alter('theme_suggestions', $suggestions, $variables, $hook);
 
     $this->assertEqual($suggestions, array('node__full', 'node__page', 'node__page__full', 'node__' . $node->id(), 'node__' . $node->id() . '__full'), 'Found expected node suggestions.');
 
     // Change the view mode.
     $view_mode = 'node.my_custom_view_mode';
     $build = \Drupal::entityManager()->getViewBuilder('node')->view($node, $view_mode);
-
     $variables['elements'] = $build;
-    $suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_node', array($variables));
+
+    $suggestions = array();
+    \Drupal::moduleHandler()->alter('theme_suggestions', $suggestions, $variables, $hook);
 
     $this->assertEqual($suggestions, array('node__node_my_custom_view_mode', 'node__page', 'node__page__node_my_custom_view_mode', 'node__' . $node->id(), 'node__' . $node->id() . '__node_my_custom_view_mode'), 'Found expected node suggestions.');
   }
diff --git a/core/modules/system/src/Tests/Entity/EntityTemplateSuggestionsTest.php b/core/modules/system/src/Tests/Entity/EntityTemplateSuggestionsTest.php
new file mode 100644
index 0000000..d11ec25
--- /dev/null
+++ b/core/modules/system/src/Tests/Entity/EntityTemplateSuggestionsTest.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Entity\EntityTemplateSuggestionsTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+/**
+ * Tests the entity template suggestions generation.
+ *
+ * @group Entity
+ */
+class EntityTemplateSuggestionsTest extends EntityUnitTestBase  {
+
+  /**
+   * @var string
+   */
+  protected $entity_name;
+
+  /**
+   * @var \Drupal\user\Entity\User
+   */
+  protected $entity_user;
+
+  protected function setUp() {
+    parent::setUp();
+  }
+
+  /**
+   * Creates a test entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   */
+  protected function createTestEntity($entity_type) {
+    $this->entity_name = $this->randomMachineName();
+    $this->entity_user = $this->createUser();
+
+    // Pass in the value of the name field when creating. With the user
+    // field we test setting a field after creation.
+    $entity = entity_create($entity_type);
+    $entity->user_id->target_id = $this->entity_user->id();
+    $entity->name->value = $this->entity_name;
+
+    return $entity;
+  }
+
+  /**
+   * Tests generation of template suggestions based on entity properties.
+   */
+  public function testReadWrite() {
+
+    // All entity variations have to have the same results.
+    foreach (entity_test_entity_types() as $entity_type) {
+      $this->assertTemplateSuggestions($entity_type);
+    }
+  }
+
+  /**
+   * Executes the template suggestions test set for a defined entity type.
+   *
+   * @param string $entity_type
+   *   The entity type to run the tests with.
+   */
+  protected function assertTemplateSuggestions($entity_type) {
+    $entity = $this->createTestEntity($entity_type);
+
+    $entity_definition = \Drupal::entityManager()->getDefinition($entity_type, FALSE);
+
+    if ($entity_definition->hasViewBuilderClass()) {
+      $view_modes = \Drupal::entityManager()->getFormModes($entity_type);
+
+      foreach ($view_modes as $id => $view_mode) {
+
+        if ($view_builder = \Drupal::entityManager()->getViewBuilder($entity_type)) {
+          $entity_id = rand(1, 20);
+          $sanitized_view_mode = strtr($id, '.', '_');
+          $bundle_name = $entity->bundle();
+          $entity->{$entity_definition->getKey('id')} = $entity_id;
+
+          $suggestions = $view_builder->getTemplateSuggestions($entity, $id);
+
+          $expected = array(
+            $entity_type . '__' . $sanitized_view_mode,
+            $entity_type . '__' . $bundle_name,
+            $entity_type . '__' . $bundle_name . '__' . $sanitized_view_mode,
+            $entity_type . '__' . $entity_id,
+            $entity_type . '__' . $entity_id . '__' . $sanitized_view_mode,
+          );
+
+          $this->assertEqual($expected, $suggestions, json_encode($suggestions));
+        }
+      }
+    }
+  }
+
+}
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index d02573b..1e751e1 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -309,6 +309,19 @@ function system_theme_suggestions_field(array $variables) {
   return $suggestions;
 }
 
+
+/**
+ * Implements hook_theme_suggestions_alter().
+ */
+function system_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
+  // If this is an entity being rendered, call the preprocess() method on the
+  // view builder.
+  if (isset($variables['elements']['#entity_type']) && isset($variables['elements']['#view_mode']) && $view_builder = \Drupal::entityManager()->getViewBuilder($variables['elements']['#entity_type'])) {
+    $entity_suggestions = $view_builder->getTemplateSuggestions($variables['elements']['#' . $variables['elements']['#entity_type']], $variables['elements']['#view_mode']);
+    $suggestions = array_merge($suggestions, $entity_suggestions);
+  }
+}
+
 /**
  * Implements hook_stream_wrappers().
  */
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index a715c0a..0483cb5 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -282,21 +282,6 @@ function taxonomy_term_view_multiple(array $terms, $view_mode = 'full', $langcod
 }
 
 /**
- * Implements hook_theme_suggestions_HOOK().
- */
-function taxonomy_theme_suggestions_taxonomy_term(array $variables) {
-  $suggestions = array();
-
-  /** @var \Drupal\taxonomy\TermInterface $term */
-  $term = $variables['elements']['#taxonomy_term'];
-
-  $suggestions[] = 'taxonomy_term__' . $term->bundle();
-  $suggestions[] = 'taxonomy_term__' . $term->id();
-
-  return $suggestions;
-}
-
-/**
  * Prepares variables for taxonomy term templates.
  *
  * Default template: taxonomy-term.html.twig.
