diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index 60d0ce0..acdc54e 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -275,38 +275,6 @@ protected function buildQuery($ids, $revision_id = FALSE) {
   }
 
   /**
-   * Attaches data to entities upon loading.
-   *
-   * This will attach fields, if the entity is fieldable. It calls
-   * hook_entity_load() for modules which need to add data to all entities.
-   * It also calls hook_TYPE_load() on the loaded entities. For example
-   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
-   * expects special parameters apart from the queried entities, you can set
-   * $this->hookLoadArguments prior to calling the method.
-   * See Drupal\node\NodeStorageController::attachLoad() for an example.
-   *
-   * @param $queried_entities
-   *   Associative array of query results, keyed on the entity ID.
-   * @param $revision_id
-   *   ID of the revision that was loaded, or FALSE if the most current revision
-   *   was loaded.
-   */
-  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
-    // Call hook_entity_load().
-    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (\Drupal::moduleHandler()->getImplementations($this->entityType . '_load') as $module) {
-      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
-    }
-  }
-
-  /**
    * Implements Drupal\Core\Entity\EntityStorageControllerInterface::create().
    */
   public function create(array $values) {
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 2bed3f6..07377aa 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -354,19 +354,7 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     if ($this->entityInfo['fieldable']) {
       $this->loadFieldItems($queried_entities, $load_revision ? static::FIELD_LOAD_REVISION : static::FIELD_LOAD_CURRENT);
     }
-
-    // Call hook_entity_load().
-    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (\Drupal::moduleHandler()->getImplementations($this->entityType . '_load') as $module) {
-      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
-    }
+    parent::attachLoad($queried_entities, $load_revision);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index d4b4ea5..cba3fff 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -645,6 +645,29 @@ public static function postDelete(EntityStorageControllerInterface $storage_cont
   /**
    * {@inheritdoc}
    */
+  public static function attachLoad(EntityStorageControllerInterface $storage_controller, $queried_entities, $load_revision) {
+    $entity_type = $storage_controller->entityType();
+    // Call hook_entity_load().
+    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
+      $function = $module . '_entity_load';
+      $function($queried_entities, $entity_type);
+    }
+    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
+    // always the queried entities, followed by additional arguments set in
+    // $this->hookLoadArguments
+    $args = array_merge(array($queried_entities), static::hookLoadArguments($queried_entities));
+    foreach (\Drupal::moduleHandler()->getImplementations($entity_type . '_load') as $module) {
+      call_user_func_array($module . '_' . $entity_type . '_load', $args);
+    }
+  }
+
+  protected static function hookLoadArguments(array $entities) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
   }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index 43ff5f1..6a1675e 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -45,15 +45,6 @@
   protected $entityInfo;
 
   /**
-   * Additional arguments to pass to hook_TYPE_load().
-   *
-   * Set before calling Drupal\Core\Entity\DatabaseStorageController::attachLoad().
-   *
-   * @var array
-   */
-  protected $hookLoadArguments = array();
-
-  /**
    * Name of the entity's ID field in the entity database table.
    *
    * @var string
@@ -87,6 +78,20 @@ public function __construct($entity_type, $entity_info) {
   /**
    * {@inheritdoc}
    */
