diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 745ef83..69e06a6 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -371,16 +371,16 @@ public function referencedEntities() {
    */
   public function changed() {
     $referenced_entity_ids = array(
-      $this->entityType() => array($this->id() => TRUE),
+      $this->entityType() => array($this->id() => $this),
     );
 
     foreach ($this->referencedEntities() as $referenced_entity) {
-      $referenced_entity_ids[$referenced_entity->entityType()][$referenced_entity->id()] = TRUE;
+      $referenced_entity_ids[$referenced_entity->entityType()][$referenced_entity->id()] = $referenced_entity;
     }
 
-    foreach ($referenced_entity_ids as $entity_type => $entity_ids) {
+    foreach ($referenced_entity_ids as $entity_type => $entities) {
       if (\Drupal::entityManager()->hasController($entity_type, 'view_builder')) {
-        \Drupal::entityManager()->getViewBuilder($entity_type)->resetCache(array_keys($entity_ids));
+        \Drupal::entityManager()->getViewBuilder($entity_type)->resetCache($entities);
       }
     }
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 2680e6f..fabd5a5 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -264,11 +264,13 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
   /**
    * {@inheritdoc}
    */
-  public function resetCache(array $ids = NULL) {
-    if (isset($ids)) {
+  public function resetCache(array $entities = NULL) {
+    if (isset($entities)) {
       $tags = array();
-      foreach ($ids as $entity_id) {
-        $tags[$this->entityType][$entity_id] = $entity_id;
+      foreach ($entities as $entity) {
+        $id = $entity->id();
+        $tags[$this->entityType][$id] = $id;
+        $tags[$this->entityType . '_view_' . $entity->bundle()] = TRUE;
       }
       \Drupal::cache($this->cacheBin)->deleteTags($tags);
     }
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index 94fe045..fcb9364 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -79,10 +79,9 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
   /**
    * Resets the entity render cache.
    *
-   * @param array|null $ids
-   *   (optional) If specified, the cache is reset for the given entity IDs
-   *   only.
+   * @param array|null $entities
+   *   (optional) If specified, the cache is reset for the given entities  only.
    */
-  public function resetCache(array $ids = NULL);
+  public function resetCache(array $entities = NULL);
 
 }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index f0c38ad..816234c 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -411,6 +411,6 @@ public function save(array $form, array &$form_state) {
     // Clear the block and page caches so that anonymous users see the comment
     // they have posted.
     Cache::invalidateTags(array('content' => TRUE));
-    $this->entityManager->getViewBuilder($entity->entityType())->resetCache(array($entity->id()));
+    $this->entityManager->getViewBuilder($entity->entityType())->resetCache(array($entity));
   }
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
index f7cfd76..7742a64 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNonNodeTest.php
@@ -307,7 +307,7 @@ function testCommentFunctionality() {
     ));
     // We've changed role permissions, so need to reset render cache.
     // @todo Revisit after https://drupal.org/node/2099105
-    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($this->entity->id()));
+    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($this->entity));
     $this->drupalGet('entity_test/' . $this->entity->id());
     $this->assertPattern('@<h2[^>]*>Comments</h2>@', 'Comments were displayed.');
     $this->assertLink('Log in', 0, 'Link to log in was found.');
@@ -326,7 +326,7 @@ function testCommentFunctionality() {
     ));
     // We've changed role permissions, so need to reset render cache.
     // @todo Revisit after https://drupal.org/node/2099105
-    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($this->entity->id()));
+    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($this->entity));
     $this->drupalGet('entity_test/' . $this->entity->id());
     $this->assertNoPattern('@<h2[^>]*>Comments</h2>@', 'Comments were not displayed.');
     $this->assertFieldByName('subject', '', 'Subject field found.');
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php
index 8adf8ef..d08d45a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityViewControllerTest.php
@@ -101,7 +101,7 @@ public function testFieldItemAttributes() {
     ))->save();
     // Browse to the entity and verify that the attributes from both modules
     // are rendered in the field item HTML markup.
