diff --git a/core/core.services.yml b/core/core.services.yml
index 32881da..a37caf4 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -206,6 +206,8 @@ services:
     arguments: ['@container.namespaces', '@service_container', '@module_handler', '@cache.cache', '@language_manager', '@string_translation']
     tags:
       - { name: plugin_manager_cache_clear }
+  entity.schema_builder:
+    class: Drupal\Core\Entity\Schema\EntitySchemaBuilder
   entity.form_builder:
     class: Drupal\Core\Entity\EntityFormBuilder
     arguments: ['@entity.manager', '@form_builder']
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
index 82e5241..d8ad815 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
@@ -205,7 +205,7 @@ public function isTranslatable() {
   /**
    * {@inheritdoc}
    */
-  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
+  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, array $record) {
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
index 31b1613..eb79f62 100644
--- a/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -8,7 +8,10 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Database;
 use Drupal\Core\Entity\Query\QueryInterface;
+use Drupal\Core\Entity\Schema\EntitySchemaBuilderInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Language\Language;
 use Drupal\field\FieldInfo;
 use Drupal\field\FieldConfigUpdateForbiddenException;
@@ -25,7 +28,7 @@
  * This class can be used as-is by most simple entity types. Entity types
  * requiring special handling can extend the class.
  */
-class FieldableDatabaseStorageController extends FieldableEntityStorageControllerBase {
+class FieldableDatabaseStorageController extends FieldableEntityStorageControllerBase implements SqlStorageControllerInterface {
 
   /**
    * Name of entity's revision database table field, if it supports revisions.
@@ -37,6 +40,13 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
   protected $revisionKey = FALSE;
 
   /**
+   * The base table of the entity, if the entity has storage.
+   *
+   * @var string
+   */
+  protected $baseTable;
+
+  /**
    * The table that stores revisions, if the entity supports revisions.
    *
    * @var string
@@ -79,13 +89,29 @@ class FieldableDatabaseStorageController extends FieldableEntityStorageControlle
   protected $fieldInfo;
 
   /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The entity schema builder.
+   *
+   * @var \Drupal\Core\Entity\Schema\EntitySchemaBuilderInterface
+   */
+  protected $schemaBuilder;
+
+  /**
    * {@inheritdoc}
    */
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
     return new static(
       $entity_type,
       $container->get('database'),
-      $container->get('field.info')
+      $container->get('field.info'),
+      $container->get('entity.manager'),
+      $container->get('entity.schema_builder')
     );
   }
 
@@ -98,12 +124,18 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Entity\Schema\EntitySchemaBuilderInterface $schema_builder
+   *   The entity schema builder.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info) {
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, EntityManagerInterface $entity_manager, EntitySchemaBuilderInterface $schema_builder) {
     parent::__construct($entity_type);
 
     $this->database = $database;
     $this->fieldInfo = $field_info;
+    $this->entityManager = $entity_manager;
+    $this->schemaBuilder = $schema_builder;
 
     // Check if the entity type supports IDs.
     if ($this->entityType->hasKey('id')) {
@@ -113,6 +145,11 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa
     // Check if the entity type supports UUIDs.
     $this->uuidKey = $this->entityType->getKey('uuid');
 
+    // Some entity types are not stored in the database at all.
+    if ($base_table = $this->entityType->getBaseTable()) {
+      $this->baseTable = $base_table;
+    }
+
     // Check if the entity type supports revisions.
     if ($this->entityType->hasKey('revision')) {
       $this->revisionKey = $this->entityType->getKey('revision');
@@ -120,12 +157,14 @@ public function __construct(EntityTypeInterface $entity_type, Connection $databa
     }
 
     // Check if the entity type has a dedicated table for fields.
+    // @todo Replace this with a check for the 'language' entity key when
+    //   https://drupal.org/node/2143729 has landed.
     if ($data_table = $this->entityType->getDataTable()) {
       $this->dataTable = $data_table;
       // Entity types having both revision and translation support should always
       // define a revision data table.
-      if ($this->revisionTable && $revision_data_table = $this->entityType->getRevisionDataTable()) {
-        $this->revisionDataTable = $revision_data_table;
+      if ($this->revisionTable) {
+        $this->revisionDataTable = $this->entityType->getRevisionDataTable();
       }
     }
   }
@@ -209,28 +248,32 @@ public function load($id) {
    */
   protected function mapFromStorageRecords(array $records) {
     $entities = array();
-    foreach ($records as $id => $record) {
+    $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId);
+    foreach ($records as $id => $values) {
       $entities[$id] = array();
       // Skip the item delta and item value levels (if possible) but let the
       // field assign the value as suiting. This avoids unnecessary array
       // hierarchies and saves memory here.
-      foreach ($record as $name => $value) {
+      foreach ($values as $name => $value) {
         // Handle columns named [field_name]__[column_name] (e.g for field types
         // that store several properties).
         if ($field_name = strstr($name, '__', TRUE)) {
           $property_name = substr($name, strpos($name, '__') + 2);
-          $entities[$id][$field_name][Language::LANGCODE_DEFAULT][$property_name] = $value;
+
+          $serialize = !empty($field_definitions[$field_name]->getColumns()[$property_name]['serialize']);
+          $entities[$id][$field_name][Language::LANGCODE_DEFAULT][$property_name] = $serialize ? unserialize($value) : $value;
         }
         else {
           // Handle columns named directly after the field (e.g if the field
           // type only stores one property).
-          $entities[$id][$name][Language::LANGCODE_DEFAULT] = $value;
+          $serialize = !empty($field_definitions[$name]->getColumns()[$field_definitions[$name]->getMainPropertyName()]['serialize']);
+          $entities[$id][$name][Language::LANGCODE_DEFAULT] = $serialize ? unserialize($value) : $value;
         }
       }
-      // If we have no multilingual values we can instantiate entity objecs
+      // If we have no multilingual values we can instantiate entity objects
       // right now, otherwise we need to collect all the field values first.
       if (!$this->dataTable) {
-        $bundle = $this->bundleKey ? $record->{$this->bundleKey} : FALSE;
+        $bundle = $this->bundleKey ? $values[$this->bundleKey] : FALSE;
         // Turn the record into an entity class.
         $entities[$id] = new $this->entityClass($entities[$id], $this->entityTypeId, $bundle);
       }
@@ -266,12 +309,16 @@ protected function attachPropertyData(array &$entities) {
 
       $data = $query->execute();
       $field_definitions = \Drupal::entityManager()->getBaseFieldDefinitions($this->entityTypeId);
+
       $translations = array();
       if ($this->revisionDataTable) {
-        $data_column_names = array_flip(array_diff(drupal_schema_fields_sql($this->entityType->getRevisionDataTable()), drupal_schema_fields_sql($this->entityType->getBaseTable())));
+        $base_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_data_table');
+        $data_revision_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_data_table');
+        $data_column_names = array_flip(array_diff(array_keys($data_revision_schema['fields']), array_keys($base_schema['fields'])));
       }
       else {
-        $data_column_names = array_flip(drupal_schema_fields_sql($this->entityType->getDataTable()));
+        $data_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'data_table');
+        $data_column_names = array_flip(array_keys($data_schema['fields']));
       }
 
       foreach ($data as $values) {
@@ -403,11 +450,14 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     }
 
     // Add fields from the {entity} table.
-    $entity_fields = drupal_schema_fields_sql($this->entityType->getBaseTable());
+    $field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityTypeId);
+    $base_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'base_table');
+    $entity_fields = array_keys($base_schema['fields']);
 
     if ($this->revisionTable) {
       // Add all fields from the {entity_revision} table.
-      $entity_revision_fields = drupal_schema_fields_sql($this->entityType->getRevisionTable());
+      $revision_schema = $this->schemaBuilder->getTableSchema($this->entityType, $field_definitions, 'revision_table');
+      $entity_revision_fields = array_keys($revision_schema['fields']);
       $entity_revision_fields = array_combine($entity_revision_fields, $entity_revision_fields);
       // The ID field is provided by entity, so remove it.
       unset($entity_revision_fields[$this->idKey]);
@@ -548,11 +598,16 @@ public function save(EntityInterface $entity) {
       $this->invokeHook('presave', $entity);
 
       // Create the storage record to be saved.
-      $record = $this->mapToStorageRecord($entity);
+      $values = $this->mapToStorageRecord($entity);
 
       if (!$entity->isNew()) {
         if ($entity->isDefaultRevision()) {
-          $return = drupal_write_record($this->entityType->getBaseTable(), $record, $this->idKey);
+          $this->database
+            ->update($this->baseTable)
+            ->fields($values)
+            ->condition($this->idKey, $values[$this->idKey])
+            ->execute();
+          $return = SAVED_UPDATED;
         }
         else {
           // @todo, should a different value be returned when saving an entity
@@ -560,7 +615,7 @@ public function save(EntityInterface $entity) {
           $return = FALSE;
         }
         if ($this->revisionTable) {
-          $record->{$this->revisionKey} = $this->saveRevision($entity);
+          $values[$this->revisionKey] = $this->saveRevision($entity);
         }
         if ($this->dataTable) {
           $this->savePropertyData($entity);
@@ -582,11 +637,20 @@ public function save(EntityInterface $entity) {
         // Ensure the entity is still seen as new after assigning it an id,
         // while storing its data.
         $entity->enforceIsNew();
-        $return = drupal_write_record($this->entityType->getBaseTable(), $record);
-        $entity->{$this->idKey}->value = (string) $record->{$this->idKey};
+        $insert_id = $this->database
+          ->insert($this->baseTable, array('return' => Database::RETURN_INSERT_ID))
+          ->fields($values)
+          ->execute();
+        // Even if this is a new entity, the ID key might have been set in which
+        // case we should not override the provided ID.
+        if (!isset($values[$this->idKey])) {
+          $values[$this->idKey] = $insert_id;
+        }
+        $return = SAVED_NEW;
+        $entity->{$this->idKey}->value = (string) $values[$this->idKey];
         if ($this->revisionTable) {
           $entity->setNewRevision();
-          $record->{$this->revisionKey} = $this->saveRevision($entity);
+          $values[$this->revisionKey] = $this->saveRevision($entity);
         }
         if ($this->dataTable) {
           $this->savePropertyData($entity);
@@ -595,7 +659,6 @@ public function save(EntityInterface $entity) {
           $this->savePropertyData($entity, 'revision_data_table');
         }
 
-
         $entity->enforceIsNew(FALSE);
         $this->invokeFieldMethod('insert', $entity);
         $this->saveFieldItems($entity, FALSE);
@@ -644,8 +707,7 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
 
     foreach ($entity->getTranslationLanguages() as $langcode => $language) {
       $translation = $entity->getTranslation($langcode);
-      $record = $this->mapToDataStorageRecord($translation, $table_key);
-      $values = (array) $record;
+      $values = $this->mapToDataStorageRecord($translation, $table_key);
       $query
         ->fields(array_keys($values))
         ->values($values);
@@ -657,41 +719,47 @@ protected function savePropertyData(EntityInterface $entity, $table_key = 'data_
   /**
    * Maps from an entity object to the storage record.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
    * @param string $table_key
    *   (optional) The entity key identifying the target table. Defaults to
    *   'base_table'.
    *
-   * @return \stdClass
-   *   The record to store.
+   * @return array
+   *   An array of values to store.
    */
-  protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'base_table') {
-    $record = new \stdClass();
+  protected function mapToStorageRecord(ContentEntityInterface $entity, $table_key = 'base_table') {
     $values = array();
     $definitions = $entity->getFieldDefinitions();
-    $schema = drupal_get_schema($this->entityType->get($table_key));
+    $schema = $this->schemaBuilder->getTableSchema($this->entityType, $definitions, $table_key);
     $is_new = $entity->isNew();
 
     $multi_column_fields = array();
-    foreach (drupal_schema_fields_sql($this->entityType->get($table_key)) as $name) {
+    foreach (array_keys($schema['fields']) as $name) {
       // Check for fields which store data in multiple columns and process them
       // separately.
       if ($field = strstr($name, '__', TRUE)) {
         $multi_column_fields[$field] = TRUE;
         continue;
       }
-      $values[$name] = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
+      $value = isset($definitions[$name]) && isset($entity->$name->value) ? $entity->$name->value : NULL;
+
+      // Since this is not a multi-column field we can assume there is only one
+      // column.
+      $serialize = !empty($definitions[$name]->getColumns()[$definitions[$name]->getMainPropertyName()]['serialize']);
+      $values[$name] = $serialize ? serialize($value) : $value;
     }
 
     // Handle fields that store multiple properties and match each property name
     // to its schema column name.
     foreach (array_keys($multi_column_fields) as $field_name) {
+      /** @var \Drupal\Core\Field\FieldItemListInterface $field_items */
       $field_items = $entity->get($field_name);
       $field_value = $field_items->getValue();
-      foreach (array_keys($field_items->getFieldDefinition()->getColumns()) as $field_schema_column) {
-        if (isset($schema['fields'][$field_name . '__' . $field_schema_column])) {
-          $values[$field_name . '__' . $field_schema_column] = isset($field_value[0][$field_schema_column]) ? $field_value[0][$field_schema_column] : NULL;
+      foreach ($field_items->getFieldDefinition()->getColumns() as $column_name => $column_info) {
+        if (isset($schema['fields'][$field_name . '__' . $column_name])) {
+          $value = isset($field_value[0][$column_name]) ? $field_value[0][$column_name] : NULL;
+          $values[$field_name . '__' . $column_name] = !empty($column_info['serialize']) ? serialize($value) : $value;
         }
       }
     }
@@ -700,7 +768,7 @@ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'bas
       // If we are creating a new entity, we must not populate the record with
       // NULL values otherwise defaults would not be applied.
       if (isset($value) || !$is_new) {
-        $record->$field_name = drupal_schema_get_field_value($schema['fields'][$field_name], $value);
+        $record[$field_name] = drupal_schema_get_field_value($schema['fields'][$field_name], $value);
       }
     }
 
@@ -710,59 +778,71 @@ protected function mapToStorageRecord(EntityInterface $entity, $table_key = 'bas
   /**
    * Maps from an entity object to the storage record of the field data.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
    * @param string $table_key
    *   (optional) The entity key identifying the target table. Defaults to
    *   'data_table'.
    *
-   * @return \stdClass
-   *   The record to store.
+   * @return array
+   *   An array of values to store.
    */
-  protected function mapToDataStorageRecord(EntityInterface $entity, $table_key = 'data_table') {
-    $record = $this->mapToStorageRecord($entity, $table_key);
-    $record->langcode = $entity->language()->id;
-    $record->default_langcode = intval($record->langcode == $entity->getUntranslated()->language()->id);
-    return $record;
+  protected function mapToDataStorageRecord(ContentEntityInterface $entity, $table_key = 'data_table') {
+    $values = $this->mapToStorageRecord($entity, $table_key);
+    $values['langcode'] = $entity->language()->id;
+    $values['default_langcode'] = intval($values['langcode'] == $entity->getUntranslated()->language()->id);
+    return $values;
   }
 
   /**
    * Saves an entity revision.
    *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
    *   The entity object.
    *
    * @return int
    *   The revision id.
    */
-  protected function saveRevision(EntityInterface $entity) {
-    $record = $this->mapToStorageRecord($entity, 'revision_table');
+  protected function saveRevision(ContentEntityInterface $entity) {
+    $values = $this->mapToStorageRecord($entity, 'revision_table');
 
     // When saving a new revision, set any existing revision ID to NULL so as to
     // ensure that a new revision will actually be created.
-    if ($entity->isNewRevision() && isset($record->{$this->revisionKey})) {
-      $record->{$this->revisionKey} = NULL;
+    if ($entity->isNewRevision() && isset($values[$this->revisionKey])) {
+      $values[$this->revisionKey] = NULL;
     }
 
-    $entity->preSaveRevision($this, $record);
+    $entity->preSaveRevision($this, $values);
 
     if ($entity->isNewRevision()) {
-      drupal_write_record($this->revisionTable, $record);
+      $insert_id = $this->database
+        ->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID))
+        ->fields($values)
+        ->execute();
+      // Even if this is a new revsision, the revision ID key might have been
+      // set in which case we should not override the provided revision ID.
+      if (!isset($values[$this->revisionKey])) {
+        $values[$this->revisionKey] = $insert_id;;
+      }
       if ($entity->isDefaultRevision()) {
         $this->database->update($this->entityType->getBaseTable())
-          ->fields(array($this->revisionKey => $record->{$this->revisionKey}))
-          ->condition($this->idKey, $record->{$this->idKey})
+          ->fields(array($this->revisionKey => $values[$this->revisionKey]))
+          ->condition($this->idKey, $values[$this->idKey])
           ->execute();
       }
     }
     else {
-      drupal_write_record($this->revisionTable, $record, $this->revisionKey);
+      $this->database
+        ->update($this->revisionTable)
+        ->fields($values)
+        ->condition($this->revisionKey, $values[$this->revisionKey])
+        ->execute();
     }
 
     // Make sure to update the new revision key for the entity.
-    $entity->{$this->revisionKey}->value = $record->{$this->revisionKey};
+    $entity->{$this->revisionKey}->value = $values[$this->revisionKey];
 
-    return $record->{$this->revisionKey};
+    return $values[$this->revisionKey];
   }
 
   /**
@@ -893,7 +973,7 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) {
         foreach ($items as $delta => $item) {
           // We now know we have someting to insert.
           $do_insert = TRUE;
-          $record = array(
+          $values = array(
             'entity_id' => $id,
             'revision_id' => $vid,
             'bundle' => $bundle,
@@ -903,10 +983,10 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) {
           foreach ($field->getColumns() as $column => $attributes) {
             $column_name = static::_fieldColumnName($field, $column);
             // Serialize the value if specified in the column schema.
-            $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
+            $values[$column_name] = !empty($attributes['serialize']) ? serialize($item->$column) : $item->$column;
           }
-          $query->values($record);
-          $revision_query->values($record);
+          $query->values($values);
+          $revision_query->values($values);
 
           if ($field->getCardinality() != FieldConfigInterface::CARDINALITY_UNLIMITED && ++$delta_count == $field->getCardinality()) {
             break;
@@ -1456,4 +1536,11 @@ static public function _fieldColumnName(FieldConfigInterface $field, $column) {
     return in_array($column, FieldConfig::getReservedColumns()) ? $column : $field->getName() . '_' . $column;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getSchema() {
+    return $this->schemaBuilder->getAllSchema($this->entityType, $this->entityManager->getBaseFieldDefinitions($this->entityTypeId));
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/RevisionableInterface.php b/core/lib/Drupal/Core/Entity/RevisionableInterface.php
index d246d24..b329ab3 100644
--- a/core/lib/Drupal/Core/Entity/RevisionableInterface.php
+++ b/core/lib/Drupal/Core/Entity/RevisionableInterface.php
@@ -58,9 +58,9 @@ public function isDefaultRevision($new_value = NULL);
    *
    * @param EntityStorageControllerInterface $storage_controller
    *   The entity storage controller object.
-   * @param \stdClass $record
-   *   The revision object.
+   * @param array $values
+   *   An array of values to save, keyed by field name.
    */
-  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record);
+  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, array $values);
 
 }
diff --git a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php
new file mode 100644
index 0000000..1523d58
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilder.php
@@ -0,0 +1,384 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Schema\EntitySchemaBuilder.
+ */
+
+namespace Drupal\Core\Entity\Schema;
+use Drupal\Core\Entity\ContentEntityType;
+use Drupal\Core\Field\FieldDefinitionInterface;
+
+/**
+ * An entity schema builder that supports revisionable, translatable entities.
+ */
+class EntitySchemaBuilder implements EntitySchemaBuilderInterface {
+
+  /**
+   * A static cache of the generated schema arrays.
+   *
+   * @var array
+   */
+  protected $schema;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAllSchema(ContentEntityType $entity_type, array $field_definitions) {
+    // Prepare basic information about the entity type.
+    $tables = $this->getTables($entity_type);
+    // If this entity type does not support storage, no schema information can
+    // be collected.
+    if (empty($tables)) {
+      $this->schema[$entity_type->id()] = NULL;
+    }
+
+    if (!isset($this->schema[$entity_type->id()])) {
+      // @todo Remove this. This is currently needed because some entity types
+      //   declare a schema that does not match their field definitions.
+      //   Therefore we use the legacy schema if it's still declared. This
+      //   allows to iteratively enable the automatic schema generation for
+      //   all entity types.
+      if ($base_schema = drupal_get_schema($tables['base_table'])) {
+        $schema[$tables['base_table']] = $base_schema;
+        unset($tables['base_table']);
+        foreach ($tables as $table) {
+          $schema[$table] = drupal_get_schema($table);
+        }
+      }
+
+      else {
+        $entity_keys = array_filter(array(
+          'id' => $entity_type->getKey('id'),
+          'revision' => $entity_type->getKey('revision'),
+          'bundle' => $entity_type->getKey('bundle'),
+          'uuid' => $entity_type->getKey('uuid'),
+        ));
+        // @todo This will become unnecessary once https://drupal.org/node/2143729
+        //   has landed.
+        if (isset($tables['data_table'])) {
+          $entity_keys['language'] = 'langcode';
+          $entity_keys['default_language'] = 'default_langcode';
+        }
+
+        // Initialize the table schema.
+        $schema[$tables['base_table']] = $this->initializeBaseTable($entity_type, $tables, $entity_keys);
+        if (isset($tables['revision_table'])) {
+          $schema[$tables['revision_table']] = $this->initializeRevisionTable($entity_type, $tables, $entity_keys);
+        }
+        if (isset($tables['data_table'])) {
+          $schema[$tables['data_table']] = $this->initializeDataTable($entity_type, $tables, $entity_keys);
+        }
+        if (isset($tables['revision_data_table'])) {
+          $schema[$tables['revision_data_table']] = $this->initializeRevisionDataTable($entity_type, $tables, $entity_keys);
+        }
+
+        // Add the schema from field definitions.
+        foreach ($field_definitions as $field_name => $field_definition) {
+          // Computed fields are not stored at all. Fields can also have a custom
+          // storage that is handled manually by the storage controller or by
+          // contributed modules, so ignore those as well.
+          // @todo Check for custom storage when https://drupal.org/node/2143069
+          //   lands.
+          if ($field_definition->isComputed()) {
+            continue;
+          }
+
+          // The entity key fields need to be handled separately.
+          if (in_array($field_name, $entity_keys)) {
+            $key = array_search($field_name, $entity_keys);
+            if ($key !== 'default_language') {
+              $this->addFieldSchema($schema[$tables['base_table']], $field_name, $field_definition);
+            }
+            if (isset($tables['revision_table']) && in_array($key, array('id', 'revision', 'language'))) {
+              $this->addFieldSchema($schema[$tables['revision_table']], $field_name, $field_definition);
+            }
+            if (isset($tables['data_table']) && in_array($key, array('id', 'revision', 'language', 'default_language'))) {
+              $this->addFieldSchema($schema[$tables['data_table']], $field_name, $field_definition);
+            }
+            if (isset($tables['revision_data_table']) && in_array($key, array('id', 'revision', 'language', 'default_language'))) {
+              $this->addFieldSchema($schema[$tables['revision_data_table']], $field_name, $field_definition);
+            }
+          }
+          // For all other fields, the storage depends on their translatability.
+          else {
+            if (!isset($tables['data_table']) || !$field_definition->isTranslatable()) {
+              $this->addFieldSchema($schema[$tables['base_table']], $field_name, $field_definition);
+              if (isset($tables['revision_table'])) {
+                $this->addFieldSchema($schema[$tables['revision_table']], $field_name, $field_definition);
+              }
+            }
+            else {
+              $this->addFieldSchema($schema[$tables['data_table']], $field_name, $field_definition);
+              if (isset($tables['revision_data_table'])) {
+                $this->addFieldSchema($schema[$tables['revision_data_table']], $field_name, $field_definition);
+              }
+            }
+          }
+        }
+
+        // Process tables after having gathered field information.
+        $this->processBaseTable($schema[$tables['base_table']], $entity_type, $entity_keys);
+        if (isset($tables['revision_table'])) {
+          $this->processRevisionTable($schema[$tables['revision_table']], $entity_type, $entity_keys);
+        }
+        if (isset($tables['data_table'])) {
+          $this->processDataTable($schema[$tables['data_table']], $entity_type, $entity_keys);
+        }
+        if (isset($tables['revision_data_table'])) {
+          $this->processRevisionDataTable($schema[$tables['revision_data_table']], $entity_type, $entity_keys);
+        }
+      }
+
+      $this->schema[$entity_type->id()] = $schema;
+    }
+
+    return $this->schema[$entity_type->id()];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTableSchema(ContentEntityType $entity_type, array $field_definitions, $table_key) {
+    $tables = $this->getTables($entity_type);
+    if (isset($tables[$table_key])) {
+      $schema = $this->getAllSchema($entity_type, $field_definitions);
+      if (isset($schema[$tables[$table_key]])) {
+        return $schema[$tables[$table_key]];
+      }
+    }
+  }
+
+  /**
+   * Gets a list of entity type tables.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type.
+   *
+   * @return array
+   *   A list of entity type tables, keyed by table key.
+   */
+  protected function getTables(ContentEntityType $entity_type) {
+    return array_filter(array(
+      'base_table' => $entity_type->getBaseTable(),
+      'revision_table' => $entity_type->getRevisionTable(),
+      'data_table' => $entity_type->getDataTable(),
+      'revision_data_table' => $entity_type->getRevisionDataTable(),
+    ));
+  }
+
+  /**
+   * Returns the schema for a single field definition.
+   *
+   * @param array $schema
+   *   The table schema to add the field schema to, passed by reference.
+   * @param string $field_name
+   *   The name of the field.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+   *   The field definition to return the schema for.
+   */
+  protected function addFieldSchema(array &$schema, $field_name, FieldDefinitionInterface $field_definition) {
+    $field_schema = $field_definition->getSchema();
+    if (count($field_schema['columns']) == 1) {
+      $schema['fields'][$field_name] = $field_schema['columns'][$field_definition->getMainPropertyName()];
+      $schema['fields'][$field_name]['description'] = $field_definition->getDescription();
+
+      if (!empty($field_schema['unique keys'])) {
+        $schema['unique keys'][$field_name] = array($field_name);
+      }
+      if (!empty($field_schema['indexes'])) {
+        // @todo Support indexes specified as an array of column name and
+        //   length.
+        $schema['indexes'][$field_name] = array($field_name);
+      }
+      if (!empty($field_schema['foreign keys'])) {
+        $schema += array('foreign keys' => array());
+        $schema['foreign keys'] += $field_schema['foreign keys'];
+      }
+    }
+    else {
+      foreach ($field_schema['columns'] as $column_name => $column_schema) {
+        $schema['fields'][$field_name . '__' . $column_name] = $column_schema;
+      }
+
+      // @todo Descriptions, unique keys, indexes and foreign keys.
+    }
+  }
+
+  /**
+   * Initializes common information for a base table.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this base table belongs to.
+   * @param array $tables
+   *   An array of tables for this entity type, keyed by table type.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function initializeBaseTable(ContentEntityType $entity_type, array $tables, array $entity_keys) {
+    return array(
+      'description' => "The base table for {$entity_type->id()} entities.",
+      'primary key' => array($entity_keys['id']),
+    );
+  }
+
+  /**
+   * Initializes common information for a revision table.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this revision table belongs to.
+   * @param array $tables
+   *   An array of tables for this entity type, keyed by table type.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the revision table.
+   */
+  protected function initializeRevisionTable(ContentEntityType $entity_type, array $tables, array $entity_keys) {
+    return array(
+      'description' => "The revision table for {$entity_type->id()} entities.",
+      'primary key' => array($entity_keys['revision']),
+      'foreign keys' => array(
+        $entity_keys['id'] => array(
+          'table' => $tables['base_table'],
+          'columns' => array($entity_keys['id'] => $entity_keys['id']),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Initializes common information for a data table.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this data table belongs to.
+   * @param array $tables
+   *   An array of tables for this entity type, keyed by table type.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the data table.
+   */
+  protected function initializeDataTable(ContentEntityType $entity_type, array $tables, array $entity_keys) {
+    return array(
+      'description' => "The data table for {$entity_type->id()} entities.",
+      'primary key' => array($entity_keys['id'], $entity_keys['language']),
+      'foreign keys' => array(
+        $entity_keys['id'] => array(
+          'table' => $tables['base_table'],
+          'columns' => array($entity_keys['id'] => $entity_keys['id']),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * Initializes common information for a revision data table.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this revision data table belongs to.
+   * @param array $tables
+   *   An array of tables for this entity type, keyed by table type.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the revision data table.
+   */
+  protected function initializeRevisionDataTable(ContentEntityType $entity_type, array $tables, array $entity_keys) {
+    return array(
+      'description' => "The revision data table for {$entity_type->id()} entities.",
+      'primary key' => array($entity_keys['revision'], $entity_keys['language']),
+      'foreign keys' => array(
+        $entity_keys['id'] => array(
+          'table' => $tables['base_table'],
+          'columns' => array($entity_keys['id'] => $entity_keys['id']),
+        ),
+        $entity_keys['revision'] => array(
+          'table' => $tables['revision_table'],
+          'columns' => array($entity_keys['revision'] => $entity_keys['revision']),
+        )
+      ),
+    );
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this base table belongs to.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processBaseTable(array &$schema, ContentEntityType $entity_type, array $entity_keys) {
+    // Change the ID field in the base table to 'serial' if it is 'int'.
+    if ($schema['fields'][$entity_keys['id']]['type'] == 'int') {
+      $schema['fields'][$entity_keys['id']]['type'] = 'serial';
+      unset($schema['fields'][$entity_keys['id']]['default']);
+    }
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this base table belongs to.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processRevisionTable(array &$schema, ContentEntityType $entity_type, array $entity_keys) {
+    // Change the revision ID field in the revision table 'serial' if it is
+    // 'int'.
+    if ($schema['fields'][$entity_keys['revision']]['type'] == 'int') {
+      $schema['fields'][$entity_keys['revision']]['type'] = 'serial';
+      unset($schema['fields'][$entity_keys['revision']]['default']);
+    }
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this base table belongs to.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processDataTable(array &$schema, ContentEntityType $entity_type, array $entity_keys) {
+  }
+
+  /**
+   * Processes the gathered schema for a base table.
+   *
+   * @param array $schema
+   *   The table schema, passed by reference.
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type this base table belongs to.
+   * @param array $entity_keys
+   *   An array of relevant entity keys.
+   *
+   * @return array
+   *   A partial schema array for the base table.
+   */
+  protected function processRevisionDataTable(array &$schema, ContentEntityType $entity_type, array $entity_keys) {
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilderInterface.php b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilderInterface.php
new file mode 100644
index 0000000..fbbe01f
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Schema/EntitySchemaBuilderInterface.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Created by PhpStorm.
+ * User: tobias
+ * Date: 24.03.14
+ * Time: 14:23
+ */
+namespace Drupal\Core\Entity\Schema;
+
+use Drupal\Core\Entity\ContentEntityType;
+
+
+/**
+ * A common interface for building the database schema for entities.
+ */
+interface EntitySchemaBuilderInterface {
+
+  /**
+   * Gets the full schema array for a given entity type.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type to return the schema for.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions
+   *   The field definitions of the entity type, keyed by field name.
+   *
+   * @return array
+   *   A schema array for the entity type's tables.
+   */
+  public function getAllSchema(ContentEntityType $entity_type, array $field_definitions);
+
+  /**
+   * Gets the schema array for a single table of a given entity type.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityType $entity_type
+   *   The entity type to return the schema for.
+   * @param \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions
+   *   The field definitions of the entity type, keyed by field name.
+   * @param string $table_key
+   *   The table key of the table to get the schema for.
+   *
+   * @return array|null
+   *   A schema array for the entity type table, or NULL if the given entity
+   *   type has no schema for the given table key.
+   */
+  public function getTableSchema(ContentEntityType $entity_type, array $field_definitions, $table_key);
+
+}
diff --git a/core/lib/Drupal/Core/Entity/SqlStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/SqlStorageControllerInterface.php
new file mode 100644
index 0000000..2e7df52
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/SqlStorageControllerInterface.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\SchemaStorageInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * A common interface for SQL-based storage controllers.
+ */
+interface SqlStorageControllerInterface {
+
+ /**
+  * Returns the schema of the entity type.
+  *
+  * @return array
+  *   A schema array for all of the entity type's tables.
+  */
+  public function getSchema();
+
+}
diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php
index a20b9b9..1bd0116 100644
--- a/core/lib/Drupal/Core/Extension/ModuleHandler.php
+++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php
@@ -8,6 +8,9 @@
 namespace Drupal\Core\Extension;
 
 use Drupal\Component\Graph\Graph;
+use Drupal\Core\Entity\ContentEntityType;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\SqlStorageControllerInterface;
 use Symfony\Component\Yaml\Parser;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\CacheBackendInterface;
@@ -662,6 +665,25 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
         // Clear plugin manager caches.
         \Drupal::getContainer()->get('plugin.cache_clearer')->clearCachedDefinitions();
 
+        // Install entity type tables.
+        $entity_manager = \Drupal::entityManager();
+        $schema = \Drupal::database()->schema();
+        $entity_types = array_filter($entity_manager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($module) {
+          ($entity_type instanceof ContentEntityType) && ($entity_type->getProvider() == $module);
+        });
+        foreach ($entity_types as $entity_type) {
+          $storage_controller = $entity_manager->getStorageController($entity_type->id());
+          if ($storage_controller instanceof SqlStorageControllerInterface) {
+            foreach ($storage_controller->getSchema() as $table_name => $table_schema) {
+              // @todo Remove this check once all entity types have been converted
+              //   to an automatic schema.
+              if (!$schema->tableExists($table_name)) {
+                $schema->createTable($table_name, $table_schema);
+              }
+            }
+          }
+        }
+
         // Set the schema version to the number of the last update provided by
         // the module, or the minimum core schema version.
         $version = \Drupal::CORE_MINIMUM_SCHEMA_VERSION;
@@ -766,6 +788,25 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) {
       // Remove the schema.
       drupal_uninstall_schema($module);
 
+      // Remove entity type tables.
+      $entity_manager = \Drupal::entityManager();
+      $schema = \Drupal::database()->schema();
+      $entity_types = array_filter($entity_manager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($module) {
+        ($entity_type instanceof ContentEntityType) && ($entity_type->getProvider() == $module);
+      });
+      foreach ($entity_types as $entity_type) {
+        $storage_controller = $entity_manager->getStorageController($entity_type->id());
+        if ($storage_controller instanceof SqlStorageControllerInterface) {
+          foreach ($storage_controller->getSchema() as $table_name => $table_schema) {
+            // @todo Remove this check once all entity types have been converted
+            //   to an automatic schema.
+            if ($schema->tableExists($table_name)) {
+              $schema->dropTable($table_name, $table_schema);
+            }
+          }
+        }
+      }
+
       // Remove the module's entry from the config.
       $module_config->clear("enabled.$module")->save();
 
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
index ea20a79..3a21001 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
@@ -118,8 +118,8 @@ public function getInstances() {
   /**
    * {@inheritdoc}
    */
-  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
-    parent::preSaveRevision($storage_controller, $record);
+  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, array $values) {
+    parent::preSaveRevision($storage_controller, $values);
 
     if ($this->isNewRevision()) {
       // When inserting either a new custom block or a new custom_block
@@ -130,14 +130,14 @@ public function preSaveRevision(EntityStorageControllerInterface $storage_contro
       // that it is at least an empty string in that case.
       // @todo: Make the {block_custom_revision}.log column nullable so that we
       // can remove this check.
-      if (!isset($record->log)) {
-        $record->log = '';
+      if (!isset($values['log'])) {
+        $values['log'] = '';
       }
     }
-    elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
+    elseif (isset($this->original) && (!isset($values['log']) || $values['log'] === '')) {
       // If we are updating an existing custom_block without adding a new
       // revision and the user did not supply a log, keep the existing one.
-      $record->log = $this->original->getRevisionLog();
+      $values['log'] = $this->original->getRevisionLog();
     }
   }
 
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index da4b174..7c8aad0 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -78,8 +78,8 @@ public function getRevisionId() {
   /**
    * {@inheritdoc}
    */
-  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
-    parent::preSaveRevision($storage_controller, $record);
+  public function preSaveRevision(EntityStorageControllerInterface $storage_controller, array $values) {
+    parent::preSaveRevision($storage_controller, $values);
 
     if ($this->newRevision) {
       // When inserting either a new node or a new node revision, $node->log
@@ -90,16 +90,16 @@ public function preSaveRevision(EntityStorageControllerInterface $storage_contro
       // an empty string in that case.
       // @todo Make the {node_field_revision}.log column nullable so that we
       //   can remove this check.
-      if (!isset($record->log)) {
-        $record->log = '';
+      if (!isset($values['log'])) {
+        $values['log'] = '';
       }
     }
-    elseif (isset($this->original) && (!isset($record->log) || $record->log === '')) {
+    elseif (isset($this->original) && (!isset($values['log']) || $values['log'] === '')) {
       // If we are updating an existing node without adding a new revision, we
       // need to make sure $entity->log is reset whenever it is empty.
       // Therefore, this code allows us to avoid clobbering an existing log
       // entry with an empty one.
-      $record->log = $this->original->log->value;
+      $values['log'] = $this->original->log->value;
     }
   }
 
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
index 56d4839..a3a3046 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Entity/Shortcut.php
@@ -95,8 +95,7 @@ public function setRouteName($route_name) {
    * {@inheritdoc}
    */
   public function getRouteParams() {
-    $value = $this->get('route_parameters')->getValue();
-    return reset($value);
+    return $this->get('route_parameters')->getValue();
   }
 
   /**
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
index e3c916d..c5f1388 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\simpletest;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Entity\SqlStorageControllerInterface;
 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
 use Drupal\Core\Language\Language;
 use Symfony\Component\DependencyInjection\Reference;
@@ -335,6 +337,36 @@ protected function installSchema($module, $tables) {
   }
 
   /**
+   * Installs the tables for a specific entity type.
+   *
+   * @param string $entity_type_id
+   *   The ID of the entity type.
+   */
+  protected function installEntitySchema($entity_type_id) {
+    /** @var \Drupal\Core\Entity\EntityManagerInterface $entity_manager */
+    $entity_manager = $this->container->get('entity.manager');
+    /** @var \Drupal\Core\Database\Schema $schema_handler */
+    $schema_handler = $this->container->get('database')->schema();
+
+    $storage_controller = $entity_manager->getStorageController($entity_type_id);
+    if ($storage_controller instanceof SqlStorageControllerInterface) {
+      $schema = $storage_controller->getSchema();
+      foreach ($schema as $table_name => $table_schema) {
+        $schema_handler->createTable($table_name, $table_schema);
+      }
+      $this->pass(String::format('Installed entity type tables for the %entity_type entity type: %tables', array(
+        '%entity_type' => $entity_type_id,
+        '%tables' => '{' . implode('}, {', array_keys($schema)) . '}',
+      )));
+    }
+    else {
+      throw new \RuntimeException(String::format('Entity type %entity_type does not support automatic schema installation.', array(
+        '%entity-type' => $entity_type_id,
+      )));
+    }
+  }
+
+  /**
    * Enables modules for this test.
    *
    * @param array $modules
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
index a9f0105..5d2364c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiTest.php
@@ -25,16 +25,10 @@ public static function getInfo() {
 
   public function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index 5dcfd38..f691068 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -42,16 +42,10 @@ public function setUp() {
     parent::setUp();
     $this->installSchema('user', array('users_data'));
     $this->installSchema('node', array('node', 'node_revision', 'node_field_data', 'node_field_revision', 'node_access'));
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
 
     // Create the test field.
     entity_test_install();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
index 1264577..3982b2f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
@@ -50,16 +50,10 @@ function setUp() {
 
     $this->languageManager = $this->container->get('language_manager');
 
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision',
-    ));
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
+
     $this->installConfig(array('language'));
 
     // Create the test field.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
index c01a2e2..31e16d0 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityQueryTest.php
@@ -57,7 +57,9 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision'));
+
+    $this->installEntitySchema('entity_test_mulrev');
+
     $this->installConfig(array('language'));
 
     $figures = drupal_strtolower($this->randomName());
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
index ec7134c..a0ec726 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUUIDTest.php
@@ -23,16 +23,9 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
 
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision',
-    ));
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
   }
 
   /**
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 8537fbb..15f2bed 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -44,7 +44,9 @@ public function setUp() {
 
     $this->installSchema('user', array('users', 'users_roles'));
     $this->installSchema('system', 'sequences');
-    $this->installSchema('entity_test', 'entity_test');
+
+    $this->installEntitySchema('entity_test');
+
     $this->installConfig(array('field'));
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
index 118abe9..169ab3d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityValidationTest.php
@@ -38,16 +38,10 @@ public static function getInfo() {
   public function setUp() {
     parent::setUp();
     $this->installSchema('user', array('users_data'));
-    $this->installSchema('entity_test', array(
-      'entity_test_mul',
-      'entity_test_mul_property_data',
-      'entity_test_rev',
-      'entity_test_rev_revision',
-      'entity_test_mulrev',
-      'entity_test_mulrev_revision',
-      'entity_test_mulrev_property_data',
-      'entity_test_mulrev_property_revision'
-    ));
+
+    $this->installEntitySchema('entity_test_rev');
+    $this->installEntitySchema('entity_test_mul');
+    $this->installEntitySchema('entity_test_mulrev');
 
     // Create the test field.
     entity_test_install();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
index 8894088..aeb6542 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldSqlStorageTest.php
@@ -66,8 +66,8 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->installSchema('entity_test', array('entity_test_rev', 'entity_test_rev_revision'));
-    $entity_type = 'entity_test_rev';
+
+    $this->installEntitySchema('entity_test_rev');
 
     $this->field_name = strtolower($this->randomName());
     $this->field_cardinality = 4;
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index 9e48029..a105f95 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -9,433 +9,36 @@
  * Implements hook_install().
  */
 function entity_test_install() {
-  $entity_types = array(
+  $entity_manager = \Drupal::entityManager();
+
+  $entity_type_ids = array(
     'entity_test',
     'entity_test_rev',
     'entity_test_mul',
     'entity_test_mulrev',
   );
-  foreach ($entity_types as $entity_type) {
+  foreach ($entity_type_ids as $entity_type_id) {
+    $storage_controller = $entity_manager->getStorageController($entity_type_id);
+
     // Auto-create fields for testing.
-    entity_create('field_config', array(
+    $storage_controller->create(array(
       'name' => 'field_test_text',
-      'entity_type' => $entity_type,
+      'entity_type' => $entity_type_id,
+      'user_id' => 1,
       'type' => 'text',
       'cardinality' => 1,
       'translatable' => FALSE,
     ))->save();
-    entity_create('field_instance_config', array(
-      'entity_type' => $entity_type,
+    $storage_controller->create(array(
+      'entity_type' => $entity_type_id,
+      'user_id' => 1,
       'field_name' => 'field_test_text',
-      'bundle' => $entity_type,
+      'bundle' => $entity_type_id,
       'label' => 'Test text-field',
     ))->save();
 
-    entity_get_form_display($entity_type, $entity_type, 'default')
+    entity_get_form_display($entity_type_id, $entity_type_id, 'default')
       ->setComponent('field_test_text', array('type' => 'text_textfield'))
       ->save();
   }
 }
-
-/**
- * Implements hook_schema().
- */
-function entity_test_schema() {
-  // Schema for simple entity.
-  $schema['entity_test'] = array(
-    'description' => 'Stores entity_test items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of the original variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-
-  // Schema for entity with revisions.
-  $schema['entity_test_rev'] = array(
-    'description' => 'Stores entity_test_rev items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_rev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_rev_revision'] = array(
-    'description' => 'Stores entity_test_rev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_rev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_rev' => 'id'),
-    ),
-    'primary key' => array('revision_id'),
-  );
-
-  // Schema for entity with data table.
-  $schema['entity_test_mul'] = array(
-    'description' => 'Stores entity_test_mul items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of the original variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_mul_property_data'] = array(
-    'description' => 'Stores entity_test_mul item properties.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mul}.id of the test entity.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => FALSE,
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mul' => 'id'),
-    ),
-    'primary key' => array('id', 'langcode'),
-  );
-
-  // Schema for entity with data table and revisions.
-  $schema['entity_test_mulrev'] = array(
-    'description' => 'Stores entity_test_mulrev items.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'serial',
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique entity-test item ID.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'uuid' => array(
-        'description' => 'Unique Key: Universally unique identifier for this entity.',
-        'type' => 'varchar',
-        'length' => 128,
-        'not null' => FALSE,
-      ),
-      'type' => array(
-        'description' => 'The bundle of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'primary key' => array('id'),
-    'unique keys' => array(
-      'uuid' => array('uuid'),
-    ),
-  );
-  $schema['entity_test_mulrev_revision'] = array(
-    'description' => 'Stores entity_test_rev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_rev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'serial',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-    ),
-    'foreign keys' => array(
-      'id' => array('entity_test_rev' => 'id'),
-    ),
-    'primary key' => array('revision_id'),
-  );
-  $schema['entity_test_mulrev_property_data'] = array(
-    'description' => 'Stores entity_test_mulrev item properties.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mulrev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'description' => 'The current {entity_test_mulrev_property_revision}.revision_id version identifier.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mulrev' => 'id'),
-    ),
-    'primary key' => array('id', 'langcode'),
-  );
-  $schema['entity_test_mulrev_property_revision'] = array(
-    'description' => 'Stores entity_test_mulrev item property revisions.',
-    'fields' => array(
-      'id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The {entity_test_mulrev}.id of the test entity.',
-      ),
-      'revision_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'description' => 'The primary identifier for this version.',
-      ),
-      'langcode' => array(
-        'description' => 'The {language}.langcode of this variant of this test entity.',
-        'type' => 'varchar',
-        'length' => 12,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'default_langcode' => array(
-        'description' => 'Boolean indicating whether the current variant is in the original entity language.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 1,
-      ),
-      'name' => array(
-        'description' => 'The name of the test entity.',
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'user_id' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => FALSE,
-        'default' => NULL,
-        'description' => 'The {users}.uid of the associated user.',
-      ),
-    ),
-    'indexes' => array(
-      'user_id' => array('user_id'),
-    ),
-    'foreign keys' => array(
-      'user_id' => array('users' => 'uid'),
-      'id' => array('entity_test_mulrev' => 'id'),
-    ),
-    'primary key' => array('revision_id', 'langcode'),
-  );
-
-  return $schema;
-}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 357b77e..49512d4 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -134,7 +134,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
     $fields['type'] = FieldDefinition::create('string')
       ->setLabel(t('Type'))
       ->setDescription(t('The bundle of the test entity.'))
-      ->setRequired(TRUE);
+      ->setRequired(TRUE)
+      ->setSetting('max_length', 32);
 
     $fields['user_id'] = FieldDefinition::create('entity_reference')
       ->setLabel(t('User ID'))
@@ -142,6 +143,10 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
       ->setSettings(array('target_type' => 'user'))
       ->setTranslatable(TRUE);
 
+    $fields['field_test_text'] = FieldDefinition::create('text')
+      ->setLabel(t('Test text'))
+      ->setDescription(t('A test text field'));
+
     return $fields;
   }
 
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
index 481711f..e881b9a 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestLabelCallback.php
@@ -15,7 +15,6 @@
  *   label = @Translation("Entity test label callback"),
  *   field_cache = FALSE,
  *   base_table = "entity_test",
- *   revision_table = "entity_test_revision",
  *   label_callback = "entity_test_label_callback",
  *   fieldable = TRUE,
  *   entity_keys = {
diff --git a/core/modules/user/lib/Drupal/user/UserStorageController.php b/core/modules/user/lib/Drupal/user/UserStorageController.php
index 0694019..aca9838 100644
--- a/core/modules/user/lib/Drupal/user/UserStorageController.php
+++ b/core/modules/user/lib/Drupal/user/UserStorageController.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Uuid\UuidInterface;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Schema\EntitySchemaBuilderInterface;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\Core\Database\Connection;
 use Drupal\field\FieldInfo;
@@ -48,15 +50,17 @@ class UserStorageController extends FieldableDatabaseStorageController implement
    *   The database connection to be used.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
-   * @param \Drupal\Component\Uuid\UuidInterface $uuid_service
-   *   The UUID Service.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Entity\Schema\EntitySchemaBuilderInterface $schema_builder
+   *   The entity schema builder.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password hashing service.
    * @param \Drupal\user\UserDataInterface $user_data
    *   The user data service.
    */
-  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, UuidInterface $uuid_service, PasswordInterface $password, UserDataInterface $user_data) {
-    parent::__construct($entity_type, $database, $field_info, $uuid_service);
+  public function __construct(EntityTypeInterface $entity_type, Connection $database, FieldInfo $field_info, EntityManagerInterface $entity_manager, EntitySchemaBuilderInterface $schema_builder, PasswordInterface $password, UserDataInterface $user_data) {
+    parent::__construct($entity_type, $database, $field_info, $entity_manager, $schema_builder);
 
     $this->password = $password;
     $this->userData = $user_data;
@@ -70,7 +74,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $entity_type,
       $container->get('database'),
       $container->get('field.info'),
-      $container->get('uuid'),
+      $container->get('entity.manager'),
+      $container->get('entity.schema_builder'),
       $container->get('password'),
       $container->get('user.data')
     );