+  public function entityType() {
+    return $this->entityType;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function entityInfo() {
+    return $this->entityInfo;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function loadUnchanged($id) {
     $this->resetCache(array($id));
     return $this->load($id);
@@ -272,4 +277,26 @@ protected function invokeTranslationHooks(EntityInterface $entity) {
     }
   }
 
+  /**
+   * Attaches data to entities upon loading.
+   *
+   * This will attach fields, if the entity is fieldable. It calls
+   * hook_entity_load() for modules which need to add data to all entities.
+   * It also calls hook_TYPE_load() on the loaded entities. For example
+   * hook_node_load() or hook_user_load(). If your hook_TYPE_load()
+   * expects special parameters apart from the queried entities, you can set
+   * $this->hookLoadArguments prior to calling the method.
+   * See Drupal\node\NodeStorageController::attachLoad() for an example.
+   *
+   * @param $queried_entities
+   *   Associative array of query results, keyed on the entity ID.
+   * @param $revision_id
+   *   ID of the revision that was loaded, or FALSE if the most current revision
+   *   was loaded.
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    $class = isset($this->entityInfo['class']) ? $this->entityInfo['class']: $this->entityClass;
+    $class::attachLoad($this, $queried_entities, $revision_id);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index c7f72f7..075b87f 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -171,4 +171,18 @@ public function invokeFieldMethod($method, EntityInterface $entity);
    */
   public function invokeFieldItemPrepareCache(EntityInterface $entity);
 
+  /**
+   * Returns the current entity type.
+   *
+   * @return string
+   */
+  public function entityType();
+
+  /**
+   * Returns the current entity info.
+   *
+   * @return string
+   */
+  public function entityInfo();
+
 }
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
index 46b3894..ad7506a 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Feed.php
@@ -199,6 +199,14 @@ public static function preCreate(EntityStorageControllerInterface $storage_contr
   /**
    * {@inheritdoc}
    */
+  public static function attachLoad(EntityStorageControllerInterface $storage_controller, $queried_entities, $load_revision) {
+    $storage_controller->loadCategories($queried_entities);
+    parent::attachLoad($storage_controller, $queried_entities, $load_revision);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
     $storage_controller->deleteCategories($entities);
     foreach ($entities as $entity) {
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php
index f0b6694..15df66e 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Entity/Item.php
@@ -135,6 +135,14 @@ public function label($langcode = NULL) {
   /**
    * {@inheritdoc}
    */
+  public static function attachLoad(EntityStorageControllerInterface $storage_controller, $queried_entities, $load_revision) {
+    $storage_controller->loadCategories($queried_entities);
+    parent::attachLoad($storage_controller, $queried_entities, $load_revision);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function postCreate(EntityStorageControllerInterface $storage_controller) {
     parent::postCreate($storage_controller);
 
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
index 6db9b3f..b8f3eda 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/FeedStorageController.php
@@ -20,14 +20,6 @@
 class FeedStorageController extends DatabaseStorageControllerNG implements FeedStorageControllerInterface {
 
   /**
-   * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
-   */
-  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    parent::attachLoad($queried_entities, $load_revision);
-    $this->loadCategories($queried_entities);
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function loadCategories(array $feeds) {
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
index e17c82b..a4e4da8 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/ItemStorageController.php
@@ -21,14 +21,6 @@
 class ItemStorageController extends DatabaseStorageControllerNG implements ItemStorageControllerInterface {
 
   /**
-   * Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
-   */
-  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    parent::attachLoad($queried_entities, $load_revision);
-    $this->loadCategories($queried_entities);
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function loadCategories(array $entities) {
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
deleted file mode 100644
index 9c5eb21..0000000
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\custom_block\CustomBlockStorageController.
- */
-
-namespace Drupal\custom_block;
-
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
-use Drupal\Core\Entity\EntityStorageControllerInterface;
-
-/**
- * Controller class for custom blocks.
- *
- * This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
- * adding required special handling for custom block entities.
- */
-class CustomBlockStorageController extends DatabaseStorageControllerNG {
-
-  /**
-   * Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
-   */
-  protected function attachLoad(&$blocks, $load_revision = FALSE) {
-    // Create an array of block types for passing as a load argument.
-    // Note that blocks at this point are still \StdClass objects returned from
-    // the database.
-    foreach ($blocks as $id => $entity) {
-      $types[$entity->type] = $entity->type;
-    }
-
-    // Besides the list of blocks, pass one additional argument to
-    // hook_custom_block_load(), containing a list of block types that were
-    // loaded.
-    $this->hookLoadArguments = array($types);
-    parent::attachLoad($blocks, $load_revision);
-  }
-
-}
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
index 2044050..7aadb99 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
@@ -22,7 +22,7 @@
  *   bundle_label = @Translation("Custom Block type"),
  *   module = "custom_block",
  *   controllers = {
- *     "storage" = "Drupal\custom_block\CustomBlockStorageController",
+ *     "storage" = "Drupal\Core\Entity\DatabaseStorageControllerNG",
  *     "access" = "Drupal\custom_block\CustomBlockAccessController",
  *     "list" = "Drupal\custom_block\CustomBlockListController",
  *     "render" = "Drupal\custom_block\CustomBlockRenderController",
@@ -308,4 +308,15 @@ public function getChangedTime() {
     return $this->get('changed')->value;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected static function hookLoadArguments(array $blocks) {
+    $types = array();
+    // Create an array of block types for passing as a load argument.
+    foreach ($blocks as $id => $entity) {
+      $types[$entity->bundle()] = $entity->bundle();
+    }
+    return array($types);
+  }
 }
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index 8367e16..4afd8d3 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -79,6 +79,27 @@ public function getRevisionId() {
   /**
    * {@inheritdoc}
    */
+  protected static function hookLoadArguments(array $nodes) {
+    $bundles = array();
+    foreach ($nodes as $id => $node) {
+      $bundles[$node->bundle()] = TRUE;
+    }
+    return array(array_keys($bundles));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
+    // @todo Handle this through property defaults.
+    if (empty($values['created'])) {
+      $values['created'] = REQUEST_TIME;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function preSave(EntityStorageControllerInterface $storage_controller) {
     parent::preSave($storage_controller);
 
@@ -144,6 +165,13 @@ public static function preDelete(EntityStorageControllerInterface $storage_contr
   /**
    * {@inheritdoc}
    */
+  public static function postDelete(EntityStorageControllerInterface $storage_controller, array $nodes) {
+    \Drupal::service('node.grant_storage')->deleteNodeRecords(array_keys($nodes));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getType() {
     return $this->bundle();
   }
diff --git a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
index fba9847..50d0855 100644
--- a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
+++ b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
@@ -230,7 +230,7 @@ public function write(NodeInterface $node, array $grants, $realm = NULL, $delete
    * {@inheritdoc}
    */
   public function delete() {
-    $this->database->delete('node_access')->execute();
+    $this->database->truncate('node_access')->execute();
   }
 
   /**
@@ -256,4 +256,14 @@ public function count() {
     return $this->database->query('SELECT COUNT(*) FROM {node_access}')->fetchField();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteNodeRecords(array $nids) {
+    $this->database->delete('node_access')
+      ->condition('nid', $nids, 'IN')
+      ->execute();
+
+  }
+
 }
diff --git a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorageInterface.php b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorageInterface.php
index 2457b8f..ad45741 100644
--- a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorageInterface.php
+++ b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorageInterface.php
@@ -121,4 +121,13 @@ public function access(EntityInterface $node, $operation, $langcode, AccountInte
    */
   public function count();
 
+  /**
+   * Remove the access records belonging to certain nodes.
+   *
+   * @param array $nids
+   *   A list of node IDs. The grant records belonging to these nodes will be
+   *   deleted.
+   */
+  public function deleteNodeRecords(array $nids);
+
 }
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 1360af3..d68a3da 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -19,58 +19,6 @@
 class NodeStorageController extends DatabaseStorageControllerNG {
 
   /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::create().
-   */
-  public function create(array $values) {
-    // @todo Handle this through property defaults.
-    if (empty($values['created'])) {
-      $values['created'] = REQUEST_TIME;
-    }
-    return parent::create($values);
-  }
-
-  /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageControllerNG::attachLoad().
-   */
-  protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
-    $queried_entities = $this->mapFromStorageRecords($queried_entities, $load_revision);
-
-    // Create an array of nodes for each content type and pass this to the
-    // object type specific callback. To preserve backward-compatibility we
-    // pass on BC decorators to node-specific hooks, while we pass on the
-    // regular entity objects else.
-    $typed_nodes = array();
-    foreach ($queried_entities as $id => $node) {
-      $typed_nodes[$node->bundle()][$id] = $queried_entities[$id];
-    }
-
-    if ($load_revision) {
-      $this->loadFieldItems($queried_entities, static::FIELD_LOAD_REVISION);
-    }
-    else {
-      $this->loadFieldItems($queried_entities, static::FIELD_LOAD_CURRENT);
-    }
-
-    // Besides the list of nodes, pass one additional argument to
-    // hook_node_load(), containing a list of node types that were loaded.
-    $argument = array_keys($typed_nodes);
-    $this->hookLoadArguments = array($argument);
-
-    // Call hook_entity_load().
-    foreach (\Drupal::moduleHandler()->getImplementations('entity_load') as $module) {
-      $function = $module . '_entity_load';
-      $function($queried_entities, $this->entityType);
-    }
-    // Call hook_TYPE_load(). The first argument for hook_TYPE_load() are
-    // always the queried entities, followed by additional arguments set in
-    // $this->hookLoadArguments.
-    $args = array_merge(array($queried_entities), $this->hookLoadArguments);
-    foreach (\Drupal::moduleHandler()->getImplementations($this->entityType . '_load') as $module) {
-      call_user_func_array($module . '_' . $this->entityType . '_load', $args);
-    }
-  }
-
-  /**
    * {@inheritdoc}
    */
   protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
@@ -79,17 +27,4 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
     $record->comment = isset($record->comment) ? intval($record->comment) : 0;
     return $record;
   }
-
-  /**
-   * Overrides Drupal\Core\Entity\DatabaseStorageController::postDelete().
-   */
-  protected function postDelete($nodes) {
-    // Delete values from other tables also referencing this node.
-    $ids = array_keys($nodes);
-
-    db_delete('node_access')
-      ->condition('nid', $ids, 'IN')
-      ->execute();
-  }
-
 }
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 95d74ce..ccec92f 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityNG;
 use Drupal\Core\Entity\Annotation\EntityType;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\Core\Language\Language;
 
 /**
@@ -136,6 +137,9 @@ public static function baseFieldDefinitions($entity_type) {
       'description' => t('The bundle of the test entity.'),
       'type' => 'string_field',
       'required' => TRUE,
+      'settings' => array(
+        'default_value' => $entity_type,
+      ),
       // @todo: Add allowed values validation.
     );
     $fields['user_id'] = array(
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
deleted file mode 100644
index bb8d390..0000000
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\entity_test\EntityTestStorageController.
- */
-
-namespace Drupal\entity_test;
-
-use Drupal\Core\Entity\DatabaseStorageControllerNG;
-
-/**
- * Defines the controller class for the test entity.
- *
- * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
- * required special handling for test entities.
- */
-class EntityTestStorageController extends DatabaseStorageControllerNG {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function create(array $values) {
-    if (empty($values['type'])) {
-      $values['type'] = $this->entityType;
-    }
-    return parent::create($values);
-  }
-
-}
