diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index fe30114..c2227a9 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 f4ef9a8..fee3770 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -534,4 +534,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 e2dc1b4..8a8ba1b 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 loadMultiple(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);
     }
@@ -196,7 +206,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);
 
@@ -338,10 +348,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);
       }
     }
 
@@ -490,7 +500,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) {
@@ -528,31 +538,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() {
@@ -566,4 +551,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 \Drupal\field\Plugin\Core\Entity\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(EntityInterface $entity, Field $field, FieldInstance $instance) {
+    parent::fieldPurgeData($entity, $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(EntityInterface $entity, 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()
+      ->fetchAll();
+  }
+
+  /**
+   * {@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 143a2ab..a2bf5d6 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;
@@ -53,8 +54,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 4766a91..3248d88 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)->id, '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);
   }
 
   /**
@@ -153,6 +179,9 @@ public function hasController($entity_type, $controller_type) {
    */
   public function getControllerClass($entity_type, $controller_type, $nested = NULL) {
     $definition = $this->getDefinition($entity_type);
+    if (!$definition) {
+      throw new \InvalidArgumentException(sprintf('The %s entity type does not exist.', $entity_type));
+    }
     $definition = $definition['controllers'];
     if (empty($definition[$controller_type])) {
       throw new \InvalidArgumentException(sprintf('The entity (%s) did not specify a %s.', $entity_type, $controller_type));
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerBase.php
index ea4e06c..6b97721 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,6 +237,26 @@ 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);
+  }
+
+  /**
    * Checks translation statuses and invoke the related hooks if needed.
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
@@ -255,4 +278,291 @@ protected function invokeTranslationHooks(EntityInterface $entity) {
     }
   }
 
+  /**
+   * 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(EntityInterface  $entity, Field $field, FieldInstance $instance) {
+    $values = $this->fieldValues($entity, $field, $instance);
+    foreach ($values as $value) {
+      $definition = _field_generate_entity_field_definition($field, $instance);
+      $items = \Drupal::typedData()->create($definition, $value, $field->id(), $entity);
+      $items->delete();
+    }
+  }
+
+  /**
+   * Get the field values for a single field of a single entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity_id
+   * @param Field $field
+   * @param FieldInstance $instance
+   * @return array
+   */
+  protected function fieldValues(EntityInterface $entity, 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 24447fd..7f03ce7 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.
@@ -173,4 +175,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(EntityInterface $entity, 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/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..04e1291 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -12,6 +12,10 @@ 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',
+  )));
+  drupal_classloader_register('comment', 'core/modules/comment');
   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 79f22a9..6d4ff98 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 6a0ddc5..dc919fb 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'));
 
@@ -239,7 +239,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/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
index 9a611d4..c614e87 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionAccessTest.php
@@ -24,7 +24,7 @@ public static function getInfo() {
     );
   }
 
-  public static $modules = array('node', 'comment', 'entity_reference');
+  public static $modules = array('node', 'comment', 'entity_reference', 'entity_test');
 
   function setUp() {
     parent::setUp();
@@ -73,7 +73,7 @@ public function testNodeHandler() {
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
@@ -216,7 +216,7 @@ public function testUserHandler() {
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
@@ -361,7 +361,7 @@ public function testCommentHandler() {
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
index f7b8fbc..6d6eaa0 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/EntityReferenceSelectionSortTest.php
@@ -22,7 +22,7 @@ public static function getInfo() {
     );
   }
 
-  public static $modules = array('node', 'entity_reference');
+  public static $modules = array('node', 'entity_reference', 'entity_test');
 
   function setUp() {
     parent::setUp();
@@ -66,7 +66,7 @@ public function testSort() {
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'default',
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
index 8127682..a460ea1 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Tests/Views/SelectionTest.php
@@ -14,7 +14,7 @@
  */
 class SelectionTest extends WebTestBase {
 
-  public static $modules = array('views', 'entity_reference', 'entity_reference_test');
+  public static $modules = array('views', 'entity_reference', 'entity_reference_test', 'entity_test');
 
   public static function getInfo() {
     return array(
@@ -53,7 +53,7 @@ public function testSelectionHandler() {
     $field->save();
     $instance = entity_create('field_instance', array(
       'field_name' => 'test_field',
-      'entity_type' => 'test_entity',
+      'entity_type' => 'entity_test',
       'bundle' => 'test_bundle',
       'settings' => array(
         'handler' => 'views',
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 5ed17ff..ddb4f2f 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..cc0ab18 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -264,21 +264,12 @@ function field_purge_batch($batch_size) {
       $query->condition($info[$entity_type]['entity_keys']['bundle'], $ids->bundle);
     }
     $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);
+        $entity = _field_create_entity_from_ids($ids);
+        Drupal::entityManager()->getStorageController($instance->entity_type)->fieldPurgeData($entity, $field, $instance);
       }
     }
     else {
@@ -299,37 +290,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 b6b2a95..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->id;
-
-  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..1977db3 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
@@ -36,23 +37,10 @@ function _update_8003_field_create_field(array &$field_config) {
     'langcode' => 'und',
   );
 
-  // Set the storage.
-  $field_config['storage'] = array(
-    'type' => 'field_sql_storage',
-    'module' => 'field_sql_storage',
-    'active' => TRUE,
-    'settings' => array(),
-  );
-
   // Save in config.
   Drupal::config('field.field.' . $field_config['id'])
     ->setData($field_config)
     ->save();
-
-  // Create storage for the field. This requires a field entity, but cannot use
-  // the regular entity_create() function here.
-  $field_entity = new Field($field_config);
-  field_sql_storage_field_storage_create_field($field_entity);
 }
 
 /**
@@ -65,10 +53,12 @@ function _update_8003_field_create_field(array &$field_config) {
  *   An array of field properties.
  * @param array $instance_config
  *   An array of field instance properties.
+ * @param bool $first
+ *   TRUE ff this is the first instance of the field. Defaults to TRUE.
  *
  * @ingroup update_api
  */
-function _update_8003_field_create_instance(array $field_config, array &$instance_config) {
+function _update_8003_field_create_instance(array $field_config, array &$instance_config, $first = TRUE) {
   $uuid = new Uuid();
 
   // Merge in defaults.
@@ -90,6 +80,66 @@ function _update_8003_field_create_instance(array $field_config, array &$instanc
   Drupal::config('field.instance.' . $instance_config['id'])
     ->setData($instance_config)
     ->save();
+  if ($first) {
+    $schema = DatabaseStorageController::fieldSqlSchema(new Field($field_config));
+    foreach ($schema as $name => $table) {
+      db_create_table($name, $table);
+    }
+  }
+}
+
+/**
+ * Writes field data directly to SQL storage.
+ *
+ * @ingroup update_api
+ */
+function _update_8000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
+  $table_name = "field_data_{$field_name}";
+  $revision_name = "field_revision_{$field_name}";
+
+  db_delete($table_name)
+    ->condition('entity_type', $entity_type)
+    ->condition('entity_id', $entity_id)
+    ->execute();
+  db_delete($revision_name)
+    ->condition('entity_type', $entity_type)
+    ->condition('entity_id', $entity_id)
+    ->condition('revision_id', $revision_id)
+    ->execute();
+
+  $columns = array();
+  foreach ($data as $langcode => $items) {
+    foreach ($items as $delta => $item) {
+      $record = array(
+        'entity_type' => $entity_type,
+        'entity_id' => $entity_id,
+        'revision_id' => $revision_id,
+        'bundle' => $bundle,
+        'delta' => $delta,
+        'langcode' => $langcode,
+      );
+      foreach ($item as $column => $value) {
+        $record[_field_sql_storage_columnname($field_name, $column)] = $value;
+      }
+
+      $records[] = $record;
+      // Record the columns used.
+      $columns += $record;
+    }
+  }
+
+  if ($columns) {
+    $query = db_insert($table_name)->fields(array_keys($columns));
+    $revision_query = db_insert($revision_name)->fields(array_keys($columns));
+    foreach ($records as $record) {
+      $query->values($record);
+      if ($revision_id) {
+        $revision_query->values($record);
+      }
+    }
+    $query->execute();
+    $revision_query->execute();
+  }
 }
 
 /**
@@ -261,12 +311,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'],
@@ -274,6 +318,7 @@ function field_update_8003() {
       'indexes' => $record['data']['indexes'] ?: array(),
       'status' => 1,
       'langcode' => 'und',
+      'storageType' => 'sql',
     );
 
     // Reassign all list.module fields to be controlled by options.module.
@@ -293,16 +338,14 @@ function field_update_8003() {
       // Additionally, rename the data tables for deleted fields. Technically
       // this would belong in an update in field_sql_storage.module, but it is
       // easier to do it now, when the old numeric ID is available.
-      if ($config['storage']['type'] == 'field_sql_storage') {
-        $field = new Field($config);
-        $tables = array(
-          "field_deleted_data_{$record['id']}" => _field_sql_storage_tablename($field),
-          "field_deleted_revision_{$record['id']}" => _field_sql_storage_revision_tablename($field),
-        );
-        foreach ($tables as $table_old => $table_new) {
-          if (db_table_exists($table_old)) {
-            db_rename_table($table_old, $table_new);
-          }
+      $field = new Field($config);
+      $tables = array(
+        "field_deleted_data_{$record['id']}" => DatabaseStorageController::fieldTableName($field),
+        "field_deleted_revision_{$record['id']}" => DatabaseStorageController::fieldRevisionTableName($field),
+      );
+      foreach ($tables as $table_old => $table_new) {
+        if (db_table_exists($table_old)) {
+          db_rename_table($table_old, $table_new);
         }
       }
     }
@@ -411,6 +454,87 @@ function field_update_8005() {
     ->save();
 }
 
+
+/**
+ * Renames the 'language' column to 'langcode' in field data tables.
+ */
+function field_update_8006(&$sandbox) {
+  // Get field definitions from config, and deleted fields from state system.
+  $config_names = config_get_storage_names_with_prefix('field.field');
+  $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
+  // Ditch UUID keys, we will iterate through deleted fields using a numeric
+  // index.
+  $deleted_fields = array_values($deleted_fields);
+
+  if (empty($config_names) && empty($deleted_fields)) {
+    return;
+  }
+
+  if (!isset($sandbox['index'])) {
+    $sandbox['index'] = 0;
+    $sandbox['max'] = count($config_names) + count($deleted_fields);
+  }
+
+  // Retrieve the next field definition. When the index exceeds the number of
+  // 'configuration' fields, use it to iterate on deleted fields.
+  if (isset($config_names[$sandbox['index']])) {
+    $field_config = config($config_names[$sandbox['index']])->get();
+  }
+  else {
+    $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
+  }
+
+  $field = new Field($field_config);
+  $base_table = DatabaseStorageController::fieldTableName($field);
+  if (db_table_exists($base_table)) {
+
+    // Prepare updated schema data structures.
+    $primary_key_data = array(
+      'entity_type',
+      'entity_id',
+      'deleted',
+      'delta',
+      'langcode',
+    );
+    $primary_key_revision = array(
+      'entity_type',
+      'entity_id',
+      'revision_id',
+      'deleted',
+      'delta',
+      'langcode',
+    );
+    $langcode_index = array(
+      'langcode',
+    );
+    $field_langcode = array(
+      'type' => 'varchar',
+      'length' => 32,
+      'not null' => TRUE,
+      'default' => '',
+    );
+    $table_info = array(
+      $base_table => $primary_key_data,
+      DatabaseStorageController::fieldRevisionTableName($field) => $primary_key_revision,
+    );
+
+    foreach ($table_info as $table => $primary_key) {
+      // Do not update tables which already have the langcode column,
+      // created during the upgrade before this update function.
+      if (db_field_exists($table, 'language')) {
+        db_drop_primary_key($table);
+        db_drop_index($table, 'language');
+        db_change_field($table, 'language', 'langcode', $field_langcode);
+        db_add_primary_key($table, $primary_key);
+        db_add_index($table, 'langcode', $langcode_index);
+      }
+    }
+  }
+
+  $sandbox['index']++;
+  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
+}
+
 /**
  * @} End of "addtogroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 7a18b9e..c4ff6fd 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -398,7 +398,6 @@ function field_modules_disabled($modules) {
  * Refreshes the 'active' and 'storage[active]' values for fields.
  */
 function field_sync_field_status() {
-  $module_handler = Drupal::moduleHandler();
   $state = Drupal::state();
 
   // Get both deleted and non-deleted field definitions.
@@ -417,25 +416,9 @@ function field_sync_field_status() {
   // Set the 'module' and 'active' values for the current set of enabled
   // modules.
   $changed = array();
-  $modules = $module_handler->getModuleList();
-  foreach ($modules as $module => $module_info) {
-    // Collect storage backends exposed by the module.
-    $storage_types = (array) $module_handler->invoke($module, 'field_storage_info');
-
-    if ($storage_types) {
-      foreach ($fields as $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;
-        }
-      }
-    }
-  }
 
   $field_types = Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
-  // 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) {
     // Associate field types.
     if (isset($field_types[$field['type']]) && ($field['module'] != $field_types[$field['type']]['module'] || !$field['active'])) {
@@ -447,11 +430,6 @@ function field_sync_field_status() {
       $field['active'] = FALSE;
       $changed[$uuid] = $field;
     }
-    // Disassociate storage backends.
-    if (!isset($modules[$field['storage']['module']]) && $field['storage']['active']) {
-      $field['storage']['active'] = FALSE;
-      $changed[$uuid] = $field;
-    }
   }
 
   // Store the updated field definitions.
@@ -463,8 +441,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 f9ce249..d40808e 100644
--- a/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
+++ b/core/modules/field/lib/Drupal/field/FieldInstanceStorageController.php
@@ -136,7 +136,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.
@@ -162,10 +161,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 41efe60..850d293 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 1bb1ae4..7eb8deb 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();
@@ -405,6 +368,15 @@ protected function saveUpdated() {
 
     $original = $storage_controller->loadUnchanged($this->id());
     $this->original = $original;
+    $original_storage_type = $this->original->getStorageType();
+    // Disallow changing storage types. Explicit changes of storage types are
+    // disallowed by setStorageType() so this can only happen if an existing
+    // field object gets out of sync with the stored field config, for example
+    // by creating a field, an instance and then re-saving the original field
+    // object.
+    if ($original_storage_type && $this->storageType != $this->original->getStorageType()) {
+      $this->storageType = $this->original->getStorageType();
+    }
 
     // Some updates are always disallowed.
     if ($this->type != $original->type) {
@@ -413,9 +385,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
@@ -426,11 +395,14 @@ protected function saveUpdated() {
     // invokes hook_field_update_forbid().
     $module_handler->invokeAll('field_update_forbid', array($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));
+    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);
+    }
+
 
     // Save the configuration.
     $result = parent::save();
@@ -440,13 +412,23 @@ protected function saveUpdated() {
   }
 
   /**
-   * {@inheritdoc}
+   * Overrides \Drupal\Core\Entity\Entity::delete().
+   *
+   * @param string $attached_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 controller 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($attached_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();
@@ -454,6 +436,9 @@ public function delete() {
         foreach ($bundles as $bundle) {
           $instance_ids[] = "$entity_type.$bundle.$this->id";
         }
+        // A field only can be instantiated on one storage type, it does not
+        // matter which $entity_type is used as long as there is one.
+        $attached_entity_type = $entity_type;
       }
       foreach ($instance_controller->loadMultiple($instance_ids) as $instance) {
         // By default, FieldInstance::delete() will automatically try to delete
@@ -463,9 +448,10 @@ 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));
+      // The only case $entity_type is empty when there never were instances.
+      if ($attached_entity_type) {
+        \Drupal::entityManager()->getStorageController($attached_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 +514,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 +542,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 +640,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 +721,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 cbfb636..3da5fef 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 3604142..8210845 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/BulkDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
index 9637826..503fbdf 100644
--- a/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/BulkDeleteTest.php
@@ -7,10 +7,10 @@
 
 namespace Drupal\field\Tests;
 
-use Drupal\field\Plugin\Core\Entity\FieldInstance;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\field\Plugin\Core\Entity\Field;
 
-use Drupal\Core\Language\Language;
 
 /**
  * Unit test class for field bulk delete and batch purge functionality.
@@ -50,7 +50,7 @@ class BulkDeleteTest extends FieldUnitTestBase {
    *
    * @var array
    */
-  protected $entity_type = 'test_entity';
+  protected $entity_type = 'entity_test';
 
   public static function getInfo() {
     return array(
@@ -130,7 +130,6 @@ function setUp() {
 
     // For each bundle, create an instance of each field, and 10
     // entities with values for each field.
-    $this->entity_type = 'entity_test';
     foreach ($this->bundles as $bundle) {
       foreach ($this->fields as $field) {
         entity_create('field_instance', array(
@@ -198,19 +197,25 @@ function testDeleteFieldInstance() {
       ->condition("$field_name.deleted", 1)
       ->sort('id')
       ->execute();
-    $ids = (object) array(
-      'entity_type' => 'entity_test',
-      'bundle' => $bundle,
-    );
-    $entities = array();
-    foreach ($found as $entity_id) {
-      $ids->entity_id = $entity_id;
-      $entities[$entity_id] = _field_create_entity_from_ids($ids);
-    }
-    field_attach_load($this->entity_type, $entities, FIELD_LOAD_CURRENT, array('instance' => $instance));
     $this->assertEqual(count($found), 10, 'Correct number of entities found after deleting');
-    foreach ($entities as $id => $entity) {
-      $this->assertEqual($this->entities[$id]->{$field->id()}->value, $entity->{$field->id()}[Language::LANGCODE_NOT_SPECIFIED][0]['value'], "Entity $id with deleted data loaded correctly");
+    $this->assertFalse(array_diff($found, array_keys($this->entities)));
+    $this->checkFieldTableContents($field);
+  }
+
+  /**
+   * Test that the actual stored content didn't change during delete.
+   *
+   * @param Field $field
+   */
+  protected function checkFieldTableContents(Field $field) {
+    $schema = DatabaseStorageController::fieldSqlSchema($field);
+    $table = DatabaseStorageController::fieldTableName($field);
+    $column = DatabaseStorageController::fieldColumnName($field->id(), 'value');
+    $result = db_select($table, 't')
+      ->fields('t', array_keys($schema[$table]['fields']))
+      ->execute();
+    foreach ($result as $row) {
+      $this->assertEqual($this->entities[$row->entity_id]->{$field->id()}->value, $row->$column);
     }
   }
 
@@ -247,13 +252,12 @@ function testPurgeInstance() {
     }
 
     // Check hooks invocations.
-    // hook_field_load() and hook_field_delete() should have been called once
-    // for each entity in the bundle.
+    // hook_field_delete() should have been called once for each entity in the
+    // bundle.
     $actual_hooks = field_test_memorize();
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
@@ -297,13 +301,12 @@ function testPurgeField() {
     field_purge_batch(10);
 
     // Check hooks invocations.
-    // hook_field_load() and hook_field_delete() should have been called once
-    // for each entity in the bundle.
+    // hook_field_delete() should have been called once for each entity in the
+    // bundle.
     $actual_hooks = field_test_memorize();
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
@@ -332,7 +335,6 @@ function testPurgeField() {
     $hooks = array();
     $entities = $this->entities_by_bundles[$bundle];
     foreach ($entities as $id => $entity) {
-      $hooks['field_test_field_load'][] = array($id => $entity);
       $hooks['field_test_field_delete'][] = $entity;
     }
     $this->checkHooksInvocations($hooks, $actual_hooks);
diff --git a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
index b7b7d58..5c57ef6 100644
--- a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
@@ -63,9 +63,6 @@ function testCreateField() {
     $field_type = field_info_field_types($field_definition['type']);
     $this->assertEqual($field_config['settings'], $field_type['settings'], 'Default field settings have been written.');
 
-    // Ensure that default storage was set.
-    $this->assertEqual($field_config['storage']['type'], config('field.settings')->get('default_storage'), 'The field type is properly saved.');
-
     // Guarantee that the name is unique.
     try {
       entity_create('field_entity', $field_definition)->save();
@@ -154,28 +151,32 @@ function testCreateField() {
   }
 
   /**
-   * Test failure to create a field.
+   * Test failure to change the storage type of a field.
    */
   function testCreateFieldFail() {
-    $field_name = 'duplicate';
-    $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure'));
-    $field = entity_load('field_entity', $field_name);
-
-    // The field does not exist.
-    $this->assertFalse($field, 'The field does not exist.');
-
-    // Try to create the field.
+    $this->assertFalse(field_info_field('field_1'), 'field_1 does not exist');
+    $field_definition = array(
+      'field_name' => 'field_1',
+      'type' => 'test_field',
+    );
+    entity_create('field_entity', $field_definition)->save();
+    $instance_definition = array(
+      'field_name' => $field_definition['field_name'],
+      'entity_type' => 'entity_test',
+      'bundle' => 'entity_test',
+    );
+    entity_create('field_instance', $instance_definition)->save();
+    $field = field_info_field('field_1');
+    // Check that not-changing does not throw an exception.
+    $storage_type = $field->getStorageType();
+    $field->setStorageType($storage_type);
     try {
-      entity_create('field_entity', $field_definition)->save();
-      $this->assertTrue(FALSE, 'Field creation (correctly) fails.');
+      $field->setStorageType($storage_type . 'x');
+      $this->fail('Field storage type can not be changed.');
     }
-    catch (\Exception $e) {
-      $this->assertTrue(TRUE, 'Field creation (correctly) fails.');
+    catch (FieldException $e) {
+      $this->assertEqual($e->getMessage(), 'Field storage type can not be changed.');
     }
-
-    // The field does not exist.
-    $field = entity_load('field_entity', $field_name);
-    $this->assertFalse($field, 'The field does not exist.');
   }
 
   /**
@@ -330,15 +331,12 @@ function testDeleteField() {
     $this->assertTrue(!empty($instance) && empty($instance['deleted']), 'A new instance for a previously used field name is created.');
 
     // Save an entity with data for the field
-    $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
-    $langcode = Language::LANGCODE_NOT_SPECIFIED;
+    $entity = entity_create('entity_test', array());
     $values[0]['value'] = mt_rand(1, 127);
     $entity->{$field['field_name']}->value = $values[0]['value'];
-    field_attach_insert($entity);
+    $entity = $this->entitySaveReload($entity);
 
     // Verify the field is present on load
-    $entity = entity_create('entity_test', array('id' => 0, 'revision_id' => 0));
-    field_attach_load('entity_test', array(0 => $entity));
     $this->assertIdentical(count($entity->{$field['field_name']}), count($values), "Data in previously deleted field saves and loads correctly");
     foreach ($values as $delta => $value) {
       $this->assertEqual($entity->{$field['field_name']}[$delta]->value, $values[$delta]['value'], "Data in previously deleted field saves and loads correctly");
@@ -382,22 +380,18 @@ function testUpdateField() {
     $instance->save();
 
     do {
-      // We need a unique ID for our entity. $cardinality will do.
-      $id = $cardinality;
-      $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
+      $entity = entity_create('entity_test', array());
       // Fill in the entity with more values than $cardinality.
       for ($i = 0; $i < 20; $i++) {
-        $entity->field_update[$i]->value = $i;
+        // We can not use $i here because 0 values are filtered out.
+        $entity->field_update[$i]->value = $i + 1;
       }
-      // Save the entity.
-      field_attach_insert($entity);
       // Load back and assert there are $cardinality number of values.
-      $entity = entity_create('entity_test', array('id' => $id, 'revision_id' => $id));
-      field_attach_load('entity_test', array($id => $entity));
-      $this->assertEqual(count($entity->field_update), $field->cardinality, 'Cardinality is kept');
+      $entity = $this->entitySaveReload($entity);
+      $this->assertEqual(count($entity->field_update), $field->cardinality);
       // Now check the values themselves.
       for ($delta = 0; $delta < $cardinality; $delta++) {
-        $this->assertEqual($entity->field_update[$delta]->value, $delta, 'Value is kept');
+        $this->assertEqual($entity->field_update[$delta]->value, $delta + 1);
       }
       // Increase $cardinality and set the field cardinality to the new value.
       $field->cardinality = ++$cardinality;
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 4739d5f..ba712de 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -236,7 +236,7 @@ function testFieldAttachPrepareViewMultiple() {
    */
   function testFieldAttachCache() {
     // Initialize random values and a test entity.
-    $entity_init = entity_create('entity_test', array('id' => 1, 'revision_id' => 1, 'type' => $this->instance['bundle']));
+    $entity_init = entity_create('entity_test', array('type' => $this->instance['bundle']));
     $langcode = Language::LANGCODE_NOT_SPECIFIED;
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
 
@@ -250,18 +250,12 @@ function testFieldAttachCache() {
     // Save, and check that no cache entry is present.
     $entity = clone($entity_init);
     $entity->{$this->field_name}->setValue($values);
-    field_attach_insert($entity);
-    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert');
-
-    // Load, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
-    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on load');
-
+    $entity = $this->entitySaveReload($entity);
+    $cid = "field:$entity_type:" . $entity->id();
+    $this->assertFalse(cache('field')->get($cid), 'Non-cached: no cache entry on insert and load');
 
     // Cacheable entity type.
     $entity_type = 'entity_test_cache';
-    $cid = "field:$entity_type:" . $entity_init->id();
     $instance_definition = $this->instance_definition;
     $instance_definition['entity_type'] = $entity_type;
     $instance_definition['bundle'] = $entity_type;
@@ -270,8 +264,6 @@ function testFieldAttachCache() {
     entity_info_cache_clear();
 
     $entity_init = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 1,
       'type' => $entity_type,
     ));
 
@@ -281,56 +273,52 @@ function testFieldAttachCache() {
     // Save, and check that no cache entry is present.
     $entity = clone($entity_init);
     $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
+    $entity->save();
+    $cid = "field:$entity_type:" . $entity->id();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on insert');
 
-    // Load a single field, and check that no cache entry is present.
-    $entity = clone($entity_init);
-    $instance = field_info_instance($entity->entityType(), $this->field_name, $entity->bundle());
-    field_attach_load($entity_type, array($entity->id() => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
-    $cache = cache('field')->get($cid);
-    $this->assertFalse($cache, 'Cached: no cache entry on loading a single field');
-
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
     $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Update with different values, and check that the cache entry is wiped.
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
+    $entity = entity_create($entity_type, array(
+      'type' => $entity_type,
+      'id' => $entity->id(),
+    ));
     $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
+    $entity->save();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on update');
 
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
     $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Create a new revision, and check that the cache entry is wiped.
-    $entity_init = entity_create($entity_type, array(
-      'id' => 1,
-      'revision_id' => 2,
+    $entity = entity_create($entity_type, array(
       'type' => $entity_type,
+      'id' => $entity->id(),
     ));
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $entity = clone($entity_init);
     $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
-    $cache = cache('field')->get($cid);
+    $entity->setNewRevision();
+    $entity->save();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on new revision creation');
 
     // Load, and check that a cache entry is present with the expected values.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $controller->resetCache();
+    $controller->load($entity->id());
     $cache = cache('field')->get($cid);
     $this->assertEqual($cache->data[$this->field_name][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Delete, and check that the cache entry is wiped.
-    field_attach_delete($entity);
+    $entity->delete();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry after delete');
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
index e5f28d0..614877e 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachStorageTest.php
@@ -62,26 +62,22 @@ function testFieldAttachSaveLoad() {
     // TODO : test empty values filtering and "compression" (store consecutive deltas).
     // Preparation: create three revisions and store them in $revision array.
     $values = array();
+    $entity = entity_create($entity_type, array());
     for ($revision_id = 0; $revision_id < 3; $revision_id++) {
-      $revision[$revision_id] = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
       // Note: we try to insert one extra value.
-      $values[$revision_id] = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
-      $current_revision = $revision_id;
-      // If this is the first revision do an insert.
-      if (!$revision_id) {
-        $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
-        field_attach_insert($revision[$revision_id]);
-      }
-      else {
-        // Otherwise do an update.
-        $revision[$revision_id]->{$this->field_name}->setValue($values[$revision_id]);
-        field_attach_update($revision[$revision_id]);
-      }
+      $current_values = $this->_generateTestFieldValues($this->field['cardinality'] + 1);
+      $entity->{$this->field_name}->setValue($current_values);
+      $entity->setNewRevision();
+      $entity->save();
+      $entity_id = $entity->id();
+      $current_revision = $entity->getRevisionId();
+      $values[$current_revision] = $current_values;
     }
 
+    $storage_controller =  $this->container->get('plugin.manager.entity')->getStorageController($entity_type);
+    $storage_controller->resetCache();
+    $entity = $storage_controller->load($entity_id);
     // Confirm current revision loads the correct data.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0));
-    field_attach_load($entity_type, array(0 => $entity));
     // Number of values per field loaded equals the field cardinality.
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], 'Current revision: expected number of values');
     for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -92,9 +88,8 @@ function testFieldAttachSaveLoad() {
     }
 
     // Confirm each revision loads the correct data.
-    foreach (array_keys($revision) as $revision_id) {
-      $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => $revision_id));
-      field_attach_load_revision($entity_type, array(0 => $entity));
+    foreach (array_keys($values) as $revision_id) {
+      $entity = $storage_controller->loadRevision($revision_id);
       // Number of values per field loaded equals the field cardinality.
       $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], format_string('Revision %revision_id: expected number of values.', array('%revision_id' => $revision_id)));
       for ($delta = 0; $delta < $this->field['cardinality']; $delta++) {
@@ -156,11 +151,14 @@ function testFieldAttachLoadMultiple() {
         $values[$index][$field_name] = mt_rand(1, 127);
         $entity->$field_name->setValue(array('value' => $values[$index][$field_name]));
       }
-      field_attach_insert($entity);
+      $entity->enforceIsnew();
+      $entity->save();
     }
 
     // Check that a single load correctly loads field values for both entities.
-    field_attach_load($entity_type, $entities);
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
+    $entities = $controller->loadMultiple();
     foreach ($entities as $index => $entity) {
       $instances = field_info_instances($entity_type, $bundles[$index]);
       foreach ($instances as $field_name => $instance) {
@@ -170,165 +168,48 @@ function testFieldAttachLoadMultiple() {
         $this->assertEqual($entity->{$field_name}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => $index)));
       }
     }
-
-    // Check that the single-field load option works.
-    $entity = entity_create($entity_type, array('id' => 1, 'revision_id' => 1, 'type' => $bundles[1]));
-    $instance = field_info_instance($entity->entityType(), $field_names[1], $entity->bundle());
-    field_attach_load($entity_type, array(1 => $entity), FIELD_LOAD_CURRENT, array('instance' => $instance));
-    $this->assertEqual($entity->{$field_names[1]}->value, $values[1][$field_names[1]], format_string('Entity %index: expected value was found.', array('%index' => 1)));
-    $this->assertEqual($entity->{$field_names[1]}->additional_key, 'additional_value', format_string('Entity %index: extra information was found', array('%index' => 1)));
-    $this->assert(empty($entity->{$field_names[2]}->value), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 2, '%field_name' => $field_names[2])));
-    $this->assert(!isset($entity->{$field_names[3]}), format_string('Entity %index: field %field_name is not loaded.', array('%index' => 3, '%field_name' => $field_names[3])));
-  }
-
-  /**
-   * Test saving and loading fields using different storage backends.
-   */
-  function testFieldAttachSaveLoadDifferentStorage() {
-    $entity_type = 'entity_test';
-
-    // Create two fields using different storage backends, and their instances.
-    $fields = array(
-      array(
-        'field_name' => 'field_1',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_sql_storage')
-      ),
-      array(
-        'field_name' => 'field_2',
-        'type' => 'test_field',
-        'cardinality' => 4,
-        'storage' => array('type' => 'field_test_storage')
-      ),
-    );
-    foreach ($fields as $field) {
-      entity_create('field_entity', $field)->save();
-      $instance = array(
-        'field_name' => $field['field_name'],
-        'entity_type' => $entity_type,
-        'bundle' => $entity_type,
-      );
-      entity_create('field_instance', $instance)->save();
-    }
-
-    $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
-
-    // Create entity and insert random values.
-    $entity = clone($entity_init);
-    $values = array();
-    foreach ($fields as $field) {
-      $values[$field['field_name']] = $this->_generateTestFieldValues($this->field['cardinality']);
-      $entity->{$field['field_name']} = $values[$field['field_name']];
-    }
-    field_attach_insert($entity);
-
-    // Check that values are loaded as expected.
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
-    foreach ($fields as $field) {
-      $this->assertEqual($values[$field['field_name']], $entity->{$field['field_name']}->getValue(), format_string('%storage storage: expected values were found.', array('%storage' => $field['storage']['type'])));
-    }
-  }
-
-  /**
-   * Test storage details alteration.
-   *
-   * @see field_test_storage_details_alter()
-   */
-  function testFieldStorageDetailsAlter() {
-    $field_name = 'field_test_change_my_details';
-    $field = entity_create('field_entity', array(
-      'field_name' => $field_name,
-      'type' => 'test_field',
-      'cardinality' => 4,
-      'storage' => array('type' => 'field_test_storage'),
-    ));
-    $field->save();
-    $instance = entity_create('field_instance', array(
-      'field_name' => $field_name,
-      'entity_type' => 'entity_test',
-      'bundle' => 'entity_test',
-    ));
-    $instance->save();
-
-    // The storage details are indexed by a storage engine type.
-    $this->assertTrue(array_key_exists('drupal_variables', $field['storage_details']), 'The storage type is Drupal variables.');
-
-    $details = $field['storage_details']['drupal_variables'];
-
-    // The field_test storage details are indexed by variable name. The details
-    // are altered, so moon and mars are correct for this test.
-    $this->assertTrue(array_key_exists('moon', $details[FIELD_LOAD_CURRENT]), 'Moon is available in the instance array.');
-    $this->assertTrue(array_key_exists('mars', $details[FIELD_LOAD_REVISION]), 'Mars is available in the instance array.');
-
-    // Test current and revision storage details together because the columns
-    // are the same.
-    foreach ($field['columns'] as $column_name => $attributes) {
-      $this->assertEqual($details[FIELD_LOAD_CURRENT]['moon'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'moon[FIELD_LOAD_CURRENT]')));
-      $this->assertEqual($details[FIELD_LOAD_REVISION]['mars'][$column_name], $column_name, format_string('Column name %value matches the definition in %bin.', array('%value' => $column_name, '%bin' => 'mars[FIELD_LOAD_REVISION]')));
-    }
   }
 
   /**
    * Tests insert and update with empty or NULL fields.
    */
   function testFieldAttachSaveEmptyData() {
-    $entity_type = 'entity_test_rev';
+    $entity_type = 'entity_test';
     $this->createFieldWithInstance('', $entity_type);
 
-    $entity_init = entity_create($entity_type, array('id' => 1, 'revision_id' => 1));
+    $entity_init = entity_create($entity_type, array('id' => 1));
 
     // Insert: Field is NULL.
-    field_cache_clear();
-    $entity = clone($entity_init);
+    $entity = clone $entity_init;
     $entity->{$this->field_name} = NULL;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity->enforceIsNew();
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
 
     // Add some real data.
-    field_cache_clear();
     $entity = clone($entity_init);
     $values = $this->_generateTestFieldValues(1);
     $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
 
     // Update: Field is NULL. Data should be wiped.
-    field_cache_clear();
     $entity = clone($entity_init);
     $entity->{$this->field_name} = NULL;
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: NULL field removes existing values');
 
     // Re-add some data.
-    field_cache_clear();
     $entity = clone($entity_init);
     $values = $this->_generateTestFieldValues(1);
     $entity->{$this->field_name} = $values;
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual($entity->{$this->field_name}->getValue(), $values, 'Field data saved');
 
     // Update: Field is empty array. Data should be wiped.
-    field_cache_clear();
     $entity = clone($entity_init);
     $entity->{$this->field_name} = array();
-    field_attach_update($entity);
-
-    $entity = clone($entity_init);
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Update: empty array removes existing values');
   }
 
@@ -351,11 +232,8 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
     // Insert: Field is NULL.
     $entity = clone($entity_init);
     $entity->getBCEntity()->{$this->field_name} = NULL;
-    field_attach_insert($entity);
-
-    $entity = clone($entity_init);
-    $entity->getBCEntity()->{$this->field_name} = array();
-    field_attach_load($entity_type, array($entity->id() => $entity));
+    $entity->enforceIsNew();
+    $entity = $this->entitySaveReload($entity);
     $this->assertTrue($entity->{$this->field_name}->isEmpty(), 'Insert: NULL field results in no value saved');
 
     // Verify that prepopulated field values are not overwritten by defaults.
@@ -370,53 +248,55 @@ function testFieldAttachSaveEmptyDataDefaultValue() {
   function testFieldAttachDelete() {
     $entity_type = 'entity_test_rev';
     $this->createFieldWithInstance('', $entity_type);
-    $rev[0] = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
+    $vids = array();
 
     // Create revision 0
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
-    $rev[0]->{$this->field_name} = $values;
-    field_attach_insert($rev[0]);
+    $entity->{$this->field_name} = $values;
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
 
     // Create revision 1
-    $rev[1] = entity_create($entity_type, array('id' => 0, 'revision_id' => 1, 'type' => $this->instance['bundle']));
-    $rev[1]->{$this->field_name} = $values;
-    field_attach_update($rev[1]);
+    $entity->setNewRevision();
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
 
     // Create revision 2
-    $rev[2] = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    $rev[2]->{$this->field_name} = $values;
-    field_attach_update($rev[2]);
+    $entity->setNewRevision();
+    $entity->save();
+    $vids[] = $entity->getRevisionId();
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
 
     // Confirm each revision loads
-    foreach (array_keys($rev) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    foreach ($vids as $vid) {
+      $revision = $controller->loadRevision($vid);
+      $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
     }
 
     // Delete revision 1, confirm the other two still load.
-    field_attach_delete_revision($rev[1]);
-    foreach (array(0, 2) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
+    $controller->deleteRevision($vids[1]);
+    $controller->resetCache();
+    foreach (array(0, 2) as $key) {
+      $vid = $vids[$key];
+      $revision = $controller->loadRevision($vid);
+      $this->assertEqual(count($revision->{$this->field_name}), $this->field['cardinality'], "The test entity revision $vid has {$this->field['cardinality']} values.");
     }
 
     // Confirm the current revision still loads
-    $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertEqual(count($read->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
+    $controller->resetCache();
+    $current = $controller->load($entity->id());
+    $this->assertEqual(count($current->{$this->field_name}), $this->field['cardinality'], "The test entity current revision has {$this->field['cardinality']} values.");
 
     // Delete all field data, confirm nothing loads
-    field_attach_delete($rev[2]);
+    $entity->delete();
+    $controller->resetCache();
     foreach (array(0, 1, 2) as $vid) {
-      $read = entity_create($entity_type, array('id' => 0, 'revision_id' => $vid, 'type' => $this->instance['bundle']));
-      field_attach_load_revision($entity_type, array(0 => $read));
-      $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), "The test entity revision $vid is deleted.");
+      $revision = $controller->loadRevision($vid);
+      $this->assertFalse($revision);
     }
-    $read = entity_create($entity_type, array('id' => 0, 'revision_id' => 2, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $read));
-    $this->assertIdentical($read->{$this->field_name}[0]->getValue(), array(), 'The test entity current revision is deleted.');
+    $this->assertFalse($controller->load($entity->id()));
   }
 
   /**
@@ -435,14 +315,11 @@ function testEntityCreateRenameBundle() {
     entity_create('field_instance', $this->instance_definition)->save();
 
     // Save an entity with data in the field.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
     $entity->{$this->field_name} = $values;
-    field_attach_insert($entity);
-
     // Verify the field data is present on load.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $entity = $this->entitySaveReload($entity);
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Data is retrieved for the new bundle");
 
     // Rename the bundle.
@@ -454,8 +331,9 @@ function testEntityCreateRenameBundle() {
     $this->assertIdentical($this->instance['bundle'], $new_bundle, "Bundle name has been updated in the instance.");
 
     // Verify the field data is present on load.
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
+    $entitt = $controller->load($entity->id());
     $this->assertEqual(count($entity->{$this->field_name}), $this->field['cardinality'], "Bundle name has been updated in the field storage");
   }
 
@@ -489,15 +367,13 @@ function testEntityDeleteBundle() {
     entity_create('field_instance', $instance)->save();
 
     // Save an entity with data for both fields
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $values = $this->_generateTestFieldValues($this->field['cardinality']);
     $entity->{$this->field_name} = $values;
     $entity->{$field_name} = $this->_generateTestFieldValues(1);
-    field_attach_insert($entity);
+    $entity = $this->entitySaveReload($entity);
 
     // Verify the fields are present on load
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
     $this->assertEqual(count($entity->{$this->field_name}), 4, 'First field got loaded');
     $this->assertEqual(count($entity->{$field_name}), 1, 'Second field got loaded');
 
@@ -505,8 +381,10 @@ function testEntityDeleteBundle() {
     entity_test_delete_bundle($this->instance['bundle'], $entity_type);
 
     // Verify no data gets loaded
-    $entity = entity_create($entity_type, array('id' => 0, 'revision_id' => 0, 'type' => $this->instance['bundle']));
-    field_attach_load($entity_type, array(0 => $entity));
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
+    $entity= $controller->load($entity->id());
+
     $this->assertTrue(empty($entity->{$this->field_name}), 'No data for first field');
     $this->assertTrue(empty($entity->{$field_name}), 'No data for second field');
 
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 012ee4f..34aeed1 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -32,15 +32,6 @@ function testFieldInfo() {
       $this->assertEqual($info[$t_key]['module'], 'field_test',  'Field type field_test module appears.');
     }
 
-    $storage_info = field_test_field_storage_info();
-    $info = field_info_storage_types();
-    foreach ($storage_info as $s_key => $storage) {
-      foreach ($storage as $key => $val) {
-        $this->assertEqual($info[$s_key][$key], $val, format_string('Storage type %s_key key %key is %value', array('%s_key' => $s_key, '%key' => $key, '%value' => print_r($val, TRUE))));
-      }
-      $this->assertEqual($info[$s_key]['module'], 'field_test',  'Storage type field_test module appears.');
-    }
-
     // Verify that no unexpected instances exist.
     $instances = field_info_instances('entity_test');
     $expected = array();
@@ -192,7 +183,14 @@ function testInstanceDisabledEntityType() {
       'entity_type' => 'comment',
       'bundle' => 'comment_node_article',
     );
-    entity_create('field_instance', $instance_definition)->save();
+    $instance = entity_create('field_instance', $instance_definition);
+    try {
+      $instance->save();
+      $this->fail('No exception was thrown');
+    }
+    catch (\InvalidArgumentException $e) {
+      $this->assertEqual($e->getMessage(), 'The comment entity type does not exist.');
+    }
 
     // Disable coment module. This clears field_info cache.
     module_disable(array('comment'));
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index 4580e7b..1753fd2 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.
@@ -92,6 +92,16 @@ function createFieldWithInstance($suffix = '', $entity_type = 'entity_test', $bu
   }
 
   /**
+   * Save and reload an entity.
+   */
+  protected function entitySaveReload(EntityInterface $entity) {
+    $entity->save();
+    $controller = $this->container->get('plugin.manager.entity')->getStorageController($entity->getType());
+    $controller->resetCache();
+    return $controller->load($entity->id());
+  }
+
+  /**
    * Generate random values for a field_test field.
    *
    * @param $cardinality
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index ebf9722..fb7065b 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -153,8 +153,7 @@ function testTranslatableFieldSaveLoad() {
     // Prepare the field translations.
     $entity_type = 'entity_test';
     field_test_entity_info_translatable($entity_type, TRUE);
-    $id = $revision_id = 1;
-    $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
+    $entity = entity_create($entity_type, array('type' => $this->instance['bundle']));
     $field_translations = array();
     $available_langcodes = field_available_languages($entity_type, $this->field);
     $this->assertTrue(count($available_langcodes) > 1, 'Field is translatable.');
@@ -165,10 +164,7 @@ function testTranslatableFieldSaveLoad() {
     }
 
     // Save and reload the field translations.
-    field_attach_insert($entity);
-    $entity = entity_create($entity_type, array('id' => $id, 'revision_id' => $revision_id, 'type' => $this->instance['bundle']));
-    $entity->langcode->value = reset($available_langcodes);
-    field_attach_load($entity_type, array($id => $entity));
+    $entity = $this->entitySaveReload($entity);
 
     // Check if the correct values were saved/loaded.
     foreach ($field_translations as $langcode => $items) {
@@ -197,9 +193,7 @@ function testTranslatableFieldSaveLoad() {
     asort($translation_langcodes);
     $translation_langcodes = array_values($translation_langcodes);
 
-    $id++;
-    $revision_id++;
-    $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+    $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
     $entity = entity_create($entity_type, $values);
     foreach ($translation_langcodes as $langcode) {
       $values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
@@ -217,9 +211,7 @@ function testTranslatableFieldSaveLoad() {
 
     // Check that explicit empty values are not overridden with default values.
     foreach (array(NULL, array()) as $empty_items) {
-      $id++;
-      $revision_id++;
-      $values = array('id' => $id, 'revision_id' => $revision_id, 'type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
+      $values = array('type' => $instance['bundle'], 'langcode' => $translation_langcodes[0]);
       $entity = entity_create($entity_type, $values);
       foreach ($translation_langcodes as $langcode) {
         $values[$this->field_name][$langcode] = $this->_generateTestFieldValues($this->field['cardinality']);
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
index df93f12..bc9f35b 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationWebTest.php
@@ -33,7 +33,7 @@ class TranslationWebTest extends FieldTestBase {
    *
    * @var string
    */
-  protected $entity_type = 'test_entity';
+  protected $entity_type = 'entity_test_rev';
 
   /**
    * The field to use in this test.
@@ -62,8 +62,6 @@ function setUp() {
 
     $this->field_name = drupal_strtolower($this->randomName() . '_field_name');
 
-    $this->entity_type = 'entity_test_rev';
-
     $field = array(
       'field_name' => $this->field_name,
       'type' => 'test_field',
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/field_sql_storage/field_sql_storage.install b/core/modules/field_sql_storage/field_sql_storage.install
index 8d14ca8..9d0be63 100644
--- a/core/modules/field_sql_storage/field_sql_storage.install
+++ b/core/modules/field_sql_storage/field_sql_storage.install
@@ -8,60 +8,6 @@
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
- * Writes field data directly to SQL storage.
- *
- * @ingroup update_api
- */
-function _update_8000_field_sql_storage_write($entity_type, $bundle, $entity_id, $revision_id, $field_name, $data) {
-  $table_name = "field_data_{$field_name}";
-  $revision_name = "field_revision_{$field_name}";
-
-  db_delete($table_name)
-    ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $entity_id)
-    ->execute();
-  db_delete($revision_name)
-    ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $entity_id)
-    ->condition('revision_id', $revision_id)
-    ->execute();
-
-  $columns = array();
-  foreach ($data as $langcode => $items) {
-    foreach ($items as $delta => $item) {
-      $record = array(
-        'entity_type' => $entity_type,
-        'entity_id' => $entity_id,
-        'revision_id' => $revision_id,
-        'bundle' => $bundle,
-        'delta' => $delta,
-        'langcode' => $langcode,
-      );
-      foreach ($item as $column => $value) {
-        $record[_field_sql_storage_columnname($field_name, $column)] = $value;
-      }
-
-      $records[] = $record;
-      // Record the columns used.
-      $columns += $record;
-    }
-  }
-
-  if ($columns) {
-    $query = db_insert($table_name)->fields(array_keys($columns));
-    $revision_query = db_insert($revision_name)->fields(array_keys($columns));
-    foreach ($records as $record) {
-      $query->values($record);
-      if ($revision_id) {
-        $revision_query->values($record);
-      }
-    }
-    $query->execute();
-    $revision_query->execute();
-  }
-}
-
-/**
  * Implements hook_update_dependencies().
  */
 function field_sql_storage_update_dependencies() {
@@ -72,82 +18,3 @@ function field_sql_storage_update_dependencies() {
   );
   return $dependencies;
 }
-
-/**
- * Renames the 'language' column to 'langcode' in field data tables.
- */
-function field_sql_storage_update_8000(&$sandbox) {
-  // Get field definitions from config, and deleted fields from state system.
-  $config_names = config_get_storage_names_with_prefix('field.field');
-  $deleted_fields = Drupal::state()->get('field.field.deleted') ?: array();
-  // Ditch UUID keys, we will iterate through deleted fields using a numeric
-  // index.
-  $deleted_fields = array_values($deleted_fields);
-
-  if (empty($config_names) && empty($deleted_fields)) {
-    return;
-  }
-
-  if (!isset($sandbox['index'])) {
-    $sandbox['index'] = 0;
-    $sandbox['max'] = count($config_names) + count($deleted_fields);
-  }
-
-  // Retrieve the next field definition. When the index exceeds the number of
-  // 'configuration' fields, use it to iterate on deleted fields.
-  if (isset($config_names[$sandbox['index']])) {
-    $field_config = config($config_names[$sandbox['index']])->get();
-  }
-  else {
-    $field_config = $deleted_fields[$sandbox['index'] - count($config_names)];
-  }
-
-  if ($field_config['storage']['type'] == 'field_sql_storage') {
-    $field = new Field($field_config);
-
-    // Prepare updated schema data structures.
-    $primary_key_data = array(
-      'entity_type',
-      'entity_id',
-      'deleted',
-      'delta',
-      'langcode',
-    );
-    $primary_key_revision = array(
-      'entity_type',
-      'entity_id',
-      'revision_id',
-      'deleted',
-      'delta',
-      'langcode',
-    );
-    $langcode_index = array(
-      'langcode',
-    );
-    $field_langcode = array(
-      'type' => 'varchar',
-      'length' => 32,
-      'not null' => TRUE,
-      'default' => '',
-    );
-
-    $table_info = array(
-      _field_sql_storage_tablename($field) => $primary_key_data,
-      _field_sql_storage_revision_tablename($field) => $primary_key_revision,
-    );
-    foreach ($table_info as $table => $primary_key) {
-      // Do not update tables which already have the langcode column,
-      // created during the upgrade before this update function.
-      if (db_field_exists($table, 'language')) {
-        db_drop_primary_key($table);
-        db_drop_index($table, 'language');
-        db_change_field($table, 'language', 'langcode', $field_langcode);
-        db_add_primary_key($table, $primary_key);
-        db_add_index($table, 'langcode', $langcode_index);
-      }
-    }
-  }
-
-  $sandbox['index']++;
-  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['index'] / $sandbox['max']);
-}
diff --git a/core/modules/file/file.install b/core/modules/file/file.install
index 1f31e43..af757b6 100644
--- a/core/modules/file/file.install
+++ b/core/modules/file/file.install
@@ -5,6 +5,7 @@
  * Install, update and uninstall functions for File module.
  */
 
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
@@ -304,12 +305,12 @@ function file_update_8003() {
   foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) {
     $field_config = config($config_name);
     // Only update file fields that use the default SQL storage.
-    if (in_array($field_config->get('type'), array('file', 'image')) && $field_config->get('storage.type') == 'field_sql_storage') {
+    if (in_array($field_config->get('type'), array('file', 'image'))) {
       $field = new Field($field_config->get());
 
       $tables = array(
-        _field_sql_storage_tablename($field),
-        _field_sql_storage_revision_tablename($field),
+        DatabaseStorageController::fieldTableName($field),
+        DatabaseStorageController::fieldRevisionTableName($field),
       );
 
       foreach ($tables as $table_name) {
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 5f7c9d2..48ca749 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 43d54ce..e999439 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 c3ccc3c..77d4c5b 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/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
index dbc1876..ace2008 100644
--- a/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
+++ b/core/modules/options/lib/Drupal/options/Tests/OptionsWidgetsTest.php
@@ -538,7 +538,7 @@ function testOnOffCheckbox() {
     $fieldUpdate['settings']['allowed_values'] = array(0 => 0, 1 => 'MyOnValue');
     $fieldUpdate->save();
     entity_create('field_instance', array(
-      'field_name' => $this->bool['field_name'],
+      'field_name' => $this->bool->id(),
       'entity_type' => 'node',
       'bundle' => 'page',
     ))->save();
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 e5e4897..25fa742 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -20,7 +20,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');
 
   /**
    * The entity manager service.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
index 01794f0..982a70d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/TreeOutputTest.php
@@ -14,7 +14,7 @@
  */
 class TreeOutputTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'menu_link');
+  public static $modules = array('system', 'menu_link', 'field');
 
   /**
    * Dummy link structure acceptable for menu_tree_output().
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
index b04e2d8..262537b 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
@@ -17,7 +17,7 @@
  */
 class ContextPluginTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'user', 'node');
+  public static $modules = array('system', 'user', 'node', 'field');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
index 79abf93..9011d0c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
@@ -6,7 +6,9 @@
  */
 
 namespace Drupal\system\Tests\Upgrade;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\Core\Language\Language;
+use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Tests upgrade of system variables.
@@ -132,12 +134,6 @@ function testFieldUpgradeToConfig() {
       'module' => 'text',
       'active' => '1',
       'settings' => array(),
-      'storage' => array(
-        'type' => 'field_sql_storage',
-        'module' => 'field_sql_storage',
-        'active' => '1',
-        'settings' => array(),
-      ),
       'locked' => 0,
       'cardinality' => 1,
       'translatable' => 0,
@@ -147,6 +143,7 @@ function testFieldUpgradeToConfig() {
       ),
       'status' => 1,
       'langcode' => 'und',
+      'storageType' => 'sql',
     ));
 
     // Check that the configuration for the instance on article and page nodes
@@ -209,16 +206,18 @@ function testFieldUpgradeToConfig() {
     // The deleted field uuid and deleted instance field_uuid must match.
     $this->assertEqual($deleted_field['uuid'], $deleted_instance['field_uuid']);
 
-    // Check that pre-existing deleted field values are read correctly.
-    $entity = _field_create_entity_from_ids((object) array(
-      'entity_type' => 'node',
-      'bundle' => 'article',
-      'entity_id' => 2,
-      'revision_id' => 2,
-    ));
-    field_attach_load('node', array(2 => $entity), FIELD_LOAD_CURRENT, array('instance' => entity_create('field_instance', $deleted_instance)));
-    $deleted_value = $entity->get('test_deleted_field');
-    $this->assertEqual($deleted_value[Language::LANGCODE_NOT_SPECIFIED][0]['value'], 'Some deleted value');
+    // Check that pre-existing deleted field table is renamed correctly.
+    $field_entity = new Field($deleted_field);
+    $table_name = DatabaseStorageController::fieldTableName($field_entity);
+    $this->assertEqual("field_deleted_data_" . substr(hash('sha256', $deleted_field['uuid']), 0, 10), $table_name);
+    $deleted_value = db_select($table_name, 't')
+      ->fields('t', array(DatabaseStorageController::fieldColumnName($deleted_field['id'], 'value')))
+      ->condition('entity_type', 'node')
+      ->condition('entity_id', 2)
+      ->condition('langcode', Language::LANGCODE_NOT_SPECIFIED)
+      ->execute()
+      ->fetchField();
+    $this->assertEqual($deleted_value, 'Some deleted value');
 
     // Check that creation of a new node works as expected.
     $value = $this->randomName();
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index 860cde3..2d0ecdd 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -6,12 +6,17 @@
  */
 
 use Drupal\Component\Uuid\Uuid;
+use Drupal\Core\Entity\DatabaseStorageController;
 use Drupal\field\Plugin\Core\Entity\Field;
 
 /**
  * Implements hook_uninstall().
  */
 function taxonomy_uninstall() {
+  Drupal::entityManager()->addNamespaces(new ArrayIterator(array(
+    'Drupal\taxonomy' => DRUPAL_ROOT . '/core/modules/taxonomy/lib',
+  )));
+  drupal_classloader_register('taxonomy', 'core/modules/taxonomy');
   // Remove taxonomy_term bundles.
   $config_names = config_get_storage_names_with_prefix('taxonomy.vocabulary.');
   foreach ($config_names as $config_name) {
@@ -356,12 +361,12 @@ function taxonomy_update_8007() {
   foreach (config_get_storage_names_with_prefix('field.field.') as $config_name) {
     $field_config = config($config_name);
     // Only update taxonomy reference fields that use the default SQL storage.
-    if ($field_config->get('type') == 'taxonomy_term_reference' && $field_config->get('storage.type') == 'field_sql_storage') {
+    if ($field_config->get('type') == 'taxonomy_term_reference') {
       $field = new Field($field_config->get());
 
       $tables = array(
-        _field_sql_storage_tablename($field),
-        _field_sql_storage_revision_tablename($field),
+        DatabaseStorageController::fieldTableName($field),
+        DatabaseStorageController::fieldRevisionTableName($field),
       );
 
       foreach ($tables as $table_name) {
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index d4fd3d7..b0b714f 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -1153,7 +1153,7 @@ function taxonomy_build_node_index($node) {
     foreach (field_info_instances('node', $node->type) as $instance) {
       $field_name = $instance['field_name'];
       $field = field_info_field($field_name);
-      if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') {
+      if ($field['module'] == 'taxonomy' && $field->getStorageType() == 'sql') {
         // If a field value is not set in the node object when $node->save() is
         // called, the old value from $node->original is used.
         if (isset($node->{$field_name})) {
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.
