diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 9994406..c03253b 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -102,6 +102,8 @@ function entity_get_bundles($entity_type = NULL) {
  */
 function entity_invoke_bundle_hook($hook, $entity_type, $bundle, $bundle_new = NULL) {
   entity_info_cache_clear();
+  $method = 'handleBundle' . ucfirst($hook);
+  Drupal::entityManager()->getStorageController($entity_type)->$method($bundle, $bundle_new);
   module_invoke_all('entity_bundle_' . $hook, $entity_type, $bundle, $bundle_new);
 }
 
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
index 14d3f18..0d9bd71 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php
@@ -75,4 +75,14 @@ public function setStatus($status);
    */
   public function status();
 
+  /**
+   * Retrieves the exportable properties of the entity.
+   *
+   * These are the values that get saved into config.
+   *
+   * @return array
+   *   An array of exportable properties and their values.
+   */
+  public function getExportProperties();
+
 }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
index b86e1f0..f4d56ad 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -529,4 +529,11 @@ public function importDelete($name, Config $new_config, Config $old_config) {
     return TRUE;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function storageType() {
+    return 'config';
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Database/Connection.php b/core/lib/Drupal/Core/Database/Connection.php
index ce7e04f..c9069b1 100644
--- a/core/lib/Drupal/Core/Database/Connection.php
+++ b/core/lib/Drupal/Core/Database/Connection.php
@@ -759,7 +759,7 @@ public function truncate($table, array $options = array()) {
    *
    * This method will lazy-load the appropriate schema library file.
    *
-   * @return Drupal\Core\Database\Schema
+   * @return \Drupal\Core\Database\Schema
    *   The database Schema object for this connection.
    */
   public function schema() {
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index b647009..534fd23 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -7,9 +7,10 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Core\Language\Language;
-use PDO;
-use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\field\FieldInfo;
+use Drupal\field\FieldUpdateForbiddenException;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\field\Plugin\Core\Entity\FieldInstance;
 use Drupal\Core\Entity\Query\QueryInterface;
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Component\Utility\NestedArray;
@@ -59,13 +60,21 @@ class DatabaseStorageController extends EntityStorageControllerBase {
   protected $database;
 
   /**
+   * The field info object.
+   *
+   * @var \Drupal\field\FieldInfo
+   */
+  protected $fieldInfo;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
     return new static(
       $entity_type,
       $entity_info,
-      $container->get('database')
+      $container->get('database'),
+      $container->get('field.info')
     );
   }
 
@@ -79,10 +88,11 @@ public static function createInstance(ContainerInterface $container, $entity_typ
    * @param \Drupal\Core\Database\Connection $database
    *   The database connection to be used.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database) {
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
     parent::__construct($entity_type, $entity_info);
 
     $this->database = $database;
+    $this->fieldInfo = $field_info;
 
     // Check if the entity type supports IDs.
     if (isset($this->entityInfo['entity_keys']['id'])) {
@@ -143,7 +153,7 @@ public function load(array $ids = NULL) {
         // We provide the necessary arguments for PDO to create objects of the
         // specified entity class.
         // @see Drupal\Core\Entity\EntityInterface::__construct()
-        $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+        $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
       }
       $queried_entities = $query_result->fetchAllAssoc($this->idKey);
     }
@@ -188,7 +198,7 @@ public function loadRevision($revision_id) {
       // We provide the necessary arguments for PDO to create objects of the
       // specified entity class.
       // @see Drupal\Core\Entity\EntityInterface::__construct()
-      $query_result->setFetchMode(PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
+      $query_result->setFetchMode(\PDO::FETCH_CLASS, $this->entityInfo['class'], array(array(), $this->entityType));
     }
     $queried_entities = $query_result->fetchAllAssoc($this->idKey);
 
@@ -330,10 +340,10 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     // Attach fields.
     if ($this->entityInfo['fieldable']) {
       if ($load_revision) {
-        field_attach_load_revision($this->entityType, $queried_entities);
+        $this->fieldLoad($this->entityType, $queried_entities, FIELD_LOAD_REVISION);
       }
       else {
-        field_attach_load($this->entityType, $queried_entities);
+        $this->fieldLoad($this->entityType, $queried_entities, FIELD_LOAD_CURRENT);
       }
     }
 
@@ -482,7 +492,7 @@ public function save(EntityInterface $entity) {
   /**
    * Saves an entity revision.
    *
-   * @param Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
    */
   protected function saveRevision(EntityInterface $entity) {
@@ -520,31 +530,6 @@ protected function saveRevision(EntityInterface $entity) {
   }
 
   /**
-   * Invokes a hook on behalf of the entity.
-   *
-   * @param $hook
-   *   One of 'presave', 'insert', 'update', 'predelete', 'delete', or
-   *  'revision_delete'.
-   * @param $entity
-   *   The entity object.
-   */
-  protected function invokeHook($hook, EntityInterface $entity) {
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($entity);
-    }
-    // Invoke the hook.
-    module_invoke_all($this->entityType . '_' . $hook, $entity);
-    // Invoke the respective entity-level hook.
-    module_invoke_all('entity_' . $hook, $entity, $this->entityType);
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function baseFieldDefinitions() {
@@ -558,4 +543,637 @@ public function baseFieldDefinitions() {
   public function getQueryServiceName() {
     return 'entity.query.sql';
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  function doFieldLoad($entity_type, $entities, $age) {
+    $load_current = $age == FIELD_LOAD_CURRENT;
+    $bundles = array();
+    $ids = $load_current ? array_keys($entities) : array();
+    foreach ($entities as $entity) {
+      $bundles[$entity->bundle()] = TRUE;
+      $ids[] = $entity->getRevisionId();
+    }
+    $fields = array();
+    foreach ($bundles as $bundle => $v) {
+      foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
+        $fields[$field_name] = $instance->getField();
+      }
+    }
+    foreach ($fields as $field_name => $field) {
+      $table = $load_current ? static::fieldTableName($field) : static::fieldRevisionTableName($field);
+
+      $results = $this->database->select($table, 't')
+        ->fields('t')
+        ->condition('entity_type', $entity_type)
+        ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
+        ->condition('langcode', field_available_languages($entity_type, $field), 'IN')
+        ->orderBy('delta')
+        ->condition('deleted', 0)
+        ->execute();
+
+      $delta_count = array();
+      foreach ($results as $row) {
+        if (!isset($delta_count[$row->entity_id][$row->langcode])) {
+          $delta_count[$row->entity_id][$row->langcode] = 0;
+        }
+
+        if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
+          $item = array();
+          // For each column declared by the field, populate the item
+          // from the prefixed database column.
+          foreach ($field['columns'] as $column => $attributes) {
+            $column_name = static::fieldColumnName($field_name, $column);
+            // Unserialize the value if specified in the column schema.
+            $item[$column] = (!empty($attributes['serialize'])) ? unserialize($row->$column_name) : $row->$column_name;
+          }
+
+          // Add the item to the field values for the entity.
+          $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
+          $delta_count[$row->entity_id][$row->langcode]++;
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldInsert(EntityInterface $entity) {
+    $this->doFieldWrite($entity, FIELD_STORAGE_INSERT);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldUpdate(EntityInterface $entity) {
+    $this->doFieldWrite($entity, FIELD_STORAGE_UPDATE);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldWrite(EntityInterface $entity, $op) {
+    $vid = $entity->getRevisionId();
+    $id = $entity->id();
+    $bundle = $entity->bundle();
+    $entity_type = $entity->entityType();
+    if (!isset($vid)) {
+      $vid = $id;
+    }
+
+    foreach ($this->fieldInfo->getBundleInstances($entity_type, $bundle) as $field_name => $instance) {
+      $field = $instance->getField();
+      $table_name = static::fieldTableName($field);
+      $revision_name = static::fieldRevisionTableName($field);
+
+      $all_langcodes = field_available_languages($entity_type, $field);
+      $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
+
+      // Delete and insert, rather than update, in case a value was added.
+      if ($op == FIELD_STORAGE_UPDATE) {
+        // Delete language codes present in the incoming $entity->$field_name.
+        // Delete all language codes if $entity->$field_name is empty.
+        $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
+        if ($langcodes) {
+          // Only overwrite the field's base table if saving the default revision
+          // of an entity.
+          if ($entity->isDefaultRevision()) {
+            $this->database->delete($table_name)
+              ->condition('entity_type', $entity_type)
+              ->condition('entity_id', $id)
+              ->condition('langcode', $langcodes, 'IN')
+              ->execute();
+          }
+          $this->database->delete($revision_name)
+            ->condition('entity_type', $entity_type)
+            ->condition('entity_id', $id)
+            ->condition('revision_id', $vid)
+            ->condition('langcode', $langcodes, 'IN')
+            ->execute();
+        }
+      }
+
+      // Prepare the multi-insert query.
+      $do_insert = FALSE;
+      $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
+      foreach ($field['columns'] as $column => $attributes) {
+        $columns[] = static::fieldColumnName($field_name, $column);
+      }
+      $query = $this->database->insert($table_name)->fields($columns);
+      $revision_query = $this->database->insert($revision_name)->fields($columns);
+
+      foreach ($field_langcodes as $langcode) {
+        $items = (array) $entity->{$field_name}[$langcode];
+        $delta_count = 0;
+        foreach ($items as $delta => $item) {
+          // We now know we have someting to insert.
+          $do_insert = TRUE;
+          $record = array(
+            'entity_type' => $entity_type,
+            'entity_id' => $id,
+            'revision_id' => $vid,
+            'bundle' => $bundle,
+            'delta' => $delta,
+            'langcode' => $langcode,
+          );
+          foreach ($field['columns'] as $column => $attributes) {
+            $column_name = static::fieldColumnName($field_name, $column);
+            $value = isset($item[$column]) ? $item[$column] : NULL;
+            // Serialize the value if specified in the column schema.
+            $record[$column_name] = (!empty($attributes['serialize'])) ? serialize($value) : $value;
+          }
+          $query->values($record);
+          if (isset($vid)) {
+            $revision_query->values($record);
+          }
+
+          if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
+            break;
+          }
+        }
+      }
+
+      // Execute the query if we have values to insert.
+      if ($do_insert) {
+        // Only overwrite the field's base table if saving the default revision
+        // of an entity.
+        if ($entity->isDefaultRevision()) {
+          $query->execute();
+        }
+        $revision_query->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function dofieldDelete(EntityInterface $entity) {
+    foreach ($this->fieldInfo->getBundleInstances($entity->getType(), $entity->bundle()) as $instance) {
+      $field = $instance->getField();
+      $table_name = static::fieldTableName($field);
+      $revision_name = static::fieldRevisionTableName($field);
+      $this->database->delete($table_name)
+        ->condition('entity_type', $entity->entityType())
+        ->condition('entity_id', $entity->id())
+        ->execute();
+      $this->database->delete($revision_name)
+        ->condition('entity_type', $entity->entityType())
+        ->condition('entity_id', $entity->id())
+        ->execute();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doFieldRevisionDelete(EntityInterface $entity) {
+    $vid = $entity->getRevisionId();
+    if (isset($vid)) {
+      foreach ($this->fieldInfo->getBundleInstances($entity->getType(), $entity->bundle()) as $instance) {
+        $revision_name = static::fieldRevisionTableName($instance->getField());
+        $this->database->delete($revision_name)
+          ->condition('entity_type', $entity->entityType())
+          ->condition('entity_id', $entity->id())
+          ->condition('revision_id', $vid)
+          ->execute();
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleRename($bundle, $bundle_new) {
+    // We need to account for deleted or inactive fields and instances.
+    $instances = field_read_instances(array('entity_type' => $this->entityType, 'bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
+    foreach ($instances as $instance) {
+      $field = field_info_field_by_id($instance['field_id']);
+      if ($field['storage']['type'] == 'field_sql_storage') {
+        $table_name = static::fieldTableName($field);
+        $revision_name = static::fieldRevisionTableName($field);
+        $this->database->update($table_name)
+          ->fields(array('bundle' => $bundle_new))
+          ->condition('entity_type', $this->entityType)
+          ->condition('bundle', $bundle)
+          ->execute();
+        $this->database->update($revision_name)
+          ->fields(array('bundle' => $bundle_new))
+          ->condition('entity_type', $this->entityType)
+          ->condition('bundle', $bundle)
+          ->execute();
+      }
+    }
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function storageType() {
+    return 'sql';
+  }
+
+  public function handleUpdateField(Field $field, Field $original) {
+    if (!$field->hasData()) {
+      // There is no data. Re-create the tables completely.
+
+      if ($this->database->supportsTransactionalDDL()) {
+        // If the database supports transactional DDL, we can go ahead and rely
+        // on it. If not, we will have to rollback manually if something fails.
+        $transaction = $this->database->startTransaction();
+      }
+
+      try {
+        $original_schema = $this->fieldSqlSchema($original);
+        foreach ($original_schema as $name => $table) {
+          $this->database->schema()->dropTable($name, $table);
+        }
+        $schema = $this->fieldSqlSchema($field);
+        foreach ($schema as $name => $table) {
+          $this->database->schema()->createTable($name, $table);
+        }
+      }
+      catch (\Exception $e) {
+        if ($this->database->supportsTransactionalDDL()) {
+          $transaction->rollback();
+        }
+        else {
+          // Recreate tables.
+          $original_schema = $this->fieldSqlSchema($original);
+          foreach ($original_schema as $name => $table) {
+            if (!$this->database->schema()->tableExists($name)) {
+              $this->database->schema()->createTable($name, $table);
+            }
+          }
+        }
+        throw $e;
+      }
+    }
+    else {
+      if ($field['columns'] != $original['columns']) {
+        throw new FieldUpdateForbiddenException("field_sql_storage cannot change the schema for an existing field with data.");
+      }
+      // There is data, so there are no column changes. Drop all the
+      // prior indexes and create all the new ones, except for all the
+      // priors that exist unchanged.
+      $table = static::fieldTableName($original);
+      $revision_table = static::fieldRevisionTableName($original);
+
+      $schema = $field->getSchema();
+      $original_schema = $original->getSchema();
+
+      foreach ($original_schema['indexes'] as $name => $columns) {
+        if (!isset($schema['indexes'][$name]) || $columns != $schema['indexes'][$name]) {
+          $real_name = static::fieldindexName($field['field_name'], $name);
+          $this->database->schema()->dropIndex($table, $real_name);
+          $this->database->schema()->dropIndex($revision_table, $real_name);
+        }
+      }
+      $table = static::fieldTableName($field);
+      $revision_table = static::fieldRevisionTableName($field);
+      foreach ($schema['indexes'] as $name => $columns) {
+        if (!isset($original_schema['indexes'][$name]) || $columns != $original_schema['indexes'][$name]) {
+          $real_name = static::fieldindexName($field['field_name'], $name);
+          $real_columns = array();
+          foreach ($columns as $column_name) {
+            // Indexes can be specified as either a column name or an array with
+            // column name and length. Allow for either case.
+            if (is_array($column_name)) {
+              $real_columns[] = array(
+                static::fieldColumnName($field['field_name'], $column_name[0]),
+                $column_name[1],
+              );
+            }
+            else {
+              $real_columns[] = static::fieldColumnName($field['field_name'], $column_name);
+            }
+          }
+          $this->database->schema()->addIndex($table, $real_name, $real_columns);
+          $this->database->schema()->addIndex($revision_table, $real_name, $real_columns);
+        }
+      }
+    }
+  }
+
+  /**
+   * Gets the SQL table schema.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param Field $field
+   *
+   * @return array
+   */
+  public static function fieldSqlSchema(Field $field) {
+    $deleted = $field['deleted'] ? 'deleted ' : '';
+    $current = array(
+      'description' => "Data storage for {$deleted}field {$field['id']} ({$field['field_name']})",
+      'fields' => array(
+        'entity_type' => array(
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The entity type this data is attached to',
+        ),
+        'bundle' => array(
+          'type' => 'varchar',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
+        ),
+        'deleted' => array(
+          'type' => 'int',
+          'size' => 'tiny',
+          'not null' => TRUE,
+          'default' => 0,
+          'description' => 'A boolean indicating whether this data item has been deleted'
+        ),
+        'entity_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The entity id this data is attached to',
+        ),
+        'revision_id' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => FALSE,
+          'description' => 'The entity revision id this data is attached to, or NULL if the entity type is not versioned',
+        ),
+        // @todo Consider storing langcode as integer.
+        'langcode' => array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => TRUE,
+          'default' => '',
+          'description' => 'The language code for this data item.',
+        ),
+        'delta' => array(
+          'type' => 'int',
+          'unsigned' => TRUE,
+          'not null' => TRUE,
+          'description' => 'The sequence number for this data item, used for multi-value fields',
+        ),
+      ),
+      'primary key' => array('entity_type', 'entity_id', 'deleted', 'delta', 'langcode'),
+      'indexes' => array(
+        'entity_type' => array('entity_type'),
+        'bundle' => array('bundle'),
+        'deleted' => array('deleted'),
+        'entity_id' => array('entity_id'),
+        'revision_id' => array('revision_id'),
+        'langcode' => array('langcode'),
+      ),
+    );
+
+    $schema = $field->getSchema();
+
+    // Add field columns.
+    foreach ($schema['columns'] as $column_name => $attributes) {
+      $real_name = static::fieldColumnName($field['field_name'], $column_name);
+      $current['fields'][$real_name] = $attributes;
+    }
+
+    // Add indexes.
+    foreach ($schema['indexes'] as $index_name => $columns) {
+      $real_name = static::fieldindexName($field['field_name'], $index_name);
+      foreach ($columns as $column_name) {
+        // Indexes can be specified as either a column name or an array with
+        // column name and length. Allow for either case.
+        if (is_array($column_name)) {
+          $current['indexes'][$real_name][] = array(
+            static::fieldColumnName($field['field_name'], $column_name[0]),
+            $column_name[1],
+          );
+        }
+        else {
+          $current['indexes'][$real_name][] = static::fieldColumnName($field['field_name'], $column_name);
+        }
+      }
+    }
+
+    // Add foreign keys.
+    foreach ($schema['foreign keys'] as $specifier => $specification) {
+      $real_name = static::fieldindexName($field['field_name'], $specifier);
+      $current['foreign keys'][$real_name]['table'] = $specification['table'];
+      foreach ($specification['columns'] as $column_name => $referenced) {
+        $sql_storage_column = static::fieldColumnName($field['field_name'], $column_name);
+        $current['foreign keys'][$real_name]['columns'][$sql_storage_column] = $referenced;
+      }
+    }
+
+    // Construct the revision table.
+    $revision = $current;
+    $revision['description'] = "Revision archive storage for {$deleted}field {$field['id']} ({$field['field_name']})";
+    $revision['primary key'] = array('entity_type', 'entity_id', 'revision_id', 'deleted', 'delta', 'langcode');
+    $revision['fields']['revision_id']['not null'] = TRUE;
+    $revision['fields']['revision_id']['description'] = 'The entity revision id this data is attached to';
+
+    return array(
+      static::fieldTableName($field) => $current,
+      static::fieldRevisionTableName($field) => $revision,
+    );
+  }
+
+  /**
+   * Generates a table name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param $field
+   *   The field structure.
+   *
+   * @return
+   *   A string containing the generated name for the database table.
+   *
+   */
+  static public function fieldTableName($field) {
+    if ($field['deleted']) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
+      // table names longer than 64 characters, we hash the uuid and return
+      // the first 10 characters so we end up with a short unique ID.
+      return "field_deleted_data_" . substr(hash('sha256', $field['uuid']), 0, 10);
+    }
+    else {
+      return "field_data_{$field['field_name']}";
+    }
+  }
+
+  /**
+   * Generates a table name for a field revision archive table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param $name
+   *   The field structure.
+   *
+   * @return
+   *   A string containing the generated name for the database table.
+   */
+  static public function fieldRevisionTableName($field) {
+    if ($field['deleted']) {
+      // When a field is a deleted, the table is renamed to
+      // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up
+      // with table names longer than 64 characters, we hash the uuid and
+      // return the first 10 characters so we end up with a short unique ID.
+      return "field_deleted_revision_" . substr(hash('sha256', $field['uuid']), 0, 10);
+    }
+    else {
+      return "field_revision_{$field['field_name']}";
+    }
+  }
+
+  /**
+   * Generates an index name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases.
+   *
+   * @param $name
+   *   The name of the field.
+   * @param $column
+   *   The name of the index.
+   *
+   * @return
+   *   A string containing a generated index name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function fieldIndexName($name, $index) {
+    return $name . '_' . $index;
+  }
+
+  /**
+   * Generates a column name for a field data table.
+   *
+   * @private Calling this function circumvents the entity system and is
+   * strongly discouraged. This function is not considered part of the public
+   * API and modules relying on it might break even in minor releases. Only
+   * call this function to write a query that \Drupal::entityQuery() does not
+   * support. Always call entity_load() before using the data found in the
+   * table.
+   *
+   * @param $name
+   *   The name of the field.
+   * @param $column
+   *   The name of the column.
+   *
+   * @return
+   *   A string containing a generated column name for a field data table that is
+   *   unique among all other fields.
+   */
+  static public function fieldColumnName($name, $column) {
+    return in_array($column, Field::getReservedColumns()) ? $column : $name . '_' . $column;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFirstInstance(FieldInstance $instance) {
+    $schema = $this->fieldSqlSchema($instance->getField());
+    foreach ($schema as $name => $table) {
+      $this->database->schema()->createTable($name, $table);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceDelete(FieldInstance $instance) {
+    $field = $instance->getField();
+    $table_name = static::fieldTableName($field);
+    $revision_name = static::fieldRevisionTableName($field);
+    $this->database->update($table_name)
+      ->fields(array('deleted' => 1))
+      ->condition('entity_type', $instance['entity_type'])
+      ->condition('bundle', $instance['bundle'])
+      ->execute();
+    $this->database->update($revision_name)
+      ->fields(array('deleted' => 1))
+      ->condition('entity_type', $instance['entity_type'])
+      ->condition('bundle', $instance['bundle'])
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleDeleteField(Field $field) {
+    // Mark all data associated with the field for deletion.
+    $field['deleted'] = FALSE;
+    $table = static::fieldTableName($field);
+    $revision_table = static::fieldRevisionTableName($field);
+    $this->database->update($table)
+      ->fields(array('deleted' => 1))
+      ->execute();
+
+    // Move the table to a unique name while the table contents are being deleted.
+    $field['deleted'] = TRUE;
+    $new_table = static::fieldTableName($field);
+    $revision_new_table = static::fieldRevisionTableName($field);
+    $this->database->schema()->renameTable($table, $new_table);
+    $this->database->schema()->renameTable($revision_table, $revision_new_table);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fieldPurgeData($entity_id, Field $field, FieldInstance $instance) {
+    parent::fieldPurgeData($entity_id, $field, $instance);
+    $table_name = static::fieldTableName($field);
+    $revision_name = static::fieldRevisionTableName($field);
+    $this->database->delete($table_name)
+      ->condition('entity_type', $instance->entity_type)
+      ->condition('entity_id', $entity_id)
+      ->execute();
+    $this->database->delete($revision_name)
+      ->condition('entity_type', $instance->entity_type)
+      ->condition('entity_id', $entity_id)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function fieldValues($entity_id, Field $field, FieldInstance $instance) {
+    $field_name = $field->id();
+    $columns = array();
+    foreach ($field->getColumns() as $column_name => $data) {
+      $columns[] = static::fieldColumnName($field_name, $column_name);
+    }
+    return $this->database->select(static::fieldTableName($field), 't')
+      ->fields('t', $columns)
+      ->condition('entity_id', $entity_id)
+      ->execute()
+      ->fetchAllAssoc();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fieldPurge(Field $field) {
+    $table_name = static::fieldTableName($field);
+    $revision_name = static::fieldRevisionTableName($field);
+    $this->database->schema()->dropTable($table_name);
+    $this->database->schema()->dropTable($revision_name);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index fe11758..79bd627 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Language\Language;
+use Drupal\field\FieldInfo;
 use PDO;
 
 use Drupal\Core\Entity\Query\QueryInterface;
@@ -54,8 +55,8 @@ class DatabaseStorageControllerNG extends DatabaseStorageController {
   /**
    * Overrides DatabaseStorageController::__construct().
    */
-  public function __construct($entity_type, array $entity_info, Connection $database) {
-    parent::__construct($entity_type,$entity_info, $database);
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info) {
+    parent::__construct($entity_type,$entity_info, $database, $field_info);
     $this->bundleKey = !empty($this->entityInfo['entity_keys']['bundle']) ? $this->entityInfo['entity_keys']['bundle'] : FALSE;
     $this->entityClass = $this->entityInfo['class'];
 
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 648ec98..c0725e1 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -88,6 +88,15 @@ class EntityManager extends PluginManagerBase {
   protected $fieldDefinitions;
 
   /**
+   * The root paths.
+   *
+   * @see \Drupal\Core\Entity\EntityManager::__construct().
+   *
+   * @var \Traversable
+   */
+  protected $namespaces;
+
+  /**
    * Constructs a new Entity plugin manager.
    *
    * @param \Traversable $namespaces
@@ -104,21 +113,38 @@ class EntityManager extends PluginManagerBase {
    */
   public function __construct(\Traversable $namespaces, ContainerInterface $container, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManager $language_manager) {
     // Allow the plugin definition to be altered by hook_entity_info_alter().
-    $annotation_namespaces = array(
-      'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
-    );
 
     $this->moduleHandler = $module_handler;
     $this->cache = $cache;
     $this->languageManager = $language_manager;
+    $this->namespaces = $namespaces;
 
+    $this->doDiscovery($namespaces);
+    $this->factory = new DefaultFactory($this->discovery);
+    $this->container = $container;
+  }
+
+  protected function doDiscovery($namespaces) {
+    $annotation_namespaces = array(
+      'Drupal\Core\Entity\Annotation' => DRUPAL_ROOT . '/core/lib',
+    );
     $this->discovery = new AnnotatedClassDiscovery('Core/Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
     $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
     $this->discovery = new AlterDecorator($this->discovery, 'entity_info');
     $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->langcode, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+  }
 
-    $this->factory = new DefaultFactory($this->discovery);
-    $this->container = $container;
+  /**
+   * Add more namespaces to the entity manager.
+   *
+   * @param \Traversable $namespaces
+   */
+  public function addNamespaces(\Traversable $namespaces) {
+    reset($this->namespaces);
+    $iterator = new \AppendIterator;
+    $iterator->append(new \IteratorIterator($this->namespaces));
+    $iterator->append($namespaces);
+    $this->doDiscovery($iterator);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index 5dbf55b..6aeff25 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
@@ -6,6 +6,9 @@
  */
 
 namespace Drupal\Core\Entity;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\field\Plugin\Core\Entity\FieldInstance;
+use Symfony\Component\DependencyInjection\Container;
 
 /**
  * A base entity storage controller class.
@@ -234,4 +237,311 @@ public function invokeFieldItemPrepareCache(EntityInterface $entity) {
     }
   }
 
+  /**
+   * Invokes a hook on behalf of the entity.
+   *
+   * @param $hook
+   *   One of 'presave', 'insert', 'update', 'predelete', 'delete', or
+   *  'revision_delete'.
+   * @param $entity
+   *   The entity object.
+   */
+  protected function invokeHook($hook, EntityInterface $entity) {
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($entity);
+    }
+    // Invoke the hook.
+    module_invoke_all($this->entityType . '_' . $hook, $entity);
+    // Invoke the respective entity-level hook.
+    module_invoke_all('entity_' . $hook, $entity, $this->entityType);
+  }
+
+  /**
+   * Loads fields for the current revisions of a group of entities.
+   *
+   * Loads all fields for each entity object in a group of a single entity type.
+   * The loaded field values are added directly to the entity objects.
+   *
+   * field_attach_load() is automatically called by the default entity controller
+   * class, and thus, in most cases, doesn't need to be explicitly called by the
+   * entity type module.
+   *
+   * @param $entity_type
+   *   The type of entities in $entities; e.g., 'node' or 'user'.
+   * @param $entities
+   *   An array of entities for which to load fields, keyed by entity ID. Each
+   *   entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
+   *   filled in. The function adds the loaded field data directly in the entity
+   *   objects of the $entities array.
+   * @param $age
+   *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
+   *   FIELD_LOAD_REVISION to load the version indicated by each entity.
+   */
+  protected function fieldLoad($entity_type, $entities, $age) {
+    $load_current = $age == FIELD_LOAD_CURRENT;
+
+    $info = entity_get_info($entity_type);
+    // Only the most current revision of non-deleted fields for cacheable entity
+    // types can be cached.
+    $use_cache = $load_current && $info['field_cache'];
+    if (empty($entities)) {
+      return;
+    }
+
+    // Ensure we are working with a BC mode entity.
+    foreach ($entities as $id => $entity) {
+      $entities[$id] = $entity->getBCEntity();
+    }
+
+    // Assume all entities will need to be queried. Entities found in the cache
+    // will be removed from the list.
+    $queried_entities = $entities;
+
+    // Fetch available entities from cache, if applicable.
+    if ($use_cache) {
+      // Build the list of cache entries to retrieve.
+      $cids = array();
+      foreach ($entities as $id => $entity) {
+        $cids[] = "field:$entity_type:$id";
+      }
+      $cache = cache('field')->getMultiple($cids);
+      // Put the cached field values back into the entities and remove them from
+      // the list of entities to query.
+      foreach ($entities as $id => $entity) {
+        $cid = "field:$entity_type:$id";
+        if (isset($cache[$cid])) {
+          unset($queried_entities[$id]);
+          foreach ($cache[$cid]->data as $field_name => $values) {
+            $entity->$field_name = $values;
+          }
+        }
+      }
+    }
+
+    // Fetch other entities from their storage location.
+    if ($queried_entities) {
+      // The invoke order is:
+      // - hook_field_storage_pre_load()
+      // - storage backend's hook_field_storage_load()
+      // - Field class's prepareCache() method.
+      // - hook_field_attach_load()
+
+      // Invoke hook_field_storage_pre_load(): let any module load field
+      // data before the storage engine, accumulating along the way.
+      foreach (module_implements('field_storage_pre_load') as $module) {
+        $function = $module . '_field_storage_pre_load';
+        $function($entity_type, $queried_entities, $age);
+      }
+
+      $this->doFieldLoad($entity_type, $queried_entities, $age);
+
+      // Invoke the field type's prepareCache() method.
+      foreach ($queried_entities as $entity) {
+        \Drupal::entityManager()
+          ->getStorageController($entity_type)
+          ->invokeFieldItemPrepareCache($entity);
+      }
+
+      // Invoke hook_field_attach_load(): let other modules act on loading the
+      // entity.
+      module_invoke_all('field_attach_load', $entity_type, $queried_entities, $age);
+
+      // Build cache data.
+      if ($use_cache) {
+        foreach ($queried_entities as $id => $entity) {
+          $data = array();
+          $instances = field_info_instances($entity_type, $entity->bundle());
+          foreach ($instances as $instance) {
+            $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
+          }
+          $cid = "field:$entity_type:$id";
+          cache('field')->set($cid, $data);
+        }
+      }
+    }
+  }
+
+  /**
+   * Save field data for a new entity.
+   *
+   * The passed-in entity must already contain its id and (if applicable)
+   * revision id attributes.
+   * Default values (if any) will be saved for fields not present in the
+   * $entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   * @return
+   *   Default values (if any) will be added to the $entity parameter for fields
+   *   it leaves unspecified.
+   */
+  protected function fieldInsert(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    // Let any module insert field data before the storage engine, accumulating
+    // saved fields along the way.
+    foreach (module_implements('field_storage_pre_insert') as $module) {
+      $function = $module . '_field_storage_pre_insert';
+      $function($entity);
+    }
+    $this->doFieldInsert($entity);
+  }
+
+  /**
+   * Saves field data for an existing entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   */
+  protected function fieldUpdate(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    // Let any module update field data before the storage engine
+    foreach (module_implements('field_storage_pre_update') as $module) {
+      $function = $module . '_field_storage_pre_update';
+      $function($entity);
+    }
+
+    $this->doFieldUpdate($entity);
+
+    $entity_info = $entity->entityInfo();
+    if ($entity_info['field_cache']) {
+      cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+    }
+  }
+
+  /**
+   * Deletes field data for an existing entity. This deletes all revisions of
+   * field data for the entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose field data to delete.
+   */
+  protected function fieldDelete(EntityInterface $entity) {
+    // Ensure we are working with a BC mode entity.
+    $entity = $entity->getBCEntity();
+
+    $this->doFieldDelete($entity);
+
+    $entity_info = $entity->entityInfo();
+    if ($entity_info['field_cache']) {
+      cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
+    }
+  }
+
+  /**
+   * Delete field data for a single revision of an existing entity. The passed
+   * entity must have a revision ID attribute.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity with fields to save.
+   */
+  protected function fieldRevisionDelete(EntityInterface $entity) {
+    $this->dofieldRevisionDelete($entity->getBCEntity());
+  }
+
+  protected function doFieldInsert(EntityInterface $entity) {
+
+  }
+
+  protected function doFieldUpdate(EntityInterface $entity) {
+
+  }
+
+  protected function doFieldDelete(EntityInterface $entity) {
+
+  }
+
+  protected function doFieldRevisionDelete(EntityInterface $entity) {
+
+  }
+
+  protected function doFieldLoad($entity_type, $queried_entities, $age) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleCreate($bundle) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleRename($bundle, $bundle_new) {
+
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleBundleDelete($bundle) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleFirstInstance(FieldInstance $instance) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleUpdateField(Field $field, Field $original) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleInstanceDelete(FieldInstance $instance) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handleDeleteField(Field $field) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fieldPurgeData($entity_id, Field $field, FieldInstance $instance) {
+    $values = $this->fieldValues($entity_id, $field, $instance);
+    foreach ($values as $value) {
+      $definition = _field_generate_entity_field_definition($field, $instance);
+      $items = \Drupal::typedData()->create($definition, $value, $field->id());
+      $items->delete();
+    }
+  }
+
+  /**
+   * Get the field values for a single field of a single entity.
+   *
+   * @param $entity_id
+   * @param Field $field
+   * @param FieldInstance $instance
+   * @return array
+   */
+  protected function fieldValues($entity_id, Field $field, FieldInstance $instance) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fieldPurge(Field $field) {
+
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index 5ae3076..08a24ac 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -6,6 +6,8 @@
  */
 
 namespace Drupal\Core\Entity;
+use Drupal\field\Plugin\Core\Entity\Field;
+use Drupal\field\Plugin\Core\Entity\FieldInstance;
 
 /**
  * Defines a common interface for entity controller classes.
@@ -162,4 +164,80 @@ public function invokeFieldMethod($method, EntityInterface $entity);
    */
   public function invokeFieldItemPrepareCache(EntityInterface $entity);
 
+  /**
+   * Allows reaction to a bundle being created.
+   *
+   * @param string $bundle
+   *   The name of the bundle created.
+   */
+  public function handleBundleCreate($bundle);
+
+  /**
+   * Allows reaction to a bundle being renamed.
+   *
+   * @param string $bundle
+   *   The name of the bundle being renamed.
+   * @param string $bundle_new
+   *   The new name of the bundle.
+   */
+  public function handleBundleRename($bundle, $bundle_new);
+
+  /**
+   * Allows reaction to a bundle being deleted.
+   *
+   * @param string $bundle
+   *   The name of the bundle being deleted.
+   */
+  public function handleBundleDelete($bundle);
+
+  /**
+   * The type of storage, for example 'sql'.
+   *
+   * @return string
+   */
+  public function storageType();
+
+  /**
+   * Allows reaction to first instance created.
+   *
+   * As there is no storage controller yet for the field when it is created,
+   * this is the first chance for the storage controller to react.
+   */
+  public function handleFirstInstance(FieldInstance $instance);
+
+  /**
+   * Allows reaction to the update of a configurable field.
+   */
+  public function handleUpdateField(Field $field, Field $original);
+
+  /**
+   * Allows reaction to the deletion of a configurable field.
+   */
+  public function handleDeleteField(Field $field);
+
+  /**
+   * Allows reaction to the deletion of a configurable field instance.
+   */
+  public function handleInstanceDelete(FieldInstance $instance);
+
+  /**
+   * Purges the field data for a single field on a single entity.
+   *
+   * The entity itself is not being deleted, and it is quite possible that
+   * other field data will remain attached to it.
+   *
+   * @param int $entity_id
+   *   The entity id for the entity whose field data is being purged.
+   * @param $field
+   *   The (possibly deleted) field whose data is being purged.
+   * @param $instance
+   *   The deleted field instance whose data is being purged.
+   */
+  public function fieldPurgeData($entity_id, Field $field, FieldInstance $instance);
+
+  /**
+   * All the field data is gone, final cleanup.
+   */
+  public function fieldPurge(Field $field);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php
index c2dfbdb..bfc7c50 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php
@@ -109,7 +109,8 @@ public function update();
    * Defines custom delete behavior for field values.
    *
    * This method is called during the process of deleting an entity, just before
-   * values are deleted from storage.
+   * values are deleted from storage. There is no parent available during
+   * delete.
    */
   public function delete();
 
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index f346129..e2e766f 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Query\Sql;
 
 use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\Query\QueryException;
 use Drupal\field\Plugin\Core\Entity\Field;
 
@@ -157,7 +158,7 @@ function addField($field, $type, $langcode) {
           $column = 'value';
         }
         $table = $this->ensureFieldTable($index_prefix, $field, $type, $langcode, $base_table, $entity_id_field, $field_id_field);
-        $sql_column = _field_sql_storage_columnname($field['field_name'], $column);
+        $sql_column = DatabaseStorageController::fieldColumnName($field['field_name'], $column);
       }
       // This is an entity property (non-configurable field).
       else {
@@ -242,7 +243,7 @@ protected function ensureEntityTable($index_prefix, $property, $type, $langcode,
   protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $base_table, $entity_id_field, $field_id_field) {
     $field_name = $field['field_name'];
     if (!isset($this->fieldTables[$index_prefix . $field_name])) {
-      $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
+      $table = $this->sqlQuery->getMetaData('age') == FIELD_LOAD_CURRENT ? DatabaseStorageController::fieldTableName($field) : DatabaseStorageController::fieldRevisionTableName($field);
       if ($field['cardinality'] != 1) {
         $this->sqlQuery->addMetaData('simple_query', FALSE);
       }
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
index 0698234..5fb5330 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockStorageController.php
@@ -16,7 +16,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageControllerNG class,
  * adding required special handling for custom block entities.
  */
-class CustomBlockStorageController extends DatabaseStorageControllerNG implements EntityStorageControllerInterface {
+class CustomBlockStorageController extends DatabaseStorageControllerNG {
 
   /**
    * Overrides \Drupal\Core\Entity\DatabaseStorageController::attachLoad().
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index efaf9f3..0b6f9f0 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -12,6 +12,9 @@ function comment_uninstall() {
   // Remove variables.
   variable_del('comment_block_count');
   $node_types = array_keys(node_type_get_types());
+  Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
+    'Drupal\comment' => DRUPAL_ROOT . '/core/modules/comment/lib',
+  )));
   foreach ($node_types as $node_type) {
     entity_invoke_bundle_hook('delete', 'comment', 'comment_node_' . $node_type);
     variable_del('comment_' . $node_type);
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
index 20ef9f0..939a01e 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverride.php
@@ -21,7 +21,7 @@ class ConfigLocaleOverride extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('locale', 'config_test', 'user', 'language', 'system');
+  public static $modules = array('locale', 'config_test', 'user', 'language', 'system', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
index c9e2e5d..271acfd 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/MessageEntityTest.php
@@ -21,7 +21,7 @@ class MessageEntityTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'contact');
+  public static $modules = array('system', 'contact', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
index 166a64b..5285eb3 100644
--- a/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
+++ b/core/modules/contact/lib/Drupal/contact/Tests/Views/ContactFieldsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\contact\Tests\Views;
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\views\Tests\ViewTestBase;
 
 /**
@@ -58,7 +59,7 @@ protected function setUp() {
    * Tests the views data generation.
    */
   public function testViewsData() {
-    $table_name = _field_sql_storage_tablename($this->field);
+    $table_name = DatabaseStorageController::fieldTableName($this->field);
     $data = $this->container->get('views.views_data')->get($table_name);
 
     // Test that the expected data array is returned.
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index 649f033..e05a0e3 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -19,7 +19,7 @@ class EditTestBase extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_sql_storage', 'field_test', 'number', 'text', 'edit');
+  public static $modules = array('system', 'entity', 'entity_test', 'field', 'field_test', 'number', 'text', 'edit');
   /**
    * Sets the default field storage backend for fields created during tests.
    */
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index 4337abd..b42cd49 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -131,7 +131,7 @@ public function testExtraFieldComponent() {
    * Tests the behavior of a field component within an EntityDisplay object.
    */
   public function testFieldComponent() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $display = entity_create('entity_display', array(
       'targetEntityType' => 'entity_test',
@@ -202,7 +202,7 @@ public function testFieldComponent() {
    * Tests renaming and deleting a bundle.
    */
   public function testRenameDeleteBundle() {
-    $this->enableModules(array('field_sql_storage', 'field_test', 'node', 'system', 'text'));
+    $this->enableModules(array('field_test', 'node', 'system', 'text'));
     $this->installSchema('system', array('variable'));
     $this->installSchema('node', array('node'));
 
@@ -231,7 +231,7 @@ public function testRenameDeleteBundle() {
    * Tests deleting field instance.
    */
   public function testDeleteFieldInstance() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $field_name = 'test_field';
     // Create a field and an instance.
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
index a74c966..112c406 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFormDisplayTest.php
@@ -53,7 +53,7 @@ public function testEntityGetFromDisplay() {
    * Tests the behavior of a field component within an EntityFormDisplay object.
    */
   public function testFieldComponent() {
-    $this->enableModules(array('field_sql_storage', 'field_test'));
+    $this->enableModules(array('field_test'));
 
     $form_display = entity_create('entity_form_display', array(
       'targetEntityType' => 'entity_test',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
index 8c17fd6..2a5a9bc 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceItemTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\entity_reference\Tests;
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Entity\Field\FieldItemInterface;
 use Drupal\Core\Language\Language;
@@ -114,12 +115,12 @@ public function testEntityReferenceFieldSchema() {
     $foreign_key_column_name = 'target_id';
 
     // Grab the SQL schema and verify that the 'foreign keys' are present.
-    $schemas = _field_sql_storage_schema($field);
-    $schema = $schemas[_field_sql_storage_tablename($field)];
+    $schemas = DatabaseStorageController::fieldSqlSchema($field);
+    $schema = $schemas[DatabaseStorageController::fieldTableName($field)];
     $this->assertEqual(count($schema['foreign keys']), 1, 'There is 1 foreign key in the schema.');
 
     $foreign_key = reset($schema['foreign keys']);
-    $foreign_key_column = _field_sql_storage_columnname($field['field_name'], $foreign_key_column_name);
+    $foreign_key_column = DatabaseStorageController::fieldColumnName($field['field_name'], $foreign_key_column_name);
     $this->assertEqual($foreign_key['table'], 'taxonomy_term_data', 'Foreign key table name preserved in the schema.');
     $this->assertEqual($foreign_key['columns'][$foreign_key_column], 'tid', 'Foreign key column name preserved in the schema.');
 
@@ -129,8 +130,8 @@ public function testEntityReferenceFieldSchema() {
     entity_reference_create_instance('entity_test', 'entity_test', $field_name, 'Test vocabulary reference', 'taxonomy_vocabulary');
     $field = field_info_field($field_name);
 
-    $schemas = _field_sql_storage_schema($field);
-    $schema = $schemas[_field_sql_storage_tablename($field)];
+    $schemas = DatabaseStorageController::fieldSqlSchema($field);
+    $schema = $schemas[DatabaseStorageController::fieldTableName($field)];
     $this->assertFalse(isset($schema['foreign keys']), 'There is no foreign key in the schema.');
   }
 }
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 02fd704..5861fd4 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -400,31 +400,6 @@ function hook_field_attach_preprocess_alter(&$variables, $context) {
 }
 
 /**
- * Act on field_purge_data().
- *
- * This hook is invoked in field_purge_data() and allows modules to act on
- * purging data from a single field pseudo-entity. For example, if a module
- * relates data in the field with its own data, it may purge its own data during
- * this process as well.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data is being purged.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- *
- * @see @link field_purge Field API bulk data deletion @endlink
- * @see field_purge_data()
- */
-function hook_field_attach_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
-  // find the corresponding data in mymodule and purge it
-  if ($entity->entityType() == 'node' && $field->field_name == 'my_field_name') {
-    mymodule_remove_mydata($entity->nid);
-  }
-}
-
-/**
  * Perform alterations on field_attach_view() or field_view_field().
  *
  * This hook is invoked after the field module has performed the operation.
@@ -556,637 +531,6 @@ function hook_field_storage_info_alter(&$info) {
 }
 
 /**
- * Reveal the internal details about the storage for a field.
- *
- * For example, an SQL storage module might return the Schema API structure for
- * the table. A key/value storage module might return the server name,
- * authentication credentials, and bin name.
- *
- * Field storage modules are not obligated to implement this hook. Modules that
- * rely on these details must only use them for read operations.
- *
- * @param $field
- *   A field structure.
- *
- * @return
- *   An array of details.
- *    - The first dimension is a store type (sql, solr, etc).
- *    - The second dimension indicates the age of the values in the store
- *      FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
- *    - Other dimensions are specific to the field storage module.
- *
- * @see hook_field_storage_details_alter()
- */
-function hook_field_storage_details($field) {
-  $details = array();
-
-  // Add field columns.
-  foreach ((array) $field['columns'] as $column_name => $attributes) {
-    $real_name = _field_sql_storage_columnname($field['field_name'], $column_name);
-    $columns[$column_name] = $real_name;
-  }
-  return array(
-    'sql' => array(
-      FIELD_LOAD_CURRENT => array(
-        _field_sql_storage_tablename($field) => $columns,
-      ),
-      FIELD_LOAD_REVISION => array(
-        _field_sql_storage_revision_tablename($field) => $columns,
-      ),
-    ),
-  );
-}
-
-/**
- * Perform alterations on Field API storage details.
- *
- * @param $details
- *   An array of storage details for fields as exposed by
- *   hook_field_storage_details() implementations.
- * @param $field
- *   A field structure.
- *
- * @see hook_field_storage_details()
- */
-function hook_field_storage_details_alter(&$details, $field) {
-  if ($field['field_name'] == 'field_of_interest') {
-    $columns = array();
-    foreach ((array) $field['columns'] as $column_name => $attributes) {
-      $columns[$column_name] = $column_name;
-    }
-    $details['drupal_variables'] = array(
-      FIELD_LOAD_CURRENT => array(
-        'moon' => $columns,
-      ),
-      FIELD_LOAD_REVISION => array(
-        'mars' => $columns,
-      ),
-    );
-  }
-}
-
-/**
- * Load field data for a set of entities.
- *
- * This hook is invoked from field_attach_load() to ask the field storage module
- * to load field data.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- *
- * @param $entity_type
- *   The type of entity, such as 'node' or 'user'.
- * @param $entities
- *   The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $fields
- *   An array listing the fields to be loaded. The keys of the array are field
- *   UUIDs, and the values of the array are the entity IDs (or revision IDs,
- *   depending on the $age parameter) to add each field to.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- *     fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_load($entity_type, $entities, $age, $fields, $options) {
-  $load_current = $age == FIELD_LOAD_CURRENT;
-
-  foreach ($fields as $field_id => $ids) {
-    // By the time this hook runs, the relevant field definitions have been
-    // populated and cached in FieldInfo, so calling field_info_field_by_id()
-    // on each field individually is more efficient than loading all fields in
-    // memory upfront with field_info_field_by_ids().
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field);
-
-    $query = db_select($table, 't')
-      ->fields('t')
-      ->condition('entity_type', $entity_type)
-      ->condition($load_current ? 'entity_id' : 'revision_id', $ids, 'IN')
-      ->condition('langcode', field_available_languages($entity_type, $field), 'IN')
-      ->orderBy('delta');
-
-    if (empty($options['deleted'])) {
-      $query->condition('deleted', 0);
-    }
-
-    $results = $query->execute();
-
-    $delta_count = array();
-    foreach ($results as $row) {
-      if (!isset($delta_count[$row->entity_id][$row->langcode])) {
-        $delta_count[$row->entity_id][$row->langcode] = 0;
-      }
-
-      if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
-        $item = array();
-        // For each column declared by the field, populate the item
-        // from the prefixed database column.
-        foreach ($field['columns'] as $column => $attributes) {
-          $column_name = _field_sql_storage_columnname($field_name, $column);
-          $item[$column] = $row->$column_name;
-        }
-
-        // Add the item to the field values for the entity.
-        $entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
-        $delta_count[$row->entity_id][$row->langcode]++;
-      }
-    }
-  }
-}
-
-/**
- * Write field data for an entity.
- *
- * This hook is invoked from field_attach_insert() and field_attach_update(), to
- * ask the field storage module to save field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $op
- *   FIELD_STORAGE_UPDATE when updating an existing entity,
- *   FIELD_STORAGE_INSERT when inserting a new entity.
- * @param $fields
- *   An array listing the fields to be written. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_write(\Drupal\Core\Entity\EntityInterface $entity, $op, $fields) {
-  $id = $entity->id();
-  $vid = $entity->getRevisionId();
-  $bundle = $entity->bundle();
-  if (!isset($vid)) {
-    $vid = $id;
-  }
-
-  foreach ($fields as $field_id) {
-    $field = field_info_field_by_id($field_id);
-    $field_name = $field['field_name'];
-    $table_name = _field_sql_storage_tablename($field);
-    $revision_name = _field_sql_storage_revision_tablename($field);
-
-    $all_langcodes = field_available_languages($entity->entityType(), $field);
-    $field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
-
-    // Delete and insert, rather than update, in case a value was added.
-    if ($op == FIELD_STORAGE_UPDATE) {
-      // Delete language codes present in the incoming $entity->$field_name.
-      // Delete all language codes if $entity->$field_name is empty.
-      $langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
-      if ($langcodes) {
-        db_delete($table_name)
-          ->condition('entity_type', $entity->entityType())
-          ->condition('entity_id', $id)
-          ->condition('langcode', $langcodes, 'IN')
-          ->execute();
-        db_delete($revision_name)
-          ->condition('entity_type', $entity->entityType())
-          ->condition('entity_id', $id)
-          ->condition('revision_id', $vid)
-          ->condition('langcode', $langcodes, 'IN')
-          ->execute();
-      }
-    }
-
-    // Prepare the multi-insert query.
-    $do_insert = FALSE;
-    $columns = array('entity_type', 'entity_id', 'revision_id', 'bundle', 'delta', 'langcode');
-    foreach ($field['columns'] as $column => $attributes) {
-      $columns[] = _field_sql_storage_columnname($field_name, $column);
-    }
-    $query = db_insert($table_name)->fields($columns);
-    $revision_query = db_insert($revision_name)->fields($columns);
-
-    foreach ($field_langcodes as $langcode) {
-      $items = (array) $entity->{$field_name}[$langcode];
-      $delta_count = 0;
-      foreach ($items as $delta => $item) {
-        // We now know we have someting to insert.
-        $do_insert = TRUE;
-        $record = array(
-          'entity_type' => $entity->entityType(),
-          'entity_id' => $id,
-          'revision_id' => $vid,
-          'bundle' => $bundle,
-          'delta' => $delta,
-          'langcode' => $langcode,
-        );
-        foreach ($field['columns'] as $column => $attributes) {
-          $record[_field_sql_storage_columnname($field_name, $column)] = isset($item[$column]) ? $item[$column] : NULL;
-        }
-        $query->values($record);
-        if (isset($vid)) {
-          $revision_query->values($record);
-        }
-
-        if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
-          break;
-        }
-      }
-    }
-
-    // Execute the query if we have values to insert.
-    if ($do_insert) {
-      $query->execute();
-      $revision_query->execute();
-    }
-  }
-}
-
-/**
- * Delete all field data for an entity.
- *
- * This hook is invoked from field_attach_delete() to ask the field storage
- * module to delete field data.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $fields
- *   An array listing the fields to delete. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_delete(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    if (isset($fields[$instance['field_id']])) {
-      $field = field_info_field_by_id($instance['field_id']);
-      field_sql_storage_field_storage_purge($entity, $field, $instance);
-    }
-  }
-}
-
-/**
- * Delete a single revision of field data for an entity.
- *
- * This hook is invoked from field_attach_delete_revision() to ask the field
- * storage module to delete field revision data.
- *
- * Deleting the current (most recently written) revision is not
- * allowed as has undefined results.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity on which to operate.
- * @param $fields
- *   An array listing the fields to delete. The keys and values of the
- *   array are field UUIDs.
- */
-function hook_field_storage_delete_revision(\Drupal\Core\Entity\EntityInterface $entity, $fields) {
-  $vid = $entity->getRevisionId();
-  if (isset($vid)) {
-    foreach ($fields as $field_id) {
-      $field = field_info_field_by_id($field_id);
-      $revision_name = _field_sql_storage_revision_tablename($field);
-      db_delete($revision_name)
-        ->condition('entity_type', $entity_type)
-        ->condition('entity_id', $entity->id())
-        ->condition('revision_id', $vid)
-        ->execute();
-    }
-  }
-}
-
-/**
- * Execute a Drupal\Core\Entity\EntityFieldQuery.
- *
- * This hook is called to find the entities having certain entity and field
- * conditions and sort them in the given field order. If the field storage
- * engine also handles property sorts and orders, it should unset those
- * properties in the called object to signal that those have been handled.
- *
- * @param Drupal\Core\Entity\EntityFieldQuery $query
- *   An EntityFieldQuery.
- *
- * @return
- *   See Drupal\Core\Entity\EntityFieldQuery::execute() for the return values.
- */
-function hook_field_storage_query($query) {
-  $groups = array();
-  if ($query->age == FIELD_LOAD_CURRENT) {
-    $tablename_function = '_field_sql_storage_tablename';
-    $id_key = 'entity_id';
-  }
-  else {
-    $tablename_function = '_field_sql_storage_revision_tablename';
-    $id_key = 'revision_id';
-  }
-  $table_aliases = array();
-  // Add tables for the fields used.
-  foreach ($query->fields as $key => $field) {
-    $tablename = $tablename_function($field);
-    // Every field needs a new table.
-    $table_alias = $tablename . $key;
-    $table_aliases[$key] = $table_alias;
-    if ($key) {
-      $select_query->join($tablename, $table_alias, "$table_alias.entity_type = $field_base_table.entity_type AND $table_alias.$id_key = $field_base_table.$id_key");
-    }
-    else {
-      $select_query = db_select($tablename, $table_alias);
-      $select_query->addTag('entity_field_access');
-      $select_query->addMetaData('base_table', $tablename);
-      $select_query->fields($table_alias, array('entity_type', 'entity_id', 'revision_id', 'bundle'));
-      $field_base_table = $table_alias;
-    }
-    if ($field['cardinality'] != 1) {
-      $select_query->distinct();
-    }
-  }
-
-  // Add field conditions.
-  foreach ($query->fieldConditions as $key => $condition) {
-    $table_alias = $table_aliases[$key];
-    $field = $condition['field'];
-    // Add the specified condition.
-    $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $condition['column']);
-    $query->addCondition($select_query, $sql_field, $condition);
-    // Add delta / language group conditions.
-    foreach (array('delta', 'langcode') as $column) {
-      if (isset($condition[$column . '_group'])) {
-        $group_name = $condition[$column . '_group'];
-        if (!isset($groups[$column][$group_name])) {
-          $groups[$column][$group_name] = $table_alias;
-        }
-        else {
-          $select_query->where("$table_alias.$column = " . $groups[$column][$group_name] . ".$column");
-        }
-      }
-    }
-  }
-
-  if (isset($query->deleted)) {
-    $select_query->condition("$field_base_table.deleted", (int) $query->deleted);
-  }
-
-  // Is there a need to sort the query by property?
-  $has_property_order = FALSE;
-  foreach ($query->order as $order) {
-    if ($order['type'] == 'property') {
-      $has_property_order = TRUE;
-    }
-  }
-
-  if ($query->propertyConditions || $has_property_order) {
-    if (empty($query->entityConditions['entity_type']['value'])) {
-      throw new EntityFieldQueryException('Property conditions and orders must have an entity type defined.');
-    }
-    $entity_type = $query->entityConditions['entity_type']['value'];
-    $entity_base_table = _field_sql_storage_query_join_entity($select_query, $entity_type, $field_base_table);
-    $query->entityConditions['entity_type']['operator'] = '=';
-    foreach ($query->propertyConditions as $property_condition) {
-      $query->addCondition($select_query, "$entity_base_table." . $property_condition['column'], $property_condition);
-    }
-  }
-  foreach ($query->entityConditions as $key => $condition) {
-    $query->addCondition($select_query, "$field_base_table.$key", $condition);
-  }
-
-  // Order the query.
-  foreach ($query->order as $order) {
-    if ($order['type'] == 'entity') {
-      $key = $order['specifier'];
-      $select_query->orderBy("$field_base_table.$key", $order['direction']);
-    }
-    elseif ($order['type'] == 'field') {
-      $specifier = $order['specifier'];
-      $field = $specifier['field'];
-      $table_alias = $table_aliases[$specifier['index']];
-      $sql_field = "$table_alias." . _field_sql_storage_columnname($field['field_name'], $specifier['column']);
-      $select_query->orderBy($sql_field, $order['direction']);
-    }
-    elseif ($order['type'] == 'property') {
-      $select_query->orderBy("$entity_base_table." . $order['specifier'], $order['direction']);
-    }
-  }
-
-  return $query->finishQuery($select_query, $id_key);
-}
-
-/**
- * Act on creation of a new field.
- *
- * This hook is invoked during the creation of a field to ask the field storage
- * module to save field information and prepare for storing field instances. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- *   The field structure being created.
- */
-function hook_field_storage_create_field($field) {
-  $schema = _field_sql_storage_schema($field);
-  foreach ($schema as $name => $table) {
-    db_create_table($name, $table);
-  }
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Update the storage information for a field.
- *
- * This is invoked on the field's storage module when updating the field,
- * before the new definition is saved to the database. The field storage module
- * should update its storage tables according to the new field definition. If
- * there is a problem, the field storage module should throw an exception.
- *
- * @param $field
- *   The updated field structure to be saved.
- * @param $prior_field
- *   The previously-saved field structure.
- */
-function hook_field_storage_update_field($field, $prior_field) {
-  if (!$field->hasData()) {
-    // There is no data. Re-create the tables completely.
-    $prior_schema = _field_sql_storage_schema($prior_field);
-    foreach ($prior_schema as $name => $table) {
-      db_drop_table($name, $table);
-    }
-    $schema = _field_sql_storage_schema($field);
-    foreach ($schema as $name => $table) {
-      db_create_table($name, $table);
-    }
-  }
-  else {
-    // There is data. See field_sql_storage_field_storage_update_field() for
-    // an example of what to do to modify the schema in place, preserving the
-    // old data as much as possible.
-  }
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field.
- *
- * This hook is invoked during the deletion of a field to ask the field storage
- * module to mark all information stored in the field for deletion.
- *
- * @param $field
- *   The field being deleted.
- */
-function hook_field_storage_delete_field($field) {
-  // Mark all data associated with the field for deletion.
-  $field['deleted'] = FALSE;
-  $table = _field_sql_storage_tablename($field);
-  $revision_table = _field_sql_storage_revision_tablename($field);
-  db_update($table)
-    ->fields(array('deleted' => 1))
-    ->execute();
-
-  // Move the table to a unique name while the table contents are being deleted.
-  $field['deleted'] = TRUE;
-  $new_table = _field_sql_storage_tablename($field);
-  $revision_new_table = _field_sql_storage_revision_tablename($field);
-  db_rename_table($table, $new_table);
-  db_rename_table($revision_table, $revision_new_table);
-  drupal_get_schema(NULL, TRUE);
-}
-
-/**
- * Act on deletion of a field instance.
- *
- * This hook is invoked during the deletion of a field instance to ask the
- * field storage module to mark all information stored for the field instance
- * for deletion.
- *
- * @param $instance
- *   The instance being deleted.
- */
-function hook_field_storage_delete_instance($instance) {
-  $field = field_info_field($instance['field_name']);
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_update($table_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-  db_update($revision_name)
-    ->fields(array('deleted' => 1))
-    ->condition('entity_type', $instance['entity_type'])
-    ->condition('bundle', $instance['bundle'])
-    ->execute();
-}
-
-/**
- * Act before the storage backends load field data.
- *
- * This hook allows modules to load data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * This lets 3rd party modules override, mirror, share, or otherwise store a
- * subset of fields in a different way than the current storage engine. Possible
- * use cases include per-bundle storage, per-combo-field storage, etc.
- *
- * Modules implementing this hook should load field values and add them to
- * objects in $entities. Fields with no values should be added as empty arrays.
- * In addition, fields loaded should be added as keys to $skip_fields.
- *
- * @param $entity_type
- *   The type of entity, such as 'node' or 'user'.
- * @param $entities
- *   The array of entity objects to add fields to, keyed by entity ID.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been loaded and
- *   therefore should not be loaded again. Add a key to this array to indicate
- *   that your module has already loaded a field.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - field_id: The field UUID that should be loaded. If unset, all fields
- *     should be loaded.
- *   - deleted: If TRUE, deleted fields should be loaded as well as non-deleted
- *     fields. If unset or FALSE, only non-deleted fields should be loaded.
- */
-function hook_field_storage_pre_load($entity_type, $entities, $age, &$skip_fields, $options) {
-  // @todo Needs function body.
-}
-
-/**
- * Act before the storage backends insert field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been written and
- *   therefore should not be written again. The values associated with these
- *   keys are not specified.
- * @return
- *   Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_insert(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
-  if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
-    $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-    foreach ($entity->taxonomy_forums as $language) {
-      foreach ($language as $delta) {
-        $query->values(array(
-          'nid' => $entity->nid,
-          'title' => $entity->title,
-          'tid' => $delta['value'],
-          'sticky' => $entity->sticky,
-          'created' => $entity->created,
-          'comment_count' => 0,
-          'last_comment_timestamp' => $entity->created,
-        ));
-      }
-    }
-    $query->execute();
-  }
-}
-
-/**
- * Act before the storage backends update field data.
- *
- * This hook allows modules to store data before the Field Storage API,
- * optionally preventing the field storage module from doing so.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @param $skip_fields
- *   An array keyed by field UUIDs whose data has already been written and
- *   therefore should not be written again. The values associated with these
- *   keys are not specified.
- * @return
- *   Saved field UUIDs are set as keys in $skip_fields.
- */
-function hook_field_storage_pre_update(\Drupal\Core\Entity\EntityInterface $entity, &$skip_fields) {
-  $first_call = &drupal_static(__FUNCTION__, array());
-
-  if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
-    // We don't maintain data for old revisions, so clear all previous values
-    // from the table. Since this hook runs once per field, per entity, make
-    // sure we only wipe values once.
-    if (!isset($first_call[$entity->nid])) {
-      $first_call[$entity->nid] = FALSE;
-      db_delete('forum_index')->condition('nid', $entity->nid)->execute();
-    }
-    // Only save data to the table if the node is published.
-    if ($entity->status) {
-      $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
-      foreach ($entity->taxonomy_forums as $language) {
-        foreach ($language as $delta) {
-          $query->values(array(
-            'nid' => $entity->nid,
-            'title' => $entity->title,
-            'tid' => $delta['value'],
-            'sticky' => $entity->sticky,
-            'created' => $entity->created,
-            'comment_count' => 0,
-            'last_comment_timestamp' => $entity->created,
-          ));
-        }
-      }
-      $query->execute();
-      // The logic for determining last_comment_count is fairly complex, so
-      // call _forum_update_forum_index() too.
-      _forum_update_forum_index($entity->nid);
-    }
-  }
-}
-
-/**
  * Returns the maximum weight for the entity components handled by the module.
  *
  * Field API takes care of fields and 'extra_fields'. This hook is intended for
@@ -1300,63 +644,6 @@ function hook_field_purge_instance($instance) {
 }
 
 /**
- * Remove field storage information when a field record is purged.
- *
- * Called from field_purge_field() to allow the field storage module to remove
- * field information when a field is being purged.
- *
- * @param $field
- *   The field being purged.
- */
-function hook_field_storage_purge_field($field) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_drop_table($table_name);
-  db_drop_table($revision_name);
-}
-
-/**
- * Remove field storage information when a field instance is purged.
- *
- * Called from field_purge_instance() to allow the field storage module to
- * remove field instance information when a field instance is being purged.
- *
- * @param $instance
- *   The instance being purged.
- */
-function hook_field_storage_purge_field_instance($instance) {
-  db_delete('my_module_field_instance_info')
-    ->condition('id', $instance['id'])
-    ->execute();
-}
-
-/**
- * Remove field storage information when field data is purged.
- *
- * Called from field_purge_data() to allow the field storage module to delete
- * field data information.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data to delete.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- */
-function hook_field_storage_purge(\Drupal\Core\Entity\EntityInterface $entity, $field, $instance) {
-  $table_name = _field_sql_storage_tablename($field);
-  $revision_name = _field_sql_storage_revision_tablename($field);
-  db_delete($table_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-  db_delete($revision_name)
-    ->condition('entity_type', $entity->entityType())
-    ->condition('entity_id', $entity->id())
-    ->execute();
-}
-
-/**
  * @} End of "addtogroup field_crud".
  */
 
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index ff28636..7175960 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -324,8 +324,7 @@ function field_invoke_method_multiple($method, $target_function, array $entities
     // fields with an empty array (those are not equivalent on update).
     foreach ($grouped_entities[$instance_id] as $langcode => $entities) {
       foreach ($entities as $id => $entity) {
-        if ($grouped_items[$instance_id][$langcode][$id] !== array() || isset($entity->{$field_name}[$langcode])) {
-          $entity->{$field_name}[$langcode] = $grouped_items[$instance_id][$langcode][$id];
+        if ($grouped_items[$instance_id][$langcode][$id] !== array() || isset($entity->{$field_name}[$langcode])) {     $entity->{$field_name}[$langcode] = $grouped_items[$instance_id][$langcode][$id];
         }
       }
     }
@@ -538,213 +537,6 @@ function field_attach_form(EntityInterface $entity, &$form, &$form_state, $langc
 }
 
 /**
- * Loads fields for the current revisions of a group of entities.
- *
- * Loads all fields for each entity object in a group of a single entity type.
- * The loaded field values are added directly to the entity objects.
- *
- * field_attach_load() is automatically called by the default entity controller
- * class, and thus, in most cases, doesn't need to be explicitly called by the
- * entity type module.
- *
- * @param $entity_type
- *   The type of entities in $entities; e.g., 'node' or 'user'.
- * @param $entities
- *   An array of entities for which to load fields, keyed by entity ID. Each
- *   entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- *   filled in. The function adds the loaded field data directly in the entity
- *   objects of the $entities array.
- * @param $age
- *   FIELD_LOAD_CURRENT to load the most recent revision for all fields, or
- *   FIELD_LOAD_REVISION to load the version indicated by each entity. Defaults
- *   to FIELD_LOAD_CURRENT; use field_attach_load_revision() instead of passing
- *   FIELD_LOAD_REVISION.
- * @param $options
- *   An associative array of additional options, with the following keys:
- *   - instance: A field instance entity, If provided, only values for the
- *     corresponding field will be loaded, and no cache is written. This
- *     option is only supported when all $entities are within the same bundle.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $options = array()) {
-  $load_current = $age == FIELD_LOAD_CURRENT;
-  $load_deleted = !empty($options['instance']->deleted);
-
-  // Merge default options.
-  $options += array('instance' => NULL);
-  // Set options for hook invocations.
-  $hook_options = array(
-    'deleted' => $load_deleted,
-  );
-  if ($options['instance']) {
-    $hook_options['field_id'] = $options['instance']->field_uuid;
-  }
-
-  $info = entity_get_info($entity_type);
-  // Only the most current revision of non-deleted fields for cacheable entity
-  // types can be cached.
-  $cache_read = $load_current && $info['field_cache'] && !$load_deleted;
-  // In addition, do not write to the cache when loading a single field.
-  $cache_write = $cache_read && !isset($options['instance']);
-
-  if (empty($entities)) {
-    return;
-  }
-
-  // Ensure we are working with a BC mode entity.
-  foreach ($entities as $id => $entity) {
-    $entities[$id] = $entity->getBCEntity();
-  }
-
-  // Assume all entities will need to be queried. Entities found in the cache
-  // will be removed from the list.
-  $queried_entities = $entities;
-
-  // Fetch available entities from cache, if applicable.
-  if ($cache_read) {
-    // Build the list of cache entries to retrieve.
-    $cids = array();
-    foreach ($entities as $id => $entity) {
-      $cids[] = "field:$entity_type:$id";
-    }
-    $cache = cache('field')->getMultiple($cids);
-    // Put the cached field values back into the entities and remove them from
-    // the list of entities to query.
-    foreach ($entities as $id => $entity) {
-      $cid = "field:$entity_type:$id";
-      if (isset($cache[$cid])) {
-        unset($queried_entities[$id]);
-        foreach ($cache[$cid]->data as $field_name => $values) {
-          $entity->$field_name = $values;
-        }
-      }
-    }
-  }
-
-  // Fetch other entities from their storage location.
-  if ($queried_entities) {
-    // The invoke order is:
-    // - hook_field_storage_pre_load()
-    // - storage backend's hook_field_storage_load()
-    // - Field class's prepareCache() method.
-    // - hook_field_attach_load()
-
-    // Invoke hook_field_storage_pre_load(): let any module load field
-    // data before the storage engine, accumulating along the way.
-    $skip_fields = array();
-    foreach (module_implements('field_storage_pre_load') as $module) {
-      $function = $module . '_field_storage_pre_load';
-      $function($entity_type, $queried_entities, $age, $skip_fields, $hook_options);
-    }
-
-    // Collect the storage backends used by the remaining fields in the entities.
-    $storages = array();
-    foreach ($queried_entities as $entity) {
-      $id = $entity->id();
-      $vid = $entity->getRevisionId();
-
-      // Determine the list of field instances to work on.
-      if ($options['instance']) {
-        $instances = array($options['instance']);
-      }
-      else {
-        $instances = field_info_instances($entity_type, $entity->bundle());
-      }
-
-      foreach ($instances as $instance) {
-        $field = $instance->getField();
-        $field_name = $field->id();
-        if (!isset($queried_entities[$id]->{$field_name})) {
-          $queried_entities[$id]->{$field_name} = array();
-        }
-        if (!isset($skip_fields[$field->uuid])) {
-          $storages[$field->storage['type']][$field->uuid][] = $load_current ? $id : $vid;
-        }
-      }
-    }
-
-    // Invoke hook_field_storage_load() on the relevant storage backends.
-    foreach ($storages as $storage => $fields) {
-      $storage_info = field_info_storage_types($storage);
-      module_invoke($storage_info['module'], 'field_storage_load', $entity_type, $queried_entities, $age, $fields, $hook_options);
-    }
-
-    // Invoke the field type's prepareCache() method.
-    if (empty($options['instance'])) {
-      foreach ($queried_entities as $entity) {
-        \Drupal::entityManager()
-          ->getStorageController($entity_type)
-          ->invokeFieldItemPrepareCache($entity);
-      }
-    }
-    else {
-      // Do not rely on invokeFieldItemPrepareCache(), which only works on
-      // fields listed in getFieldDefinitions(), and will fail if we are loading
-      // values for a deleted field. Instead, generate FieldItem objects
-      // directly, and call their prepareCache() method.
-      foreach ($queried_entities as $entity) {
-        $field = $options['instance']->getField();
-        $field_name = $field->id();
-        // Call the prepareCache() method on each item.
-        foreach ($entity->{$field_name} as $langcode => $values) {
-          $definition = _field_generate_entity_field_definition($field, $options['instance']);
-          $items = \Drupal::typedData()->create($definition, $values, $field_name, $entity);
-          foreach ($items as $item) {
-            $item->prepareCache();
-          }
-          $entity->{$field_name}[$langcode] = $items->getValue();
-        }
-      }
-    }
-
-    // Invoke hook_field_attach_load(): let other modules act on loading the
-    // entity.
-    module_invoke_all('field_attach_load', $entity_type, $queried_entities, $age, $options);
-
-    // Build cache data.
-    if ($cache_write) {
-      foreach ($queried_entities as $id => $entity) {
-        $data = array();
-        $instances = field_info_instances($entity_type, $entity->bundle());
-        foreach ($instances as $instance) {
-          $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
-        }
-        $cid = "field:$entity_type:$id";
-        cache('field')->set($cid, $data);
-      }
-    }
-  }
-}
-
-/**
- * Loads all fields for previous versions of a group of entities.
- *
- * Loading different versions of the same entities is not supported, and should
- * be done by separate calls to the function.
- *
- * field_attach_load_revision() is automatically called by the default entity
- * controller class, and thus, in most cases, doesn't need to be explicitly
- * called by the entity type module.
- *
- * @param $entity_type
- *   The type of entities in $entities; e.g. 'node' or 'user'.
- * @param $entities
- *   An array of entities for which to load fields, keyed by entity ID. Each
- *   entity needs to have its 'bundle', 'id' and (if applicable) 'revision' keys
- *   filled. The function adds the loaded field data directly in the entity
- *   objects of the $entities array.
- * @param $options
- *   An associative array of additional options. See field_attach_load() for
- *   details.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_load_revision($entity_type, $entities, $options = array()) {
-  return field_attach_load($entity_type, $entities, FIELD_LOAD_REVISION, $options);
-}
-
-/**
  * Performs field validation against form-submitted field values.
  *
  * There are two levels of validation for fields in forms: widget validation and
@@ -841,159 +633,6 @@ function field_attach_extract_form_values(EntityInterface $entity, $form, &$form
 }
 
 /**
- * Save field data for a new entity.
- *
- * The passed-in entity must already contain its id and (if applicable)
- * revision id attributes.
- * Default values (if any) will be saved for fields not present in the
- * $entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- * @return
- *   Default values (if any) will be added to the $entity parameter for fields
- *   it leaves unspecified.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_insert(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Let any module insert field data before the storage engine, accumulating
-  // saved fields along the way.
-  $skip_fields = array();
-  foreach (module_implements('field_storage_pre_insert') as $module) {
-    $function = $module . '_field_storage_pre_insert';
-    $function($entity, $skip_fields);
-  }
-
-  // Collect the storage backends used by the remaining fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $field_name = $field['field_name'];
-    if (!empty($entity->$field_name)) {
-      // Collect the storage backend if the field has not been written yet.
-      if (!isset($skip_fields[$field_id])) {
-        $storages[$field['storage']['type']][$field_id] = $field_id;
-      }
-    }
-  }
-
-  // Field storage backends save any remaining unsaved fields.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_INSERT, $fields);
-  }
-}
-
-/**
- * Saves field data for an existing entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_update(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Let any module update field data before the storage engine, accumulating
-  // saved fields along the way.
-  $skip_fields = array();
-  foreach (module_implements('field_storage_pre_update') as $module) {
-    $function = $module . '_field_storage_pre_update';
-    $function($entity, $skip_fields);
-  }
-
-  // Collect the storage backends used by the remaining fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    // Collect the storage backend if the field has not been written yet.
-    if (!isset($skip_fields[$field_id])) {
-      $storages[$field['storage']['type']][$field_id] = $field_id;
-    }
-  }
-
-  // Field storage backends save any remaining unsaved fields.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_write', $entity, FIELD_STORAGE_UPDATE, $fields);
-  }
-
-  $entity_info = $entity->entityInfo();
-  if ($entity_info['field_cache']) {
-    cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-  }
-}
-
-/**
- * Deletes field data for an existing entity. This deletes all revisions of
- * field data for the entity.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity whose field data to delete.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Collect the storage backends used by the fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $storages[$field['storage']['type']][$field_id] = $field_id;
-  }
-
-  // Field storage backends delete their data.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_delete', $entity, $fields);
-  }
-
-  $entity_info = $entity->entityInfo();
-  if ($entity_info['field_cache']) {
-    cache('field')->delete('field:' . $entity->entityType() . ':' . $entity->id());
-  }
-}
-
-/**
- * Delete field data for a single revision of an existing entity. The passed
- * entity must have a revision ID attribute.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The entity with fields to save.
- *
- * @deprecated as of Drupal 8.0. Use the entity system instead.
- */
-function field_attach_delete_revision(EntityInterface $entity) {
-  // Ensure we are working with a BC mode entity.
-  $entity = $entity->getBCEntity();
-
-  // Collect the storage backends used by the fields in the entities.
-  $storages = array();
-  foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
-    $field = field_info_field_by_id($instance['field_id']);
-    $field_id = $field['uuid'];
-    $storages[$field['storage']['type']][$field_id] = $field_id;
-  }
-
-  // Field storage backends delete their data.
-  foreach ($storages as $storage => $fields) {
-    $storage_info = field_info_storage_types($storage);
-    module_invoke($storage_info['module'], 'field_storage_delete_revision', $entity, $fields);
-  }
-}
-
-/**
  * Prepares field data prior to display.
  *
  * This function lets field types and formatters load additional data needed for
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index ed129df..73f00a6 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -266,19 +266,8 @@ function field_purge_batch($batch_size) {
     $results = $query->execute();
 
     if ($results) {
-      $entities = array();
-      foreach ($results as $revision_id => $entity_id) {
-        // This might not be the revision id if the entity type does not support
-        // revisions but _field_create_entity_from_ids() checks that and
-        // disregards this value so there is no harm setting it.
-        $ids->revision_id = $revision_id;
-        $ids->entity_id = $entity_id;
-        $entities[$entity_id] = _field_create_entity_from_ids($ids);
-      }
-      field_attach_load($entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
-      foreach ($entities as $entity) {
-        // Purge the data for the entity.
-        field_purge_data($entity, $field, $instance);
+      foreach ($results as $entity_id) {
+        Drupal::entityManager()->getStorageController($instance->entity_type)->fieldPurgeData($entity_id, $field, $instance);
       }
     }
     else {
@@ -299,37 +288,6 @@ function field_purge_batch($batch_size) {
 }
 
 /**
- * Purges the field data for a single field on a single pseudo-entity.
- *
- * This is basically the same as field_attach_delete() except it only applies to
- * a single field. The entity itself is not being deleted, and it is quite
- * possible that other field data will remain attached to it.
- *
- * @param \Drupal\Core\Entity\EntityInterface $entity
- *   The pseudo-entity whose field data is being purged.
- * @param $field
- *   The (possibly deleted) field whose data is being purged.
- * @param $instance
- *   The deleted field instance whose data is being purged.
- */
-function field_purge_data(EntityInterface $entity, $field, $instance) {
-  foreach ($entity->{$field->id()} as $value) {
-    $definition = _field_generate_entity_field_definition($field, $instance);
-    $items = \Drupal::typedData()->create($definition, $value, $field->id(), $entity);
-    $items->delete();
-  }
-
-  // Tell the field storage system to purge the data.
-  module_invoke($field['storage']['module'], 'field_storage_purge', $entity, $field, $instance);
-
-  // Let other modules act on purging the data.
-  foreach (module_implements('field_attach_purge') as $module) {
-    $function = $module . '_field_attach_purge';
-    $function($entity, $field, $instance);
-  }
-}
-
-/**
  * Purges a field instance record from the database.
  *
  * This function assumes all data for the instance has already been purged and
diff --git a/core/modules/field/field.info.inc b/core/modules/field/field.info.inc
index 932f6b2..e935657 100644
--- a/core/modules/field/field.info.inc
+++ b/core/modules/field/field.info.inc
@@ -42,77 +42,10 @@ function field_info_cache_clear() {
   Drupal::typedData()->clearCachedDefinitions();
   Drupal::service('plugin.manager.entity.field.field_type')->clearCachedDefinitions();
 
-  _field_info_collate_types_reset();
   Field::fieldInfo()->flush();
 }
 
 /**
- * Collates all information on field types, widget types and related structures.
- *
- * @return
- *   An associative array containing:
- *   - 'storage types': Array of hook_field_storage_info() results, keyed by
- *     storage type names. Each element has the following components: label,
- *     description, and settings from hook_field_storage_info(), as well as
- *     module, giving the module that exposes the storage type.
- *
- * @see _field_info_collate_types_reset()
- */
-function _field_info_collate_types() {
-  $language_interface = language(Language::TYPE_INTERFACE);
-
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['field_info_collate_types'] = &drupal_static(__FUNCTION__);
-  }
-  $info = &$drupal_static_fast['field_info_collate_types'];
-
-  // The _info() hooks invoked below include translated strings, so each
-  // language is cached separately.
-  $langcode = $language_interface->langcode;
-
-  if (!isset($info)) {
-    if ($cached = cache('field')->get("field_info_types:$langcode")) {
-      $info = $cached->data;
-    }
-    else {
-      $info = array(
-        'storage types' => array(),
-      );
-
-      // Populate storage types.
-      foreach (module_implements('field_storage_info') as $module) {
-        $storage_types = (array) module_invoke($module, 'field_storage_info');
-        foreach ($storage_types as $name => $storage_info) {
-          // Provide defaults.
-          $storage_info += array(
-            'settings' => array(),
-          );
-          $info['storage types'][$name] = $storage_info;
-          $info['storage types'][$name]['module'] = $module;
-        }
-      }
-      drupal_alter('field_storage_info', $info['storage types']);
-
-      cache('field')->set("field_info_types:$langcode", $info, CacheBackendInterface::CACHE_PERMANENT, array('field_info_types' => TRUE));
-    }
-  }
-
-  return $info;
-}
-
-/**
- * Clears collated information on field and widget types and related structures.
- */
-function _field_info_collate_types_reset() {
-  drupal_static_reset('_field_info_collate_types');
-  // Clear all languages.
-  cache('field')->deleteTags(array('field_info_types' => TRUE));
-}
-
-/**
  * Determines the behavior of a widget with respect to an operation.
  *
  * @param string $op
@@ -225,31 +158,6 @@ function field_info_formatter_types($formatter_type = NULL) {
 }
 
 /**
- * Returns information about field storage from hook_field_storage_info().
- *
- * @param $storage_type
- *   (optional) A storage type name. If omitted, all storage types will be
- *   returned.
- *
- * @return
- *   Either a storage type description, as provided by
- *   hook_field_storage_info(), or an array of all existing storage types, keyed
- *   by storage type name.
- */
-function field_info_storage_types($storage_type = NULL) {
-  $info = _field_info_collate_types();
-  $storage_types = $info['storage types'];
-  if ($storage_type) {
-    if (isset($storage_types[$storage_type])) {
-      return $storage_types[$storage_type];
-    }
-  }
-  else {
-    return $storage_types;
-  }
-}
-
-/**
  * Returns all field definitions.
  *
  * Use of this function should be avoided when possible, since it loads and
@@ -541,21 +449,5 @@ function field_info_formatter_settings($type) {
 }
 
 /**
- * Returns a field storage type's default settings.
- *
- * @param $type
- *   A field storage type name.
- *
- * @return
- *   The storage type's default settings, as provided by
- *   hook_field_storage_info(), or an empty array if type or settings are
- *   undefined.
- */
-function field_info_storage_settings($type) {
-  $info = field_info_storage_types($type);
-  return isset($info['settings']) ? $info['settings'] : array();
-}
-
-/**
  * @} End of "defgroup field_info".
  */
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index b425169..adc26a5 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -261,12 +261,6 @@ function field_update_8003() {
       'module' => $record['module'],
       'active' => $record['active'],
       'settings' => $record['data']['settings'],
-      'storage' => array(
-        'type' => $record['storage_type'],
-        'module' => $record['storage_module'],
-        'active' => $record['storage_active'],
-        'settings' => $record['data']['storage']['settings'],
-      ),
       'locked' => $record['locked'],
       'cardinality' => $record['cardinality'],
       'translatable' => $record['translatable'],
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 19bf6ed..05a675e 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -423,9 +423,8 @@ function field_sync_field_status() {
   foreach ($modules as $module => $module_info) {
     // Collect field types and storage backends exposed by the module.
     $field_types = (array) $module_handler->invoke($module, 'field_info');
-    $storage_types = (array) $module_handler->invoke($module, 'field_storage_info');
 
-    if ($field_types || $storage_types) {
+    if ($field_types) {
       foreach ($fields as $uuid => &$field) {
         // Associate field types.
         if (isset($field_types[$field['type']]) && ($field['module'] !== $module || !$field['active'])) {
@@ -433,26 +432,16 @@ function field_sync_field_status() {
           $field['active'] = TRUE;
           $changed[$uuid] = $field;
         }
-        // Associate storage backends.
-        if (isset($storage_types[$field['storage']['type']]) && ($field['storage']['module'] !== $module || !$field['storage']['active'])) {
-          $field['storage']['module'] = $module;
-          $field['storage']['active'] = TRUE;
-          $changed[$uuid] = $field;
-        }
       }
     }
   }
 
-  // Set fields with missing field type or storage modules to inactive.
+  // Set fields with missing field type modules to inactive.
   foreach ($fields as $uuid => &$field) {
     if (!isset($modules[$field['module']]) && $field['active']) {
       $field['active'] = FALSE;
       $changed[$uuid] = $field;
     }
-    if (!isset($modules[$field['storage']['module']]) && $field['storage']['active']) {
-      $field['storage']['active'] = FALSE;
-      $changed[$uuid] = $field;
-    }
   }
 
   // Store the updated field definitions.
@@ -464,8 +453,6 @@ function field_sync_field_status() {
       Drupal::config('field.field.' . $field['id'])
         ->set('module', $field['module'])
         ->set('active', $field['active'])
-        ->set('storage.module', $field['storage']['module'])
-        ->set('storage.active', $field['storage']['active'])
         ->save();
     }
   }
diff --git a/core/modules/field/field.views.inc b/core/modules/field/field.views.inc
index 5a1f4f3..e77beff 100644
--- a/core/modules/field/field.views.inc
+++ b/core/modules/field/field.views.inc
@@ -8,6 +8,8 @@
  */
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Implements hook_views_data().
@@ -18,7 +20,7 @@
 function field_views_data() {
   $data = array();
   foreach (field_info_fields() as $field) {
-    if ($field['storage']['type'] != 'field_sql_storage') {
+    if ($field->getStorageType() != 'sql') {
       continue;
     }
 
@@ -48,7 +50,7 @@ function field_views_data() {
  */
 function field_views_data_alter(&$data) {
   foreach (field_info_fields() as $field) {
-    if ($field['storage']['type'] != 'field_sql_storage') {
+    if ($field->getStorageType() != 'sql') {
       continue;
     }
 
@@ -89,7 +91,7 @@ function field_views_field_label($field_name) {
 /**
  * Default views data implementation for a field.
  */
-function field_views_field_default_views_data($field) {
+function field_views_field_default_views_data(Field $field) {
   $field_types = field_info_field_types();
 
   // Check the field module is available.
@@ -99,8 +101,8 @@ function field_views_field_default_views_data($field) {
 
   $data = array();
 
-  $current_table = _field_sql_storage_tablename($field);
-  $revision_table = _field_sql_storage_revision_tablename($field);
+  $current_table = DatabaseStorageController::fieldTableName($field);
+  $revision_table = DatabaseStorageController::fieldRevisionTableName($field);
 
   // The list of entity:bundle that this field is used in.
   $bundles_names = array();
@@ -172,7 +174,7 @@ function field_views_field_default_views_data($field) {
 
   $add_fields = array('delta', 'langcode', 'bundle');
   foreach ($field['columns'] as $column_name => $attributes) {
-    $add_fields[] = _field_sql_storage_columnname($field['field_name'], $column_name);
+    $add_fields[] = DatabaseStorageController::fieldColumnName($field->id(), $column_name);
   }
 
   // Note: we don't have a label available here, because we are at the field
@@ -296,10 +298,11 @@ function field_views_field_default_views_data($field) {
       else {
         $group = t('@group (historical data)', array('@group' => $group_name));
       }
-      $column_real_name = $field['storage_details']['sql'][$type][$table][$column];
+      $column_real_name = DatabaseStorageController::fieldColumnName($field->id(), $column);
 
       // Load all the fields from the table by default.
-      $additional_fields = array_values($field['storage_details']['sql'][$type][$table]);
+      $field_sql_schema = DatabaseStorageController::fieldSqlSchema($field);
+      $additional_fields = array_keys($field_sql_schema[$current_table]['fields']);
 
       $data[$table][$column_real_name] = array(
         'group' => $group,
diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php
index 349872e..1ae4a22 100644
--- a/core/modules/field/lib/Drupal/field/FieldInfo.php
+++ b/core/modules/field/lib/Drupal/field/FieldInfo.php
@@ -180,7 +180,7 @@ public function getFieldMap() {
     // Get active fields.
     foreach (config_get_storage_names_with_prefix('field.field') as $config_id) {
       $field_config = $this->config->get($config_id)->get();
-      if ($field_config['active'] && $field_config['storage']['active']) {
+      if ($field_config['active']) {
         $fields[$field_config['uuid']] = $field_config;
       }
     }
@@ -554,7 +554,6 @@ public function getBundleExtraFields($entity_type, $bundle) {
   public function prepareField($field) {
     // Make sure all expected field settings are present.
     $field['settings'] += field_info_field_settings($field['type']);
-    $field['storage']['settings'] += field_info_storage_settings($field['storage']['type']);
 
     return $field;
   }
diff --git a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
index 9535bee..ddcf131 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
@@ -135,7 +135,6 @@ public function loadByProperties(array $conditions = array()) {
     // Translate "do not include inactive fields" into actual conditions.
     if (!$include_inactive) {
       $conditions['field.active'] = TRUE;
-      $conditions['field.storage.active'] = TRUE;
     }
 
     // Collect matching instances.
@@ -161,10 +160,6 @@ public function loadByProperties(array $conditions = array()) {
             $checked_value = $field->active;
             break;
 
-          case 'field.storage.active':
-            $checked_value = $field->storage['active'];
-            break;
-
           case 'field_id':
             $checked_value = $instance->field_uuid;
             break;
diff --git a/core/modules/field/lib/Drupal/field/FieldInterface.php b/core/modules/field/lib/Drupal/field/FieldInterface.php
index 132bc39..8d7edd8 100644
--- a/core/modules/field/lib/Drupal/field/FieldInterface.php
+++ b/core/modules/field/lib/Drupal/field/FieldInterface.php
@@ -45,34 +45,6 @@ public function getSchema();
   public function getColumns();
 
   /**
-   * Returns information about how the storage backend stores the field data.
-   *
-   * The content of the returned value depends on the storage backend, and some
-   * storage backends might provide no information.
-   *
-   * It is strongly discouraged to use this information to perform direct write
-   * operations to the field data storage, bypassing the regular field saving
-   * APIs.
-   *
-   * Example return value for the default field_sql_storage backend:
-   * - 'sql'
-   *   - FIELD_LOAD_CURRENT
-   *     - Table name (string).
-   *       - Table schema (array)
-   *   - FIELD_LOAD_REVISION
-   *     - Table name (string).
-   *       - Table schema (array).
-   *
-   * @return array
-   *   The storage details.
-   *    - The first dimension is a store type (sql, solr, etc).
-   *    - The second dimension indicates the age of the values in the store
-   *      FIELD_LOAD_CURRENT or FIELD_LOAD_REVISION.
-   *    - Other dimensions are specific to the field storage backend.
-   */
-  public function getStorageDetails();
-
-  /**
    * Returns the list of bundles where the field has instances.
    *
    * @return array
diff --git a/core/modules/field/lib/Drupal/field/FieldStorageController.php b/core/modules/field/lib/Drupal/field/FieldStorageController.php
index 07c6c71..bd658aa 100644
--- a/core/modules/field/lib/Drupal/field/FieldStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldStorageController.php
@@ -118,7 +118,6 @@ public function loadByProperties(array $conditions = array()) {
     // Translate "do not include inactive instances" into actual conditions.
     if (!$include_inactive) {
       $conditions['active'] = TRUE;
-      $conditions['storage.active'] = TRUE;
     }
 
     // Collect matching fields.
@@ -127,10 +126,6 @@ public function loadByProperties(array $conditions = array()) {
       foreach ($conditions as $key => $value) {
         // Extract the actual value against which the condition is checked.
         switch ($key) {
-          case 'storage.active':
-            $checked_value = $field->storage['active'];
-            break;
-
           case 'field_name';
             $checked_value = $field->id;
             break;
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
index 4af0fc2..c4c7057 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
@@ -142,25 +142,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $locked = FALSE;
 
   /**
-   * The field storage definition.
-   *
-   * An array of key/value pairs identifying the storage backend to use for the
-   * field:
-   * - type: (string) The storage backend used by the field. Storage backends
-   *   are defined by modules that implement hook_field_storage_info().
-   * - settings: (array) A sub-array of key/value pairs of settings. The keys
-   *   and default values are defined by the storage backend in the 'settings'
-   *   entry of hook_field_storage_info().
-   * - module: (string, read-only) The name of the module that implements the
-   *   storage backend.
-   * - active: (integer, read-only) TRUE if the module that implements the
-   *   storage backend is currently enabled, FALSE otherwise.
-   *
-   * @var array
-   */
-  public $storage = array();
-
-  /**
    * The custom storage indexes for the field data storage.
    *
    * This set of indexes is merged with the "default" indexes specified by the
@@ -194,6 +175,14 @@ class Field extends ConfigEntityBase implements FieldInterface {
   public $deleted = FALSE;
 
   /**
+   * The storage type this field is in. Typically 'sql'.
+   *
+   * @var string
+   */
+  protected $storageType;
+
+
+  /**
    * The field schema.
    *
    * @var array
@@ -201,13 +190,6 @@ class Field extends ConfigEntityBase implements FieldInterface {
   protected $schema;
 
   /**
-   * The storage information for the field.
-   *
-   * @var array
-   */
-  protected $storageDetails;
-
-  /**
    * The original field.
    *
    * @var \Drupal\field\Plugin\Core\Entity\Field
@@ -270,11 +252,11 @@ public function getExportProperties() {
       'module',
       'active',
       'entity_types',
-      'storage',
       'locked',
       'cardinality',
       'translatable',
       'indexes',
+      'storageType',
     );
     $properties = array();
     foreach ($names as $name) {
@@ -296,7 +278,7 @@ public function getExportProperties() {
    */
   public function save() {
     // Clear the derived data about the field.
-    unset($this->schema, $this->storageDetails);
+    unset($this->schema);
 
     if ($this->isNew()) {
       return $this->saveNew();
@@ -318,7 +300,6 @@ public function save() {
    *   In case of failures at the configuration storage level.
    */
   protected function saveNew() {
-    $module_handler = \Drupal::moduleHandler();
     $entity_manager = \Drupal::entityManager();
     $storage_controller = $entity_manager->getStorageController($this->entityType);
 
@@ -363,24 +344,6 @@ protected function saveNew() {
     // definition is passed to the various hooks and written to config.
     $this->settings += $field_type['settings'];
 
-    // Provide default storage.
-    $this->storage += array(
-      'type' => variable_get('field_storage_default', 'field_sql_storage'),
-      'settings' => array(),
-    );
-    // Check that the storage type is known.
-    $storage_type = field_info_storage_types($this->storage['type']);
-    if (!$storage_type) {
-      throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array('%type' => $this->storage['type'])));
-    }
-    $this->storage['module'] = $storage_type['module'];
-    $this->storage['active'] = TRUE;
-    // Provide default storage settings.
-    $this->storage['settings'] += $storage_type['settings'];
-
-    // Invoke the storage backend's hook_field_storage_create_field().
-    $module_handler->invoke($this->storage['module'], 'field_storage_create_field', array($this));
-
     // Save the configuration.
     $result = parent::save();
     field_cache_clear();
@@ -400,7 +363,6 @@ protected function saveNew() {
    *   In case of failures at the configuration storage level.
    */
   protected function saveUpdated() {
-    $module_handler = \Drupal::moduleHandler();
     $storage_controller = \Drupal::entityManager()->getStorageController($this->entityType);
 
     $original = $storage_controller->loadUnchanged($this->id());
@@ -413,9 +375,6 @@ protected function saveUpdated() {
     if ($this->entity_types != $original->entity_types) {
       throw new FieldException("Cannot change an existing field's entity_types property.");
     }
-    if ($this->storage['type'] != $original->storage['type']) {
-      throw new FieldException("Cannot change an existing field's storage type.");
-    }
 
     // Make sure all settings are present, so that a complete field definition
     // is saved. This allows calling code to perform partial updates on field
@@ -424,13 +383,14 @@ protected function saveUpdated() {
 
     // See if any module forbids the update by throwing an exception. This
     // invokes hook_field_update_forbid().
-    $module_handler->invokeAll('field_update_forbid', array($this, $original));
+    if ($entity_types = array_keys($this->getBundles())) {
+      $data_storage_controller = \Drupal::entityManager()->getStorageController($entity_types[0]);
+      // Tell the storage engine to update the field. The storage engine can
+      // reject the definition update as invalid by raising an exception,
+      // which stops execution before the definition is written to config.
+      $data_storage_controller->handleUpdateField($this, $original);
+    }
 
-    // Tell the storage engine to update the field by invoking the
-    // hook_field_storage_update_field(). The storage engine can reject the
-    // definition update as invalid by raising an exception, which stops
-    // execution before the definition is written to config.
-    $module_handler->invoke($this->storage['module'], 'field_storage_update_field', array($this, $original));
 
     // Save the configuration.
     $result = parent::save();
@@ -440,13 +400,23 @@ protected function saveUpdated() {
   }
 
   /**
-   * {@inheritdoc}
+   * Overrides \Drupal\Core\Entity\Entity::delete().
+   *
+   * @param string $last_entity_type
+   *   (internal) The field data storage is handled by an entity storage
+   *   controller. In order to get this controller, an entity type is
+   *   necessary. If the field being deleted has no instances, the field
+   *   object itself can not find the entity type it was previously attached
+   *   to and so for example the SQL storage table would not be able to drop
+   *   the field table. To avoid this problem, when the last instance is
+   *   deleted \Drupal\Core\Entity\FieldInstance::delete() passes in the
+   *   entity type in this argument.
    */
-  public function delete() {
+  public function delete($last_entity_type = '') {
     if (!$this->deleted) {
-      $module_handler = \Drupal::moduleHandler();
       $instance_controller = \Drupal::entityManager()->getStorageController('field_instance');
       $state = \Drupal::state();
+      $entity_type = '';
 
       // Delete all non-deleted instances.
       $instance_ids = array();
@@ -463,9 +433,14 @@ public function delete() {
         $instance->delete(FALSE);
       }
 
-      // Mark field data for deletion by invoking
-      // hook_field_storage_delete_field().
-      $module_handler->invoke($this->storage['module'], 'field_storage_delete_field', array($this));
+      if ($this->storageType) {
+        // Either the loop iterating $this->getBundles() above have found
+        // instances and then $entity_type is set or there are no more
+        // instances. In the latter case when deleting the last one,
+        // \Drupal\Core\Entity\FieldInstance::delete() passes in the entity
+        // type of the last instance.
+        \Drupal::entityManager()->getStorageController($entity_type ?: $last_entity_type)->handleDeleteField($this);
+      }
 
       // Delete the configuration of this field and save the field configuration
       // in the key_value table so we can use it later during
@@ -528,25 +503,6 @@ public function getColumns() {
   /**
    * {@inheritdoc}
    */
-  public function getStorageDetails() {
-    if (!isset($this->storageDetails)) {
-      $module_handler = \Drupal::moduleHandler();
-
-      // Collect the storage details from the storage backend, and let other
-      // modules alter it. This invokes hook_field_storage_details() and
-      // hook_field_storage_details_alter().
-      $details = (array) $module_handler->invoke($this->storage['module'], 'field_storage_details', array($this));
-      $module_handler->alter('field_storage_details', $details, $this);
-
-      $this->storageDetails = $details;
-    }
-
-    return $this->storageDetails;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getBundles() {
     if (empty($this->deleted)) {
       $map = field_info_field_map();
@@ -575,10 +531,6 @@ public function getFieldType() {
    * {@inheritdoc}
    */
   public function getFieldSettings() {
-    // @todo field_info_field_types() calls _field_info_collate_types() which
-    //   maintains its own static cache. However, do some CPU and memory
-    //   profiling to see if it's worth statically caching $field_type_info, or
-    //   the default field and instance settings, within $this.
     $field_type_info = field_info_field_types($this->type);
 
     $settings = $field_type_info['instance_settings'] + $this->settings + $field_type_info['settings'];
@@ -677,10 +629,6 @@ public function &offsetGet($offset) {
       case 'bundles':
         $bundles = $this->getBundles();
         return $bundles;
-
-      case 'storage_details':
-        $this->getStorageDetails();
-        return $this->storageDetails;
     }
 
     return $this->{$offset};
@@ -762,4 +710,28 @@ public function hasData() {
 
     return FALSE;
   }
+
+  public function getStorageType() {
+    return $this->storageType;
+  }
+
+  /**
+   * Set the storage type.
+   *
+   * @param $storage_type
+   *   The storage type.
+   * @return bool
+   *   TRUE when there was no storage type before.
+   * @throws \Drupal\field\FieldException
+   */
+  public function setStorageType($storage_type) {
+    if (!$this->storageType && $storage_type) {
+      $this->storageType = $storage_type;
+      return TRUE;
+    }
+    elseif ($storage_type != $this->storageType) {
+      throw new FieldException('Field storage type can not be changed.');
+    }
+    return FALSE;
+  }
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
index 5f7e435..91b8cba 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
@@ -352,13 +352,17 @@ public function save() {
    *   In case of failures at the configuration storage level.
    */
   protected function saveNew() {
-    $module_handler = \Drupal::moduleHandler();
     $instance_controller = \Drupal::entityManager()->getStorageController($this->entityType);
 
     // Check that the field can be attached to this entity type.
     if (!empty($this->field->entity_types) && !in_array($this->entity_type, $this->field->entity_types)) {
       throw new FieldException(format_string('Attempt to create an instance of field @field_id on forbidden entity type @entity_type.', array('@field_id' => $this->field->id, '@entity_type' => $this->entity_type)));
     }
+    $data_storage_controller = \Drupal::entityManager()->getStorageController($this->entity_type);
+    if ($this->field->setStorageType($data_storage_controller->storageType())) {
+      $this->field->save();
+      $data_storage_controller->handleFirstInstance($this);
+    }
 
     // Assign the ID.
     $this->id = $this->id();
@@ -441,7 +445,6 @@ protected function prepareSave() {
    */
   public function delete($field_cleanup = TRUE) {
     if (!$this->deleted) {
-      $module_handler = \Drupal::moduleHandler();
       $state = \Drupal::state();
 
       // Delete the configuration of this instance and save the configuration
@@ -455,13 +458,10 @@ public function delete($field_cleanup = TRUE) {
 
       parent::delete();
 
+      \Drupal::entityManager()->getStorageController($this->entity_type)->handleInstanceDelete($this);
       // Clear the cache.
       field_cache_clear();
 
-      // Mark instance data for deletion by invoking
-      // hook_field_storage_delete_instance().
-      $module_handler->invoke($this->field->storage['module'], 'field_storage_delete_instance', array($this));
-
       // Remove the instance from the entity form displays.
       if ($form_display = entity_load('entity_form_display', $this->entity_type . '.' . $this->bundle . '.default')) {
         $form_display->removeComponent($this->field->id())->save();
@@ -479,7 +479,7 @@ public function delete($field_cleanup = TRUE) {
 
       // Delete the field itself if we just deleted its last instance.
       if ($field_cleanup && count($this->field->getBundles()) == 0) {
-        $this->field->delete();
+        $this->field->delete($this->entity_type);
       }
     }
   }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index 0ae63b7..1bfe570 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\field\Plugin\views\field;
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager;
@@ -286,7 +287,7 @@ public function clickSort($order) {
     }
 
     $this->ensureMyTable();
-    $column = _field_sql_storage_columnname($this->definition['field_name'], $this->options['click_sort_column']);
+    $column = DatabaseStorageController::fieldColumnName($this->definition['field_name'], $this->options['click_sort_column']);
     if (!isset($this->aliases[$column])) {
       // Column is not in query; add a sort on it (without adding the column).
       $this->aliases[$column] = $this->tableAlias . '.' . $column;
diff --git a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
index c74d426..d431831 100644
--- a/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/ActiveTest.php
@@ -43,23 +43,7 @@ function testActive() {
     // - the field type module,
     // - the storage module,
     // - both.
-    $this->_testActiveHelper($field_definition, array('field_test'));
-    $this->_testActiveHelper($field_definition, array('field_sql_storage'));
-    $this->_testActiveHelper($field_definition, array('field_test', 'field_sql_storage'));
-  }
-
-  /**
-   * Helper function for testActive().
-   *
-   * Test dependency between a field and a set of modules.
-   *
-   * @param $field_definition
-   *   A field definition.
-   * @param $modules
-   *   An aray of module names. The field will be tested to be inactive as long
-   *   as any of those modules is disabled.
-   */
-  function _testActiveHelper($field_definition, $modules) {
+    $modules = array('field_test');
     $field_name = $field_definition['field_name'];
 
     // Read the field.
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index 4580e7b..8b2e038 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
@@ -20,7 +20,7 @@
    *
    * @var array
    */
-  public static $modules = array('user', 'entity', 'system', 'field', 'text', 'field_sql_storage', 'entity_test', 'field_test');
+  public static $modules = array('user', 'entity', 'system', 'field', 'text', 'entity_test', 'field_test');
 
   /**
    * A string for assert raw and text helper methods.
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index 4bf2625..b964baa 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\field\Tests\Views;
+use Drupal\Core\Entity\DatabaseStorageController;
 
 /**
  * Test the produced views_data.
@@ -84,8 +85,8 @@ function testViewsData() {
     // Check the table and the joins of the first field.
     // Attached to node only.
     $field = $this->fields[0];
-    $current_table = _field_sql_storage_tablename($field);
-    $revision_table = _field_sql_storage_revision_tablename($field);
+    $current_table = DatabaseStorageController::fieldTableName($field);
+    $revision_table = DatabaseStorageController::fieldRevisionTableName($field);
     $data[$current_table] = $views_data->get($current_table);
     $data[$revision_table] = $views_data->get($revision_table);
 
@@ -117,8 +118,8 @@ function testViewsData() {
     // Check the table and the joins of the second field.
     // Attached to both node and user.
     $field_2 = $this->fields[2];
-    $current_table_2 = _field_sql_storage_tablename($field_2);
-    $revision_table_2 = _field_sql_storage_revision_tablename($field_2);
+    $current_table_2 = DatabaseStorageController::fieldTableName($field_2);
+    $revision_table_2 = DatabaseStorageController::fieldRevisionTableName($field_2);
     $data[$current_table_2] = $views_data->get($current_table_2);
     $data[$revision_table_2] = $views_data->get($revision_table_2);
 
diff --git a/core/modules/field/tests/modules/field_test/field_test.module b/core/modules/field/tests/modules/field_test/field_test.module
index 800f4d4..3ab0c7e 100644
--- a/core/modules/field/tests/modules/field_test/field_test.module
+++ b/core/modules/field/tests/modules/field_test/field_test.module
@@ -18,7 +18,6 @@
 
 require_once __DIR__ . '/field_test.entity.inc';
 require_once __DIR__ . '/field_test.field.inc';
-require_once __DIR__ . '/field_test.storage.inc';
 
 /**
  * Implements hook_permission().
diff --git a/core/modules/field_sql_storage/field_sql_storage.info.yml b/core/modules/field_sql_storage/field_sql_storage.info.yml
deleted file mode 100644
index e5172b5..0000000
--- a/core/modules/field_sql_storage/field_sql_storage.info.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: 'Field SQL Storage'
-type: module
-description: 'Stores field data in an SQL database.'
-package: Core
-version: VERSION
-core: 8.x
-dependencies:
-  - field
-required: true
diff --git a/core/modules/file/file.views.inc b/core/modules/file/file.views.inc
index 5c2743f..8c319e7 100644
--- a/core/modules/file/file.views.inc
+++ b/core/modules/file/file.views.inc
@@ -6,6 +6,7 @@
  *
  * @ingroup views_module_handlers
  */
+use Drupal\Core\Entity\DatabaseStorageController;
 
 /**
  * Implements hook_views_data().
@@ -488,7 +489,7 @@ function file_field_views_data_views_data_alter(&$data, $field) {
       'help' => t('Relate each @entity with a @field set to the file.', array('@entity' => $entity, '@field' => $label)),
       'id' => 'entity_reverse',
       'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
+      'field table' => DatabaseStorageController::fieldTableName($field),
       'field field' => $field['field_name'] . '_target_id',
       'base' => $entity_info['base_table'],
       'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 11a1054..b163ee2 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -493,7 +493,7 @@ function forum_comment_delete($comment) {
 /**
  * Implements hook_field_storage_pre_insert().
  */
-function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields) {
+function forum_field_storage_pre_insert(EntityInterface $entity) {
   if ($entity->entityType() == 'node' && $entity->status && _forum_node_check_node_type($entity)) {
     $query = db_insert('forum_index')->fields(array('nid', 'title', 'tid', 'sticky', 'created', 'comment_count', 'last_comment_timestamp'));
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
@@ -515,7 +515,7 @@ function forum_field_storage_pre_insert(EntityInterface $entity, &$skip_fields)
 /**
  * Implements hook_field_storage_pre_update().
  */
-function forum_field_storage_pre_update(EntityInterface $entity, &$skip_fields) {
+function forum_field_storage_pre_update(EntityInterface $entity) {
   $first_call = &drupal_static(__FUNCTION__, array());
 
   if ($entity->entityType() == 'node' && _forum_node_check_node_type($entity)) {
diff --git a/core/modules/image/image.views.inc b/core/modules/image/image.views.inc
index 2e70c08..b6e0c37 100644
--- a/core/modules/image/image.views.inc
+++ b/core/modules/image/image.views.inc
@@ -6,6 +6,7 @@
  *
  * @ingroup views_module_handlers
  */
+use Drupal\Core\Entity\DatabaseStorageController;
 
 /**
  * Implements hook_field_views_data().
@@ -51,7 +52,7 @@ function image_field_views_data_views_data_alter(&$data, $field) {
       'help' => t('Relate each @entity with a @field set to the image.', array('@entity' => $entity, '@field' => $label)),
       'id' => 'entity_reverse',
       'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
+      'field table' => DatabaseStorageController::fieldTableName($field),
       'field field' => $field['field_name'] . '_target_id',
       'base' => $entity_info['base_table'],
       'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index 90fbdbb..0cb532f 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\Core\Database\Connection;
+use Drupal\field\FieldInfo;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Cmf\Component\Routing\RouteProviderInterface;
 
@@ -55,8 +56,8 @@ class MenuLinkStorageController extends DatabaseStorageController implements Men
    * @param \Symfony\Cmf\Component\Routing\RouteProviderInterface $route_provider
    *   The route provider service.
    */
-  public function __construct($entity_type, array $entity_info, Connection $database, RouteProviderInterface $route_provider) {
-    parent::__construct($entity_type, $entity_info, $database);
+  public function __construct($entity_type, array $entity_info, Connection $database, FieldInfo $field_info, RouteProviderInterface $route_provider) {
+    parent::__construct($entity_type, $entity_info, $database, $field_info);
 
     $this->routeProvider = $route_provider;
 
@@ -85,6 +86,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
+      $container->get('field.info'),
       $container->get('router.route_provider')
     );
   }
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 524036b..f78203d 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\DatabaseStorageControllerNG;
 use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\DependencyInjection\Container;
 
 /**
  * Controller class for nodes.
@@ -46,10 +47,10 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
     }
 
     if ($load_revision) {
-      field_attach_load_revision($this->entityType, $queried_entities);
+      $this->fieldLoad($this->entityType, $queried_entities, FIELD_LOAD_REVISION);
     }
     else {
-      field_attach_load($this->entityType, $queried_entities);
+      $this->fieldLoad($this->entityType, $queried_entities, FIELD_LOAD_CURRENT);
     }
 
     // Besides the list of nodes, pass one additional argument to
@@ -80,14 +81,9 @@ protected function invokeHook($hook, EntityInterface $node) {
     // Inline parent::invokeHook() to pass on BC-entities to node-specific
     // hooks.
 
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($node);
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($node);
     }
 
     // Invoke the hook.
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
index 74fa8d9..0341b57 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
@@ -16,7 +16,7 @@
    *
    * @var array
    */
-  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text', 'field_sql_storage');
+  public static $modules = array('serialization', 'system', 'entity', 'field', 'entity_test', 'text');
 
   protected function setUp() {
     parent::setUp();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
index 664b09c..bbe7c83 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -19,7 +19,7 @@
    *
    * @var array
    */
-  public static $modules = array('entity', 'user', 'system', 'field', 'text', 'field_sql_storage', 'entity_test');
+  public static $modules = array('entity', 'user', 'system', 'field', 'text', 'entity_test');
 
   public function setUp() {
     parent::setUp();
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index 5646268..daaeab6 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -6,6 +6,7 @@
  *
  * @ingroup views_module_handlers
  */
+use Drupal\Core\Entity\DatabaseStorageController;
 
 /**
  * Implements hook_views_data().
@@ -379,7 +380,7 @@ function taxonomy_field_views_data_views_data_alter(&$data, $field) {
       'help' => t('Relate each @entity with a @field set to the term.', array('@entity' => $entity, '@field' => $label)),
       'id' => 'entity_reverse',
       'field_name' => $field['field_name'],
-      'field table' => _field_sql_storage_tablename($field),
+      'field table' => DatabaseStorageController::fieldTableName($field),
       'field field' => $field['field_name'] . '_target_id',
       'base' => $entity_info['base_table'],
       'base field' => $entity_info['entity_keys']['id'],
diff --git a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
index 6158f07..82924bd 100644
--- a/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
+++ b/core/modules/telephone/lib/Drupal/telephone/Tests/TelephoneFieldTest.php
@@ -21,7 +21,6 @@ class TelephoneFieldTest extends WebTestBase {
    */
   public static $modules = array(
     'field',
-    'field_sql_storage',
     'node',
     'telephone'
   );
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 0a2b900..5d2c3e4 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -26,7 +26,7 @@ class TextPlainUnitTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('entity', 'field', 'field_sql_storage', 'text', 'entity_test', 'system');
+  public static $modules = array('entity', 'field', 'text', 'entity_test', 'system');
 
   /**
    * Contains rendered content.
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
index 14d885c..c073b95 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserEntityTest.php
@@ -23,7 +23,7 @@ class UserEntityTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'user');
+  public static $modules = array('system', 'user', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 8b4653c..b2a76b2 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -11,7 +11,9 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
+use Drupal\field\FieldInfo;
 use Drupal\user\UserDataInterface;
+use Symfony\Component\DependencyInjection\Container;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Entity\DatabaseStorageControllerNG;
 
@@ -51,8 +53,8 @@ class UserStorageController extends DatabaseStorageControllerNG implements UserS
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct($entity_type, $entity_info, Connection $database, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $entity_info, $database);
+  public function __construct($entity_type, $entity_info, Connection $database, FieldInfo $field_info, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $entity_info, $database, $field_info);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -66,6 +68,7 @@ public static function createInstance(ContainerInterface $container, $entity_typ
       $entity_type,
       $entity_info,
       $container->get('database'),
+      $container->get('field.info'),
       $container->get('password'),
       $container->get('user.data')
     );
@@ -155,14 +158,9 @@ public function deleteUserRoles(array $uids) {
    * {@inheritdoc}
    */
   protected function invokeHook($hook, EntityInterface $entity) {
-    $function = 'field_attach_' . $hook;
-    // @todo: field_attach_delete_revision() is named the wrong way round,
-    // consider renaming it.
-    if ($function == 'field_attach_revision_delete') {
-      $function = 'field_attach_delete_revision';
-    }
-    if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $function($entity->getBCEntity());
+    $method = Container::camelize('field_' . $hook);
+    if (!empty($this->entityInfo['fieldable']) && method_exists($this, $method)) {
+      $this->$method($entity->getBCEntity());
     }
 
     // Invoke the hook.