-    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($entity->id()));
+    \Drupal::entityManager()->getViewBuilder('entity_test')->resetCache(array($entity));
     $this->drupalGet('entity_test/' . $entity->id());
     $xpath = $this->xpath('//div[@data-field-item-attr="foobar" and @property="schema:text" and text()=:value]', array(':value' => $test_value));
     $this->assertTrue($xpath, 'The field item attributes from both modules have been found in the rendered output of the field.');
diff --git a/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php b/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
index eaf785e..b13f388 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/views/field/Link.php
@@ -61,7 +61,9 @@ public function query() {
    * {@inheritdoc}
    */
   public function render(ResultRow $values) {
-    return $this->renderLink($this->getEntity($values), $values);
+    if ($entity = $this->getEntity($values)) {
+      return $this->renderLink($entity, $values);
+    }
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php
index 86c9720..2fcd761 100644
--- a/core/modules/views/lib/Drupal/views/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Entity/View.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views\Entity;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\views\Views;
@@ -278,6 +279,8 @@ public function getExportProperties() {
   public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
     parent::postSave($storage_controller, $update);
 
+    $id = $this->id();
+    Cache::deleteTags(array('view' => array($id => $id)));
     views_invalidate_cache();
   }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
index 91c3ea9..e51b1e1 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
@@ -7,8 +7,8 @@
 
 namespace Drupal\views\Plugin\views\cache;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
-use Drupal\views\ViewExecutable;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\views\Plugin\views\PluginBase;
 use Drupal\Core\Database\Query\Select;
@@ -94,7 +94,8 @@ public function summaryTitle() {
    * @param $type
    *   The cache type, either 'query', 'result' or 'output'.
    */
-  protected function cacheExpire($type) { }
+  protected function cacheExpire($type) {
+  }
 
    /**
     * Determine expiration time in the cache table of the cache type
@@ -109,7 +110,6 @@ protected function cacheSetExpire($type) {
     return CacheBackendInterface::CACHE_PERMANENT;
   }
 
-
   /**
    * Save data to the cache.
    *
@@ -126,17 +126,16 @@ public function cacheSet($type) {
           'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0,
           'current_page' => $this->view->getCurrentPage(),
         );
-        cache($this->table)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type));
+        \Drupal::cache($this->table)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type), $this->getCacheTags());
         break;
       case 'output':
         $this->storage['output'] = $this->view->display_handler->output;
         $this->gatherHeaders();
-        cache($this->table)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type));
+        \Drupal::cache($this->table)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->getCacheTags());
         break;
     }
   }
 
-
   /**
    * Retrieve data from the cache.
    *
@@ -151,7 +150,7 @@ public function cacheGet($type) {
       case 'results':
         // Values to set: $view->result, $view->total_rows, $view->execute_time,
         // $view->current_page.
-        if ($cache = cache($this->table)->get($this->generateResultsKey())) {
+        if ($cache = \Drupal::cache($this->table)->get($this->generateResultsKey())) {
           if (!$cutoff || $cache->created > $cutoff) {
             $this->view->result = $cache->data['result'];
             $this->view->total_rows = $cache->data['total_rows'];
@@ -162,7 +161,7 @@ public function cacheGet($type) {
         }
         return FALSE;
       case 'output':
-        if ($cache = cache($this->table)->get($this->generateOutputKey())) {
+        if ($cache = \Drupal::cache($this->table)->get($this->generateOutputKey())) {
           if (!$cutoff || $cache->created > $cutoff) {
             $this->storage = $cache->data;
             $this->view->display_handler->output = $cache->data['output'];
@@ -181,7 +180,8 @@ public function cacheGet($type) {
    * to be sure that we catch everything. Maybe that's a bad idea.
    */
   public function cacheFlush() {
-    cache($this->table)->deleteTags(array($this->view->storage->id() => TRUE));
+    $id = $this->view->storage->id();
+    Cache::invalidateTags(array('view' => array($id => $id)));
   }
 
   /**
@@ -326,6 +326,50 @@ public function generateOutputKey() {
     return $this->outputKey;
   }
 
+  /**
+   * Gets an array of cache tags for the current view.
+   *
+   * @return array
+   *   An array fo cache tags based on the current view.
+   */
+  protected function getCacheTags() {
+    $tags = array();
+    $id = $this->view->storage->id();
+
+    $tags['view'][$id] = $id;
+
+    $entity_information = $this->view->query->getEntityTableInfo();
+
+    if (!empty($entity_information)) {
+      // Add an ENTITY_TYPE_view tag for each entity type used by this view.
+      foreach (array_keys($entity_information) as $type) {
+        $tags[$type . '_view'] = TRUE;
+      }
+
+      // Collect entity IDs if there are view results.
+      if (!empty($this->view->result)) {
+        foreach ($this->view->result as $result) {
+          $type = $result->_entity->entityType();
+
+          $tags[$type][] = $result->_entity->id();
+          $tags[$type . '_view_' . $result->_entity->bundle()] = TRUE;
+
+          foreach ($result->_relationship_entities as $entity) {
+            $type = $entity->entityType();
+
+            $tags[$type][] = $entity->id();
+            $tags[$type . '_view_' . $entity->bundle()] = TRUE;
+          }
+        }
+      }
+    }
+
+    // Filter out any duplicate values from generated tags.
+    return array_map(function($item) {
+      return is_array($item) ? array_unique($item) : $item;
+    }, $tags);
+  }
+
 }
 
 /**
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php
index f4c8285..11e5d0c 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/None.php
@@ -23,7 +23,8 @@
  */
 class None extends CachePluginBase {
 
-  public function cacheStart() { /* do nothing */ }
+  public function cacheStart() {
+  }
 
   public function summaryTitle() {
     return t('None');
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/Tag.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/Tag.php
new file mode 100644
index 0000000..4cad30b
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/Tag.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\cache\Tag.
+ */
+
+namespace Drupal\views\Plugin\views\cache;
+
+/**
+ * Simple caching of query results for Views displays.
+ *
+ * @ingroup views_cache_plugins
+ *
+ * @ViewsCache(
+ *   id = "tag",
+ *   title = @Translation("Tag based"),
+ *   help = @Translation("Tag based caching of data. Caches will persist until any related cache tags are invalidated.")
+ * )
+ */
+class Tag extends CachePluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summaryTitle() {
+    return t('Tag');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function cacheExpire($type) {
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index db08a61..f7e37b5 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1247,8 +1247,8 @@ public function query($get_count = FALSE) {
 
     // Make sure each entity table has the base field added so that the
     // entities can be loaded.
-    $entity_tables = $this->getEntityTables();
-    if ($entity_tables) {
+    $entity_information = $this->getEntityTableInfo();
+    if ($entity_information) {
       $params = array();
       if ($groupby) {
         // Handle grouping, by retrieving the minimum entity_id.
@@ -1257,10 +1257,10 @@ public function query($get_count = FALSE) {
         );
       }
 
-      foreach ($entity_tables as $table_alias => $table) {
-        $info = entity_get_info($table['entity_type']);
-        $base_field = empty($table['revision']) ? $info['entity_keys']['id'] : $info['entity_keys']['revision'];
-        $this->addField($table_alias, $base_field, '', $params);
+      foreach ($entity_information as $entity_type => $info) {
+        $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
+        $base_field = empty($table['revision']) ? $entity_info['entity_keys']['id'] : $entity_info['entity_keys']['revision'];
+        $this->addField($info['alias'], $base_field, '', $params);
       }
     }
 
@@ -1451,9 +1451,11 @@ function execute(ViewExecutable $view) {
    * Returns an array of all tables from the query that map to an entity type.
    *
    * Includes the base table and all relationships, if eligible.
+   *
    * Available keys for each table:
    * - base: The actual base table (i.e. "user" for an author relationship).
    * - relationship_id: The id of the relationship, or "none".
+   * - alias: The alias used for the relationship.
    * - entity_type: The entity type matching the base table.
    * - revision: A boolean that specifies whether the table is a base table or
    *   a revision table of the entity type.
@@ -1461,14 +1463,17 @@ function execute(ViewExecutable $view) {
    * @return array
    *   An array of table information, keyed by table alias.
    */
-  public function getEntityTables() {
+  public function getEntityTableInfo() {
     // Start with the base table.
     $entity_tables = array();
     $views_data = Views::viewsData();
-    $base_table_data = $views_data->get($this->view->storage->get('base_table'));
+    $base_table = $this->view->storage->get('base_table');
+    $base_table_data = $views_data->get($base_table);
+
     if (isset($base_table_data['table']['entity type'])) {
-      $entity_tables[$this->view->storage->get('base_table')] = array(
-        'base' => $this->view->storage->get('base_table'),
+      $entity_tables[$base_table_data['table']['entity type']] = array(
+        'base' => $base_table,
+        'alias' => $base_table,
         'relationship_id' => 'none',
         'entity_type' => $base_table_data['table']['entity type'],
         'revision' => FALSE,
@@ -1478,9 +1483,10 @@ public function getEntityTables() {
     foreach ($this->view->relationship as $relationship_id => $relationship) {
       $table_data = $views_data->get($relationship->definition['base']);
       if (isset($table_data['table']['entity type'])) {
-        $entity_tables[$relationship->alias] = array(
+        $entity_tables[$table_data['table']['entity type']] = array(
           'base' => $relationship->definition['base'],
           'relationship_id' => $relationship_id,
+          'alias' => $relationship->alias,
           'entity_type' => $table_data['table']['entity type'],
           'revision' => FALSE,
         );
@@ -1489,7 +1495,7 @@ public function getEntityTables() {
 
     // Determine which of the tables are revision tables.
     foreach ($entity_tables as $table_alias => $table) {
-      $info = entity_get_info($table['entity_type']);
+      $info = \Drupal::entityManager()->getDefinition($table['entity_type']);
       if (isset($info['revision table']) && $info['revision table'] == $table['base']) {
         $entity_tables[$table_alias]['revision'] = TRUE;
       }
@@ -1506,36 +1512,34 @@ public function getEntityTables() {
    * $result->_relationship_entities[$relationship_id];
    */
   function loadEntities(&$results) {
-    $entity_tables = $this->getEntityTables();
+    $entity_information = $this->getEntityTableInfo();
     // No entity tables found, nothing else to do here.
-    if (empty($entity_tables)) {
+    if (empty($entity_information)) {
       return;
     }
 
     // Assemble a list of entities to load.
-    $ids_by_table = array();
-    foreach ($entity_tables as $table_alias => $table) {
-      $entity_type = $table['entity_type'];
-      $info = entity_get_info($entity_type);
-      $id_key = empty($table['revision']) ? $info['entity_keys']['id'] : $info['entity_keys']['revision'];
-      $id_alias = $this->getFieldAlias($table_alias, $id_key);
+    $ids_by_type = array();
+    foreach ($entity_information as $entity_type => $info) {
+      $entity_info = \Drupal::entityManager()->getDefinition($entity_type);
+      $id_key = empty($table['revision']) ? $entity_info['entity_keys']['id'] : $entity_info['entity_keys']['revision'];
+      $id_alias = $this->getFieldAlias($info['alias'], $id_key);
 
       foreach ($results as $index => $result) {
         // Store the entity id if it was found.
         if (isset($result->{$id_alias}) && $result->{$id_alias} != '') {
-          $ids_by_table[$table_alias][$index] = $result->$id_alias;
+          $ids_by_type[$entity_type][$index] = $result->$id_alias;
         }
       }
     }
 
     // Load all entities and assign them to the correct result row.
-    foreach ($ids_by_table as $table_alias => $ids) {
-      $table = $entity_tables[$table_alias];
-      $entity_type = $table['entity_type'];
-      $relationship_id = $table['relationship_id'];
+    foreach ($ids_by_type as $entity_type => $ids) {
+      $info = $entity_information[$entity_type];
+      $relationship_id = $info['relationship_id'];
 
       // Drupal core currently has no way to load multiple revisions. Sad.
-      if ($table['revision']) {
+      if ($info['revision']) {
         $entities = array();
         foreach ($ids as $revision_id) {
           $entity = entity_revision_load($entity_type, $revision_id);
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTagTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTagTest.php
new file mode 100644
index 0000000..6e393d3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTagTest.php
@@ -0,0 +1,203 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\Plugin\CacheTagTest.
+ */
+
+namespace Drupal\views\Tests\Plugin;
+
+use Drupal\views\Views;
+
+/**
+ * Tests the Tag class.
+ *
+ * @see \Drupal\views\Plugin\views\cache\Tag
+ */
+class CacheTagTest extends PluginTestBase {
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $testViews = array('test_tag_cache');
+
+  /**
+   * Views used by this test.
+   *
+   * @var array
+   */
+  public static $modules = array('node');
+
+  /**
+   * The node storage controller.
+   *
+   * @var \Drupal\node\NodeStorageController
+   */
+  protected $nodeStorageController;
+
+  /**
+   * The node view builder.
+   *
+   * @var \Drupal\node\NodeViewBuilder
+   */
+  protected $nodeViewBuilder;
+
+  /**
+   * The user view builder.
+   *
+   * @var \Drupal\Core\Entity\EntityViewBuilder
+   */
+  protected $userViewBuilder;
+
+  /**
+   * An array of page nodes.
+   *
+   * @var \Drupal\node\Entity\Node[]
+   */
+  protected $pages;
+
+  /**
+   * An article node.
+   *
+   * @var \Drupal\node\Entity\Node
+   */
+  protected $article;
+
+  /**
+   * A test user.
+   *
+   * @var \Drupla\user\Entity\User
+   */
+  protected $user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Cache tag',
+      'description' => 'Tests tag cache plugin.',
+      'group' => 'Views Plugins'
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
+    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+
+    $this->nodeStorageController = $this->container->get('entity.manager')->getStorageController('node');
+    $this->nodeViewBuilder = $this->container->get('entity.manager')->getViewBuilder('node');
+    $this->userViewBuilder = $this->container->get('entity.manager')->getViewBuilder('user');
+
+    for ($i = 1; $i <= 5; $i++) {
+      $this->pages[] = $this->drupalCreateNode(array('title' => "Test $i", 'type' => 'page'));
+    }
+    $this->article = $this->drupalCreateNode(array('title' => "Test article", 'type' => 'article'));
+    $this->user = $this->drupalCreateUser();
+  }
+
+  /**
+   * Tests the tag cache plugin.
+   */
+  public function testTagCaching() {
+    $view = Views::getView('test_tag_cache');
+    $view->render();
+
+    // Saving the view should invalidate the tags.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $view->storage->save();
+
+    $this->assertFalse($cache_plugin->cacheGet('results'), 'Results cache empty after the view is saved.');
+    $this->assertFalse($cache_plugin->cacheGet('output'), 'Output cache empty after the view is saved.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test invalidating the nodes in this view invalidates the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $this->nodeViewBuilder->resetCache($this->pages);
+
+    $this->assertFalse($cache_plugin->cacheGet('results'), 'Results cache empty after resetCache is called with pages.');
+    $this->assertFalse($cache_plugin->cacheGet('output'), 'Output cache empty after resetCache is called with pages.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test saving a node in this view invalidates the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $node = reset($this->pages);
+    $node->save();
+
+    $this->assertFalse($cache_plugin->cacheGet('results'), 'Results cache empty after a page node is saved.');
+    $this->assertFalse($cache_plugin->cacheGet('output'), 'Output cache empty after a page node is saved.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test that invalidating a tag for a different node type, does not
+    // invalidate the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $this->nodeViewBuilder->resetCache(array($this->article));
+
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found after an article node is invalidated.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found after an article node is invalidated.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test that saving a node for a different node type, does not invalidate
+    // the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $this->article->save();
+
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found after an article node is saved.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found after an article node is saved.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test that invalidating a tag for a user, does not invalidate the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $this->userViewBuilder->resetCache(array($this->user));
+
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found after a user is invalidated.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found after a user is invalidated.');
+
+    $view->destroy();
+    $view->render();
+
+    // Test the cacheFlush method invalidates the cache.
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertTrue($cache_plugin->cacheGet('results'), 'Results cache found.');
+    $this->assertTrue($cache_plugin->cacheGet('output'), 'Output cache found.');
+
+    $cache_plugin->cacheFlush();
+
+    $cache_plugin = $view->display_handler->getPlugin('cache');
+    $this->assertFalse($cache_plugin->cacheGet('results'), 'Results cache empty after the cacheFlush() method is called.');
+    $this->assertFalse($cache_plugin->cacheGet('output'), 'Output cache empty after the cacheFlush() method is called.');
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
index 237a7ab..eca97db 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/CacheTest.php
@@ -42,7 +42,7 @@ protected function setUp() {
    *
    * @see views_plugin_cache_time
    */
-  function testTimeCaching() {
+  public function testTimeCaching() {
     // Create a basic result which just 2 results.
     $view = views_get_view('test_cache');
     $view->setDisplay();
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php
index 8e16535..c3e4fd1 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewExecutableTest.php
@@ -2,11 +2,12 @@
 
 /**
  * @file
- * Contains \Drupal\views\Tests\ViewExecutableUnitTest.
+ * Contains \Drupal\views\Tests\ViewExecutableTest.
  */
 
 namespace Drupal\views\Tests;
 
+use Drupal\views\Views;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewExecutableFactory;
 use Drupal\views\DisplayBag;
@@ -35,7 +36,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $testViews = array('test_destroy', 'test_executable_displays');
+  public static $testViews = array('test_destroy', 'test_executable_displays', 'test_view');
 
   /**
    * Properties that should be stored in the configuration.
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml
new file mode 100644
index 0000000..f81b53e
--- /dev/null
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_tag_cache.yml
@@ -0,0 +1,103 @@
+base_field: nid
+base_table: node
+core: 8
+description: ''
+status: true
+display:
+  default:
+    display_options:
+      access:
+        type: none
+      cache:
+        type: tag
+      exposed_form:
+        type: basic
+      fields:
+        nid:
+          alter:
+            alter_text: '0'
+            ellipsis: '1'
+            html: '0'
+            make_link: '0'
+            strip_tags: '0'
+            trim: '0'
+            word_boundary: '1'
+          empty_zero: '0'
+          field: nid
+          group_type: {  }
+          hide_empty: '0'
+          id: nid
+          table: node
+          plugin_id: numeric
+          provider: views
+        title:
+          id: title
+          table: node_field_data
+          field: title
+          label: Title
+          exclude: false
+          alter:
+            alter_text: false
+          element_class: ''
+          element_default_classes: true
+          empty: ''
+          hide_empty: false
+          empty_zero: false
+          hide_alter_empty: true
+          link_to_node: true
+          plugin_id: node
+          provider: node
+      filters:
+        type:
+          id: type
+          table: node_field_data
+          field: type
+          relationship: none
+          group_type: group
+          admin_label: ''
+          operator: in
+          value:
+            page: page
+          group: '1'
+          exposed: false
+          expose:
+            operator_id: false
+            label: ''
+            description: ''
+            use_operator: false
+            operator: ''
+            identifier: ''
+            required: false
+            remember: false
+            multiple: false
+            remember_roles:
+              authenticated: authenticated
+            reduce: false
+          is_grouped: false
+          group_info:
+            label: ''
+            description: ''
+            identifier: ''
+            optional: true
+            widget: select
+            multiple: false
+            remember: 0
+            default_group: All
+            default_group_multiple: {  }
+            group_items: {  }
+          plugin_id: bundle
+          provider: views
+      group_by: '1'
+      pager:
+        type: some
+      style:
+        type: default
+      row:
+        type: fields
+    display_plugin: default
+    display_title: Master
+    id: default
+    position: '0'
+label: ''
+id: test_tag_cache
+tag: ''
