diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index 1bef225..ec90b7e 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Config\Entity;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\Entity;
 use Drupal\Core\Config\ConfigDuplicateUUIDException;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -163,6 +164,8 @@ public function enable() {
    * {@inheritdoc}
    */
   public function disable() {
+    // An entity was disabled, invalidate its own cache tag.
+    Cache::invalidateTags(array($this->entityTypeId => array($this->id())));
     return $this->setStatus(FALSE);
   }
 
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2300f60..ed95b23 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -7,7 +7,9 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\DependencyInjection\DependencySerialization;
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\Entity\Exception\ConfigEntityIdLengthException;
@@ -357,9 +359,7 @@ public function preSave(EntityStorageInterface $storage) {
    */
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     $this->onSaveOrDelete();
-    if ($update) {
-      $this->onUpdateBundleEntity();
-    }
+    $this->invalidateTagsOnSave($update);
   }
 
   /**
@@ -384,9 +384,7 @@ public static function preDelete(EntityStorageInterface $storage, array $entitie
    * {@inheritdoc}
    */
   public static function postDelete(EntityStorageInterface $storage, array $entities) {
-    foreach ($entities as $entity) {
-      $entity->onSaveOrDelete();
-    }
+    self::invalidateTagsOnDelete($entities);
   }
 
   /**
@@ -403,6 +401,21 @@ public function referencedEntities() {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getCacheTag() {
+    return array($this->entityTypeId => array($this->id()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getListCacheTags() {
+    // @todo Add bundle-specific listing cache tag? https://drupal.org/node/2145751
+    return array($this->entityTypeId . 's' => TRUE);
+  }
+
+  /**
    * Acts on an entity after it was saved or deleted.
    */
   protected function onSaveOrDelete() {
@@ -422,6 +435,46 @@ protected function onSaveOrDelete() {
   }
 
   /**
+   * Invalidates an entity's cache tags upon save.
+   *
+   * @param bool $update
+   *   TRUE if the entity has been updated, or FALSE if it has been inserted.
+   */
+  protected function invalidateTagsOnSave($update) {
+    // An entity was created or updated: invalidate its list cache tags. (An
+    // updated entity may start to appear in a listing because it now meets that
+    // listing's filtering requirements. A newly created entity may start to
+    // appear in listings because it did not exist before.)
+    $tags = $this->getListCacheTags();
+    if ($update) {
+      // An existing entity was updated, also invalidate its unique cache tag.
+      $tags = NestedArray::mergeDeep($tags, $this->getCacheTag());
+      $this->onUpdateBundleEntity();
+    }
+    Cache::invalidateTags($tags);
+  }
+
+  /**
+   * Invalidates an entity's cache tags upon delete.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface[] $entities
+   *   An array of entities.
+   */
+  protected static function invalidateTagsOnDelete(array $entities) {
+    $tags = array();
+    foreach ($entities as $entity) {
+      // An entity was deleted: invalidate its own cache tag, but also its list
+      // cache tags. (A deleted entity may cause changes in a paged list on
+      // other pages than the one it's on. The one it's on is handled by its own
+      // cache tag, but subsequent list pages would not be invalidated, hence we
+      // must invalidate its list cache tags as well.)
+      $tags = NestedArray::mergeDeepArray(array($tags, $entity->getCacheTag(), $entity->getListCacheTags()));
+      $entity->onSaveOrDelete();
+    }
+    Cache::invalidateTags($tags);
+  }
+
+  /**
    * Acts on entities of which this entity is a bundle entity type.
    */
   protected function onUpdateBundleEntity() {
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 90e4699..ab62144 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -324,4 +324,23 @@ public function setOriginalId($id);
    */
   public function toArray();
 
+  /**
+   * The unique cache tag associated with this entity.
+   *
+   * @return array
+   *   An array of cache tags.
+   */
+  public function getCacheTag();
+
+  /**
+   * The list cache tags associated with this entity.
+   *
+   * Enables code listing entities of this type to ensure that newly created
+   * entities show up immediately.
+   *
+   * @return array
+   *   An array of cache tags.
+   */
+  public function getListCacheTags();
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index b4c72a4..a0b8154 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Field\FieldItemInterface;
@@ -144,11 +145,8 @@ protected function getBuildDefaults(EntityInterface $entity, $view_mode, $langco
       '#view_mode' => $view_mode,
       '#langcode' => $langcode,
       '#cache' => array(
-        'tags' =>  array(
-          $this->entityTypeId . '_view' => TRUE,
-          $this->entityTypeId => array($entity->id()),
-        ),
-      )
+        'tags' =>  NestedArray::mergeDeep($this->getCacheTag(), $entity->getCacheTag()),
+      ),
     );
 
     // Cache the rendered output if permitted by the view mode and global entity
@@ -274,14 +272,12 @@ public function resetCache(array $entities = NULL) {
       // Always invalidate the ENTITY_TYPE_list tag.
       $tags = array($this->entityTypeId . '_list' => TRUE);
       foreach ($entities as $entity) {
-        $id = $entity->id();
-        $tags[$this->entityTypeId][$id] = $id;
-        $tags[$this->entityTypeId . '_view_' . $entity->bundle()] = TRUE;
+        $tags = NestedArray::mergeDeep($tags, $entity->getCacheTag());
       }
-      Cache::deleteTags($tags);
+      Cache::invalidateTags($tags);
     }
     else {
-      Cache::deleteTags(array($this->entityTypeId . '_view' => TRUE));
+      Cache::invalidateTags($this->getCacheTag());
     }
   }
 
@@ -364,4 +360,11 @@ protected function isViewModeCacheable($view_mode) {
     return !empty($view_modes_info[$view_mode]['cache']);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTag() {
+    return array($this->entityTypeId . '_view' => TRUE);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
index b41848b..aee9588 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilderInterface.php
@@ -149,4 +149,15 @@ public function viewField(FieldItemListInterface $items, $display_options = arra
    */
   public function viewFieldItem(FieldItemInterface $item, $display_options = array());
 
+  /**
+   * The cache tag associated with this entity view builder.
+   *
+   * An entity view builder is instantiated on a per-entity type basis, so the
+   * cache tags are also per-entity type.
+   *
+   * @return array
+   *   An array of cache tags.
+   */
+  public function getCacheTag();
+
 }
diff --git a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
index cb44851..25efaf7 100644
--- a/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
+++ b/core/modules/block/lib/Drupal/block/BlockViewBuilder.php
@@ -68,16 +68,13 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
       );
       $build[$entity_id]['#configuration']['label'] = check_plain($configuration['label']);
 
-      // Set cache tags; these always need to be set, whether the block is
-      // cacheable or not, so that the page cache is correctly informed.
-      $default_cache_tags = array(
-        'content' => TRUE,
-        'block_view' => TRUE,
-        'block' => array($entity->id()),
-        'theme' => $entity->get('theme'),
-      );
-      $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeep($default_cache_tags, $plugin->getCacheTags());
-
+      $build[$entity_id]['#cache']['tags'] = NestedArray::mergeDeepArray(array(
+        array('content' => TRUE),
+        $this->getCacheTag(), // Block view builder cache tag.
+        $entity->getCacheTag(), // Block entity cache tag.
+        array('theme' => array($entity->id())), // Block entity theme.
+        $plugin->getCacheTags(), // Block plugin cache tags.
+      ));
 
       if ($plugin->isCacheable()) {
         $build[$entity_id]['#pre_render'][] = array($this, 'buildBlock');
@@ -156,16 +153,4 @@ public function buildBlock($build) {
     return $build;
    }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function resetCache(array $entities = NULL) {
-    if (isset($entities)) {
-      Cache::invalidateTags(array('block' => array_keys($entities)));
-    }
-    else {
-      Cache::invalidateTags(array('block_view' => TRUE));
-    }
-  }
-
 }
diff --git a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
index 2101d93..887d3ca 100644
--- a/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
+++ b/core/modules/comment/tests/Drupal/comment/Tests/Entity/CommentLockTest.php
@@ -36,6 +36,8 @@ public function testLocks() {
     $container = new ContainerBuilder();
     $container->set('module_handler', $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'));
     $container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
+    $container->set('cache.test', $this->getMock('Drupal\Core\Cache\CacheBackendInterface'));
+    $container->setParameter('cache_bins', array('cache.test' => 'test'));
     $container->register('request', 'Symfony\Component\HttpFoundation\Request');
     $lock = $this->getMock('Drupal\Core\Lock\LockBackendInterface');
     $cid = 2;
@@ -84,7 +86,12 @@ public function testLocks() {
       ->method('get')
       ->with('status')
       ->will($this->returnValue((object) array('value' => NULL)));
-
+    $comment->expects($this->once())
+      ->method('getCacheTag')
+      ->will($this->returnValue(array('comment' => array($cid))));
+    $comment->expects($this->once())
+      ->method('getListCacheTags')
+      ->will($this->returnValue(array('comments' => TRUE)));
     $storage = $this->getMock('Drupal\comment\CommentStorageInterface');
     $comment->preSave($storage);
     $comment->postSave($storage);
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 2e71d68..75b781d 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -177,7 +177,6 @@ function filter_formats(AccountInterface $account = NULL) {
  * @see filter_formats()
  */
 function filter_formats_reset() {
-  Cache::deleteTags(array('filter_formats' => TRUE));
   drupal_static_reset('filter_formats');
 }
 
diff --git a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
index 365a8aa..9974a63 100644
--- a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
+++ b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\filter\Entity;
 
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Config\Entity\EntityWithPluginBagInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
@@ -197,7 +196,6 @@ public function disable() {
 
     // Clear the filter cache whenever a text format is disabled.
     filter_formats_reset();
-    Cache::deleteTags(array('filter_format' => $this->format));
 
     return $this;
   }
@@ -235,11 +233,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     // Clear the static caches of filter_formats() and others.
     filter_formats_reset();
 
-    if ($update) {
-      // Clear the filter cache whenever a text format is updated.
-      Cache::deleteTags(array('filter_format' => $this->id()));
-    }
-    else {
+    if (!$update) {
       // Default configuration of modules and installation profiles is allowed
       // to specify a list of user roles to grant access to for the new format;
       // apply the defined user role permissions when a new format is inserted
diff --git a/core/modules/node/lib/Drupal/node/Entity/NodeType.php b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
index 8ca6f72..fde17a2 100644
--- a/core/modules/node/lib/Drupal/node/Entity/NodeType.php
+++ b/core/modules/node/lib/Drupal/node/Entity/NodeType.php
@@ -8,7 +8,6 @@
 namespace Drupal\node\Entity;
 
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\node\NodeTypeInterface;
@@ -157,9 +156,6 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
     if (!$update) {
-      // Clear the node type cache, so the new type appears.
-      Cache::deleteTags(array('node_types' => TRUE));
-
       entity_invoke_bundle_hook('create', 'node', $this->id());
 
       // Create a body if the create_body property is true and we're not in
@@ -170,9 +166,6 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
       }
     }
     elseif ($this->getOriginalId() != $this->id()) {
-      // Clear the node type cache to reflect the rename.
-      Cache::deleteTags(array('node_types' => TRUE));
-
       $update_count = node_type_update_nodes($this->getOriginalId(), $this->id());
       if ($update_count) {
         drupal_set_message(format_plural($update_count,
@@ -185,10 +178,6 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
       }
       entity_invoke_bundle_hook('rename', 'node', $this->getOriginalId(), $this->id());
     }
-    else {
-      // Invalidate the cache tag of the updated node type only.
-      Cache::invalidateTags(array('node_type' => $this->id()));
-    }
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Entity/Menu.php b/core/modules/system/lib/Drupal/system/Entity/Menu.php
index 4b6cee5..2ea8dd1 100644
--- a/core/modules/system/lib/Drupal/system/Entity/Menu.php
+++ b/core/modules/system/lib/Drupal/system/Entity/Menu.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\system\Entity;
 
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\system\MenuInterface;
@@ -81,22 +80,4 @@ public function isLocked() {
     return (bool) $this->locked;
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
-    parent::postSave($storage, $update);
-
-    Cache::invalidateTags(array('menu' => $this->id()));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function postDelete(EntityStorageInterface $storage, array $entities) {
-    parent::postDelete($storage, $entities);
-
-    Cache::invalidateTags(array('menu' => array_keys($entities)));
-  }
-
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
index 5112cc2..e4a6a52 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCacheTagsTestBase.php
@@ -222,7 +222,7 @@ public function testReferencedEntity() {
       'entity_test:' . $this->referencing_entity->id(),
       // Includes the main entity's cache tags, since this entity references it.
       $cache_tag,
-      $view_cache_tag
+      $view_cache_tag,
     );
     $non_referencing_entity_cache_tags = array(
       'entity_test_view:1',
diff --git a/core/modules/user/lib/Drupal/user/Entity/Role.php b/core/modules/user/lib/Drupal/user/Entity/Role.php
index 81a0ee6..d9cd07c 100644
--- a/core/modules/user/lib/Drupal/user/Entity/Role.php
+++ b/core/modules/user/lib/Drupal/user/Entity/Role.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\user\Entity;
 
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\user\RoleInterface;
@@ -134,20 +133,8 @@ public function preSave(EntityStorageInterface $storage) {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
-    Cache::invalidateTags(array('role' => $this->id()));
     // Clear render cache.
     entity_render_cache_clear();
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public static function postDelete(EntityStorageInterface $storage, array $entities) {
-    parent::postDelete($storage, $entities);
-
-    $ids = array_keys($entities);
-    $storage->deleteRoleReferences($ids);
-    Cache::invalidateTags(array('role' => $ids));
-  }
-
 }
diff --git a/core/modules/user/lib/Drupal/user/PermissionsHash.php b/core/modules/user/lib/Drupal/user/PermissionsHash.php
index 3c6fe87..1fed13e 100644
--- a/core/modules/user/lib/Drupal/user/PermissionsHash.php
+++ b/core/modules/user/lib/Drupal/user/PermissionsHash.php
@@ -58,7 +58,7 @@ public function generate(AccountInterface $account) {
     }
     else {
       $permissions_hash = $this->doGenerate($sorted_roles);
-      $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, array('role' => $sorted_roles));
+      $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, array('user_role' => $sorted_roles));
     }
 
     return $permissions_hash;
diff --git a/core/modules/views/lib/Drupal/views/Entity/View.php b/core/modules/views/lib/Drupal/views/Entity/View.php
index 2beb7ac..304d1f8 100644
--- a/core/modules/views/lib/Drupal/views/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Entity/View.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\views\Entity;
 
-use Drupal\Core\Cache\Cache;
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\views\Views;
@@ -305,10 +304,7 @@ public function calculateDependencies() {
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
-    // Clear cache tags for this view.
     // @todo Remove if views implements a view_builder controller.
-    $id = $this->id();
-    Cache::deleteTags(array('view' => array($id => $id)));
     views_invalidate_cache();
   }
 
@@ -359,17 +355,9 @@ public static function postDelete(EntityStorageInterface $storage, array $entiti
     parent::postDelete($storage, $entities);
 
     $tempstore = \Drupal::service('user.tempstore')->get('views');
-    $tags = array();
-
     foreach ($entities as $entity) {
-      $id = $entity->id();
-      $tempstore->delete($id);
-      $tags['view'][$id] = $id;
+      $tempstore->delete($entity->id());
     }
-
-    // Clear cache tags for these views.
-    // @todo Remove if views implements a view_builder controller.
-    Cache::deleteTags($tags);
   }
 
   /**
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
index ca3d5ec..0aabe85 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -1213,4 +1213,18 @@ public function calculateDependencies() {
   public function getConfigDependencyName() {
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTag() {
+    $this->storage->getCacheTag();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getListCacheTags() {
+    $this->storage->getListCacheTags();
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index 016cc73..29bca09 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -79,6 +79,13 @@ class ConfigEntityBaseUnitTest extends UnitTestCase {
   protected $id;
 
   /**
+   * The mocked cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheBackend;
+
+  /**
    * {@inheritdoc}
    */
   public static function getInfo() {
@@ -120,10 +127,14 @@ public function setUp() {
       ->with('en')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
     $container->set('uuid', $this->uuid);
     $container->set('language_manager', $this->languageManager);
+    $container->set('cache.test', $this->cacheBackend);
+    $container->setParameter('cache_bins', array('cache.test' => 'test'));
     \Drupal::setContainer($container);
 
     $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Config\Entity\ConfigEntityBase', array($values, $this->entityTypeId));
@@ -333,6 +344,9 @@ public function testEnable() {
    * @depends testSetStatus
    */
   public function testDisable() {
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array($this->entityTypeId => array($this->id)));
     $this->entity->setStatus(TRUE);
     $this->assertSame($this->entity, $this->entity->disable());
     $this->assertFalse($this->entity->status());
@@ -425,4 +439,5 @@ public function testToArray() {
       $this->assertSame($this->entity->get($name), $properties[$name]);
     }
   }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index d6f5c9c..fb1ab1c 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -28,6 +28,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
   protected $entityType;
 
   /**
+   * The type ID of the entity under test.
+   *
+   * @var string
+   */
+  protected $entityTypeId;
+
+  /**
    * The module handler.
    *
    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
@@ -84,6 +91,13 @@ class ConfigEntityStorageTest extends UnitTestCase {
   protected $entityManager;
 
   /**
+   * The mocked cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheBackend;
+
+  /**
    * {@inheritdoc}
    */
   public static function getInfo() {
@@ -101,6 +115,7 @@ protected function setUp() {
     parent::setUp();
 
     $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $this->entityTypeId = 'test_entity_type';
     $this->entityType->expects($this->any())
       ->method('getKey')
       ->will($this->returnValueMap(array(
@@ -109,7 +124,7 @@ protected function setUp() {
       )));
     $this->entityType->expects($this->any())
       ->method('id')
-      ->will($this->returnValue('test_entity_type'));
+      ->will($this->returnValue($this->entityTypeId));
     $this->entityType->expects($this->any())
       ->method('getConfigPrefix')
       ->will($this->returnValue('the_config_prefix'));
@@ -144,8 +159,12 @@ protected function setUp() {
       ->with('test_entity_type')
       ->will($this->returnValue($this->entityType));
 
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
+    $container->set('cache.test', $this->cacheBackend);
+    $container->setParameter('cache_bins', array('cache.test' => 'test'));
     \Drupal::setContainer($container);
 
   }
@@ -158,6 +177,9 @@ public function testCreateWithPredefinedUuid() {
       ->method('getClass')
       ->will($this->returnValue(get_class($this->getMockEntity())));
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->moduleHandler->expects($this->at(0))
       ->method('invokeAll')
       ->with('test_entity_type_create');
@@ -183,6 +205,9 @@ public function testCreate() {
       ->method('getClass')
       ->will($this->returnValue(get_class($this->getMockEntity())));
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->moduleHandler->expects($this->at(0))
       ->method('invokeAll')
       ->with('test_entity_type_create');
@@ -221,6 +246,12 @@ public function testSaveInsert(EntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+      ));
+
     $this->configFactory->expects($this->once())
       ->method('get')
       ->with('the_config_prefix.foo')
@@ -273,6 +304,13 @@ public function testSaveUpdate(EntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId => array('foo'), // Own cache tag.
+      ));
+
     $this->configFactory->expects($this->exactly(2))
       ->method('loadMultiple')
       ->with(array('the_config_prefix.foo'))
@@ -325,6 +363,13 @@ public function testSaveRename(ConfigEntityInterface $entity) {
     $config_object->expects($this->once())
       ->method('save');
 
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId => array('bar'), // Own cache tag.
+      ));
+
     $this->configFactory->expects($this->once())
       ->method('rename')
       ->will($this->returnValue($config_object));
@@ -362,6 +407,9 @@ public function testSaveRename(ConfigEntityInterface $entity) {
    * @expectedExceptionMessage The entity does not have an ID.
    */
   public function testSaveInvalid() {
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $entity = $this->getMockEntity();
     $this->entityStorage->save($entity);
   }
@@ -383,6 +431,9 @@ public function testSaveDuplicate() {
     $config_object->expects($this->never())
       ->method('save');
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->configFactory->expects($this->once())
       ->method('get')
       ->with('the_config_prefix.foo')
@@ -410,6 +461,9 @@ public function testSaveMismatch() {
     $config_object->expects($this->never())
       ->method('save');
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->configFactory->expects($this->once())
       ->method('get')
       ->with('the_config_prefix.foo')
@@ -439,6 +493,12 @@ public function testSaveNoMismatch() {
     $config_object->expects($this->once())
       ->method('save');
 
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+      ));
+
     $this->configFactory->expects($this->once())
       ->method('get')
       ->with('the_config_prefix.baz')
@@ -482,6 +542,9 @@ public function testSaveChangedUuid() {
         array('id', 'foo'),
       )));
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->configFactory->expects($this->at(1))
       ->method('loadMultiple')
       ->with(array('the_config_prefix.foo'))
@@ -643,6 +706,9 @@ public function testLoadRevision() {
    * @covers ::deleteRevision()
    */
   public function testDeleteRevision() {
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
   }
 
@@ -669,6 +735,13 @@ public function testDelete() {
       $config_map[] = array("the_config_prefix.$id", $config_object);
     }
 
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId => array('foo', 'bar'), // Own cache tag.
+      ));
+
     $this->configFactory->expects($this->exactly(2))
       ->method('get')
       ->will($this->returnValueMap($config_map));
@@ -710,6 +783,9 @@ public function testDeleteNothing() {
     $this->configFactory->expects($this->never())
       ->method('get');
 
+    $this->cacheBackend->expects($this->never())
+      ->method('invalidateTags');
+
     $this->entityStorage->delete(array());
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index 47c9ad4..6ccad14 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -68,6 +68,13 @@ class EntityUnitTest extends UnitTestCase {
   protected $languageManager;
 
   /**
+   * The mocked cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $cacheBackend;
+
+  /**
    * The entity values.
    *
    * @var array
@@ -112,11 +119,14 @@ public function setUp() {
       ->with('en')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
+    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
+
     $container = new ContainerBuilder();
     $container->set('entity.manager', $this->entityManager);
     $container->set('uuid', $this->uuid);
     $container->set('language_manager', $this->languageManager);
-
+    $container->set('cache.test', $this->cacheBackend);
+    $container->setParameter('cache_bins', array('cache.test' => 'test'));
     \Drupal::setContainer($container);
 
     $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\Entity', array($this->values, $this->entityTypeId));
@@ -280,9 +290,26 @@ public function testPreSave() {
    * @covers ::postSave
    */
   public function testPostSave() {
+    $this->cacheBackend->expects($this->at(0))
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+      ));
+    $this->cacheBackend->expects($this->at(1))
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId . 's' => TRUE, // List cache tag.
+        $this->entityTypeId => array($this->values['id']), // Own cache tag.
+      ));
+
     // This method is internal, so check for errors on calling it only.
     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
-    $this->entity->postSave($storage);
+
+    // A creation should trigger the invalidation of the "list" cache tag.
+    $this->entity->postSave($storage, FALSE);
+    // An update should trigger the invalidation of both the "list" and the
+    // "own" cache tags.
+    $this->entity->postSave($storage, TRUE);
   }
 
   /**
@@ -317,16 +344,23 @@ public function testPreDelete() {
    * @covers ::postDelete
    */
   public function testPostDelete() {
+    $this->cacheBackend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(array(
+        $this->entityTypeId => array($this->values['id']),
+        $this->entityTypeId . 's' => TRUE,
+      ));
     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
 
     $entity = $this->getMockBuilder('\Drupal\Core\Entity\Entity')
+      ->setConstructorArgs(array($this->values, $this->entityTypeId))
       ->setMethods(array('onSaveOrDelete'))
-      ->disableOriginalConstructor()
       ->getMock();
     $entity->expects($this->once())
       ->method('onSaveOrDelete');
 
-    $this->entity->postDelete($storage, array($entity));
+    $entities = array($this->values['id'] => $entity);
+    $this->entity->postDelete($storage, $entities);
   }
 
   /**
