diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index 0040ae076a..d904982775 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -51,6 +51,20 @@
    */
   protected $cacheBackend;
 
+  /**
+   * The entity last installed schema repository.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $entityLastInstalledSchemaRepository;
+
+  /**
+   * The entity type's field storage definitions.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+   */
+  protected $fieldStorageDefinitions;
+
   /**
    * Stores the latest revision IDs for entities.
    *
@@ -71,8 +85,10 @@
    *   The memory cache backend.
    * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
    *   The entity type bundle info.
+   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
+   *   The entity last installed schema repository.
    */
-  public function __construct(EntityTypeInterface $entity_type, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, MemoryCacheInterface $memory_cache = NULL, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL) {
+  public function __construct(EntityTypeInterface $entity_type, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, MemoryCacheInterface $memory_cache = NULL, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) {
     parent::__construct($entity_type, $memory_cache);
     $this->bundleKey = $this->entityType->getKey('bundle');
     $this->entityFieldManager = $entity_field_manager;
@@ -82,6 +98,14 @@ public function __construct(EntityTypeInterface $entity_type, EntityFieldManager
       $entity_type_bundle_info = \Drupal::service('entity_type.bundle.info');
     }
     $this->entityTypeBundleInfo = $entity_type_bundle_info;
+    if (!$entity_last_installed_schema_repository) {
+      @trigger_error('Calling ContentEntityStorageBase::__construct() with the $entity_last_installed_schema_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
+      $entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
+    }
+    $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
+
+    $this->entityType = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($this->entityTypeId) ?: $entity_type;
+    $this->fieldStorageDefinitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($this->entityTypeId) ?: $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
   }
 
   /**
@@ -93,10 +117,46 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $container->get('entity_field.manager'),
       $container->get('cache.entity'),
       $container->get('entity.memory_cache'),
-      $container->get('entity_type.bundle.info')
+      $container->get('entity_type.bundle.info'),
+      $container->get('entity.last_installed_schema.repository')
     );
   }
 
+  /**
+   * Updates the wrapped entity type definition.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The update entity type.
+   *
+   * @internal Only to be used internally by Entity API.
+   */
+  public function setEntityType(EntityTypeInterface $entity_type) {
+    if ($this->entityType->id() === $entity_type->id()) {
+      $this->entityType = $entity_type;
+    }
+    else {
+      throw new EntityStorageException("Unsupported entity type {$entity_type->id()}");
+    }
+  }
+
+  /**
+   * Updates the internal list of field storage definitions.
+   *
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
+   *   An array of field storage definitions.
+   *
+   * @internal Only to be used internally by Entity API.
+   */
+  public function setFieldStorageDefinitions(array $field_storage_definitions) {
+    foreach ($field_storage_definitions as $field_storage_definition) {
+      if ($field_storage_definition->getTargetEntityTypeId() !== $this->entityType->id()) {
+        throw new EntityStorageException("Unsupported entity type {$field_storage_definition->getTargetEntityTypeId()}");
+      }
+    }
+
+    $this->fieldStorageDefinitions = $field_storage_definitions;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -985,9 +1045,8 @@ protected function populateAffectedRevisionTranslations(ContentEntityInterface $
    *   The sanitized list of entity key values.
    */
   protected function cleanIds(array $ids, $entity_key = 'id') {
-    $definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityTypeId);
     $field_name = $this->entityType->getKey($entity_key);
-    if ($field_name && $definitions[$field_name]->getType() == 'integer') {
+    if ($field_name && $this->fieldStorageDefinitions[$field_name]->getType() == 'integer') {
       $ids = array_filter($ids, function ($id) {
         return is_numeric($id) && $id == (int) $id;
       });
diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
index 5baf85fd1f..58aede1920 100644
--- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
@@ -227,6 +227,7 @@ public function updateFieldableEntityType(EntityTypeInterface $entity_type, arra
 
     $original_field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id());
     $this->entityTypeListener->onFieldableEntityTypeUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
+    $this->clearCachedDefinitions();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php b/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
index 70e552c742..9dfdcc8fe1 100644
--- a/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
+++ b/core/lib/Drupal/Core/Entity/EntityLastInstalledSchemaRepository.php
@@ -17,6 +17,20 @@ class EntityLastInstalledSchemaRepository implements EntityLastInstalledSchemaRe
    */
   protected $keyValueFactory;
 
+  /**
+   * An array of entity type definitions.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface[]
+   */
+  protected $entityTypeDefinitions;
+
+  /**
+   * An array of field storage definitions, keyed by their entity type ID.
+   *
+   * @var array
+   */
+  protected $fieldStorageDefinitions;
+
   /**
    * Constructs a new EntityLastInstalledSchemaRepository.
    *
@@ -31,29 +45,36 @@ public function __construct(KeyValueFactoryInterface $key_value_factory) {
    * {@inheritdoc}
    */
   public function getLastInstalledDefinition($entity_type_id) {
-    return $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.entity_type');
+    if (!isset($this->entityTypeDefinitions[$entity_type_id])) {
+      $this->entityTypeDefinitions[$entity_type_id] = $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.entity_type');
+    }
+    return $this->entityTypeDefinitions[$entity_type_id];
   }
 
   /**
    * {@inheritdoc}
    */
   public function getLastInstalledDefinitions() {
-    $all_definitions = $this->keyValueFactory->get('entity.definitions.installed')->getAll();
-
-    // Filter out field storage definitions.
-    $filtered_keys = array_filter(array_keys($all_definitions), function ($key) {
-        return substr($key, -12) === '.entity_type';
-    });
-    $entity_type_definitions = array_intersect_key($all_definitions, array_flip($filtered_keys));
-
-    // Ensure that the returned array is keyed by the entity type ID.
-    $keys = array_keys($entity_type_definitions);
-    $keys = array_map(function ($key) {
-      $parts = explode('.', $key);
-      return $parts[0];
-    }, $keys);
-
-    return array_combine($keys, $entity_type_definitions);
+    if (!isset($this->entityTypeDefinitions)) {
+      $all_definitions = $this->keyValueFactory->get('entity.definitions.installed')->getAll();
+
+      // Filter out field storage definitions.
+      $filtered_keys = array_filter(array_keys($all_definitions), function ($key) {
+          return substr($key, -12) === '.entity_type';
+      });
+      $entity_type_definitions = array_intersect_key($all_definitions, array_flip($filtered_keys));
+
+      // Ensure that the returned array is keyed by the entity type ID.
+      $keys = array_keys($entity_type_definitions);
+      $keys = array_map(function ($key) {
+        $parts = explode('.', $key);
+        return $parts[0];
+      }, $keys);
+
+      $this->entityTypeDefinitions = array_combine($keys, $entity_type_definitions);
+    }
+
+    return $this->entityTypeDefinitions;
   }
 
   /**
@@ -62,6 +83,7 @@ public function getLastInstalledDefinitions() {
   public function setLastInstalledDefinition(EntityTypeInterface $entity_type) {
     $entity_type_id = $entity_type->id();
     $this->keyValueFactory->get('entity.definitions.installed')->set($entity_type_id . '.entity_type', $entity_type);
+    $this->entityTypeDefinitions[$entity_type_id] = $entity_type;
     return $this;
   }
 
@@ -74,6 +96,8 @@ public function deleteLastInstalledDefinition($entity_type_id) {
     // isn't currently fieldable, there might be legacy definitions or an
     // empty array stored from when it was.
     $this->keyValueFactory->get('entity.definitions.installed')->delete($entity_type_id . '.field_storage_definitions');
+    unset($this->entityTypeDefinitions[$entity_type_id]);
+    unset($this->fieldStorageDefinitions[$entity_type_id]);
     return $this;
   }
 
@@ -81,7 +105,10 @@ public function deleteLastInstalledDefinition($entity_type_id) {
    * {@inheritdoc}
    */
   public function getLastInstalledFieldStorageDefinitions($entity_type_id) {
-    return $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', []);
+    if (!isset($this->fieldStorageDefinitions[$entity_type_id])) {
+      $this->fieldStorageDefinitions[$entity_type_id] = $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', []);
+    }
+    return $this->fieldStorageDefinitions[$entity_type_id];
   }
 
   /**
@@ -89,6 +116,7 @@ public function getLastInstalledFieldStorageDefinitions($entity_type_id) {
    */
   public function setLastInstalledFieldStorageDefinitions($entity_type_id, array $storage_definitions) {
     $this->keyValueFactory->get('entity.definitions.installed')->set($entity_type_id . '.field_storage_definitions', $storage_definitions);
+    $this->fieldStorageDefinitions[$entity_type_id] = $storage_definitions;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index f550b62a80..eaa410995f 100644
--- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
+++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Entity\Query\Sql;
 
 use Drupal\Core\Database\Query\SelectInterface;
+use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
 use Drupal\Core\Entity\EntityType;
 use Drupal\Core\Entity\Query\QueryException;
 use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
@@ -16,6 +17,13 @@
  */
 class Tables implements TablesInterface {
 
+  use DeprecatedServicePropertyTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $deprecatedProperties = ['entityManager' => 'entity.manager'];
+
   /**
    * @var \Drupal\Core\Database\Query\SelectInterface
    */
@@ -44,11 +52,18 @@ class Tables implements TablesInterface {
   protected $fieldTables = [];
 
   /**
-   * The entity manager.
+   * The entity type manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
    */
-  protected $entityManager;
+  protected $entityTypeManager;
+
+  /**
+   * The entity last installed schema repository.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $entityLastInstalledSchemaRepository;
 
   /**
    * List of case sensitive fields.
@@ -62,7 +77,8 @@ class Tables implements TablesInterface {
    */
   public function __construct(SelectInterface $sql_query) {
     $this->sqlQuery = $sql_query;
-    $this->entityManager = \Drupal::entityManager();
+    $this->entityTypeManager = \Drupal::entityTypeManager();
+    $this->entityLastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
   }
 
   /**
@@ -85,9 +101,9 @@ public function addField($field, $type, $langcode) {
     // This will contain the definitions of the last specifier seen by the
     // system.
     $propertyDefinitions = [];
-    $entity_type = $this->entityManager->getDefinition($entity_type_id);
+    $entity_type = $this->getEntityType($entity_type_id);
+    $field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type_id);
 
-    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
     for ($key = 0; $key <= $count; $key++) {
       // This can either be the name of an entity base field or a configurable
       // field.
@@ -118,7 +134,7 @@ public function addField($field, $type, $langcode) {
       }
 
       /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
-      $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
+      $table_mapping = $this->entityTypeManager->getStorage($entity_type_id)->getTableMapping();
 
       // Check whether this field is stored in a dedicated table.
       if ($field_storage && $table_mapping->requiresDedicatedTableStorage($field_storage)) {
@@ -277,8 +293,8 @@ public function addField($field, $type, $langcode) {
           if (!$entity_type_id && $target_definition instanceof EntityDataDefinitionInterface) {
             $entity_type_id = $target_definition->getEntityTypeId();
           }
-          $entity_type = $this->entityManager->getDefinition($entity_type_id);
-          $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
+          $entity_type = $this->getEntityType($entity_type_id);
+          $field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type_id);
           // Add the new entity base table using the table and sql column.
           $base_table = $this->addNextBaseTable($entity_type, $table, $sql_column, $field_storage);
           $propertyDefinitions = [];
@@ -302,6 +318,28 @@ public function isFieldCaseSensitive($field_name) {
     }
   }
 
+  /**
+   * Gets the definition of an entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return \Drupal\Core\Entity\ContentEntityTypeInterface
+   *   The entity type definition.
+   *
+   * @throws \Drupal\Core\Entity\Query\QueryException
+   *   Thrown when the entity type definition could not be loaded.
+   */
+  private function getEntityType($entity_type_id) {
+    $entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id);
+
+    if (!$entity_type) {
+      throw new QueryException("Invalid entity type '$entity_type_id'.");
+    }
+
+    return $entity_type;
+  }
+
   /**
    * Joins the entity table, if necessary, and returns the alias for it.
    *
@@ -364,7 +402,7 @@ protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $b
     if (!isset($this->fieldTables[$index_prefix . $field_name])) {
       $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
       /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
-      $table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
+      $table_mapping = $this->entityTypeManager->getStorage($entity_type_id)->getTableMapping();
       $table = !$this->sqlQuery->getMetaData('all_revisions') ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field);
       if ($field->getCardinality() != 1) {
         $this->sqlQuery->addMetaData('simple_query', FALSE);
@@ -395,7 +433,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N
     $arguments = [];
     if ($langcode) {
       $entity_type_id = $this->sqlQuery->getMetaData('entity_type');
-      $entity_type = $this->entityManager->getDefinition($entity_type_id);
+      $entity_type = $this->getEntityType($entity_type_id);
       // Only the data table follows the entity language key, dedicated field
       // tables have an hard-coded 'langcode' column.
       $langcode_key = $entity_type->getDataTable() == $table ? $entity_type->getKey('langcode') : 'langcode';
@@ -423,7 +461,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N
    *   mapping is not available, FALSE is returned.
    */
   protected function getTableMapping($table, $entity_type_id) {
-    $storage = $this->entityManager->getStorage($entity_type_id);
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
     if ($storage instanceof SqlEntityStorageInterface) {
       $mapping = $storage->getTableMapping()->getAllColumns($table);
     }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index 2f770aede1..738bf9cdce 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -128,13 +128,6 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
    */
   protected $entityTypeManager;
 
-  /**
-   * The entity last installed schema repository.
-   *
-   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
-   */
-  protected $entityLastInstalledSchemaRepository;
-
   /**
    * Whether this storage should use the temporary table mapping.
    *
@@ -165,8 +158,15 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    * @return \Drupal\Core\Field\FieldDefinitionInterface[]
    *   The array of base field definitions for the entity type, keyed by field
    *   name.
+   *
+   * @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0.
+   *   Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions()
+   *   instead.
+   *
+   * @see https://www.drupal.org/node/3040966
    */
   public function getFieldStorageDefinitions() {
+    @trigger_error('SqlContentEntityStorage::getFieldStorageDefinitions() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.', E_USER_DEPRECATED);
     return $this->entityFieldManager->getBaseFieldDefinitions($this->entityTypeId);
   }
 
@@ -193,14 +193,9 @@ public function getFieldStorageDefinitions() {
    *   The entity last installed schema repository.
    */
   public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache = NULL, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, EntityTypeManagerInterface $entity_type_manager = NULL, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) {
-    parent::__construct($entity_type, $entity_field_manager, $cache, $memory_cache, $entity_type_bundle_info);
+    parent::__construct($entity_type, $entity_field_manager, $cache, $memory_cache, $entity_type_bundle_info, $entity_last_installed_schema_repository);
     $this->database = $database;
     $this->languageManager = $language_manager;
-    if (!$entity_last_installed_schema_repository) {
-      @trigger_error('Calling SqlContentEntityStorage::__construct() with the $entity_last_installed_schema_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
-      $entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
-    }
-    $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
     if (!$entity_type_manager) {
       @trigger_error('Calling SqlContentEntityStorage::__construct() with the $entity_type_manager argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
       $entity_type_manager = \Drupal::entityTypeManager();
@@ -295,22 +290,11 @@ protected function getStorageSchema() {
   }
 
   /**
-   * Updates the wrapped entity type definition.
-   *
-   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
-   *   The update entity type.
-   *
-   * @internal Only to be used internally by Entity API. Expected to be
-   *   removed by https://www.drupal.org/node/2274017.
+   * {@inheritdoc}
    */
   public function setEntityType(EntityTypeInterface $entity_type) {
-    if ($this->entityType->id() == $entity_type->id()) {
-      $this->entityType = $entity_type;
-      $this->initTableLayout();
-    }
-    else {
-      throw new EntityStorageException("Unsupported entity type {$entity_type->id()}");
-    }
+    parent::setEntityType($entity_type);
+    $this->initTableLayout();
   }
 
   /**
@@ -357,9 +341,7 @@ public function getTableMapping(array $storage_definitions = NULL) {
     // If we are using our internal storage definitions, which is our main use
     // case, we can statically cache the computed table mapping.
     if (!isset($this->tableMapping)) {
-      $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
-
-      $this->tableMapping = $this->getCustomTableMapping($this->entityType, $storage_definitions);
+      $this->tableMapping = $this->getCustomTableMapping($this->entityType, $this->fieldStorageDefinitions);
     }
 
     return $this->tableMapping;
@@ -463,7 +445,6 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
       $field_names = array_unique(array_merge($field_names, $this->tableMapping->getFieldNames($this->revisionTable)));
     }
 
-    $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
     $values = [];
     foreach ($records as $id => $record) {
       $values[$id] = [];
@@ -474,7 +455,7 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
         $field_columns = $this->tableMapping->getColumnNames($field_name);
         // Handle field types that store several properties.
         if (count($field_columns) > 1) {
-          $definition_columns = $storage_definitions[$field_name]->getColumns();
+          $definition_columns = $this->fieldStorageDefinitions[$field_name]->getColumns();
           foreach ($field_columns as $property_name => $column_name) {
             if (property_exists($record, $column_name)) {
               $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT][$property_name] = !empty($definition_columns[$property_name]['serialize']) ? unserialize($record->{$column_name}) : $record->{$column_name};
@@ -486,7 +467,7 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
         else {
           $column_name = reset($field_columns);
           if (property_exists($record, $column_name)) {
-            $columns = $storage_definitions[$field_name]->getColumns();
+            $columns = $this->fieldStorageDefinitions[$field_name]->getColumns();
             $column = reset($columns);
             $values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = !empty($column['serialize']) ? unserialize($record->{$column_name}) : $record->{$column_name};
             unset($record->{$column_name});
@@ -582,7 +563,6 @@ protected function loadFromSharedTables(array &$values, array &$translations, $l
         $all_fields = $table_mapping->getFieldNames($this->dataTable);
       }
 
-      $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
       $result = $query->execute();
       foreach ($result as $row) {
         $id = $row[$record_key];
@@ -594,7 +574,7 @@ protected function loadFromSharedTables(array &$values, array &$translations, $l
         $translations[$id][$langcode] = TRUE;
 
         foreach ($all_fields as $field_name) {
-          $storage_definition = $storage_definitions[$field_name];
+          $storage_definition = $this->fieldStorageDefinitions[$field_name];
           $definition_columns = $storage_definition->getColumns();
           $columns = $table_mapping->getColumnNames($field_name);
           // Do not key single-column fields by property name.
@@ -898,14 +878,13 @@ protected function doSaveFieldItems(ContentEntityInterface $entity, array $names
     }
     else {
       $table_mapping = $this->getTableMapping();
-      $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
       $shared_table_fields = FALSE;
       $dedicated_table_fields = [];
 
       // Collect the name of fields to be written in dedicated tables and check
       // whether shared table records need to be updated.
       foreach ($names as $name) {
-        $storage_definition = $storage_definitions[$name];
+        $storage_definition = $this->fieldStorageDefinitions[$name];
         if ($table_mapping->allowsSharedTableStorage($storage_definition)) {
           $shared_table_fields = TRUE;
         }
@@ -1059,10 +1038,10 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam
     $table_mapping = $this->getTableMapping();
     foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
 
-      if (empty($this->getFieldStorageDefinitions()[$field_name])) {
+      if (empty($this->fieldStorageDefinitions[$field_name])) {
         throw new EntityStorageException("Table mapping contains invalid field $field_name.");
       }
-      $definition = $this->getFieldStorageDefinitions()[$field_name];
+      $definition = $this->fieldStorageDefinitions[$field_name];
       $columns = $table_mapping->getColumnNames($field_name);
 
       foreach ($columns as $column_name => $schema_name) {
@@ -1420,8 +1399,7 @@ protected function saveToDedicatedTables(ContentEntityInterface $entity, $update
    */
   protected function deleteFromDedicatedTables(ContentEntityInterface $entity) {
     $table_mapping = $this->getTableMapping();
-    foreach ($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
-      $storage_definition = $field_definition->getFieldStorageDefinition();
+    foreach ($this->fieldStorageDefinitions as $storage_definition) {
       if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
         continue;
       }
@@ -1448,8 +1426,7 @@ protected function deleteRevisionFromDedicatedTables(ContentEntityInterface $ent
     $vid = $entity->getRevisionId();
     if (isset($vid)) {
       $table_mapping = $this->getTableMapping();
-      foreach ($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
-        $storage_definition = $field_definition->getFieldStorageDefinition();
+      foreach ($this->fieldStorageDefinitions as $storage_definition) {
         if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
           continue;
         }
@@ -1554,10 +1531,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
    * {@inheritdoc}
    */
   public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
-    $table_mapping = $this->getTableMapping(
-      $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($this->entityType->id())
-    );
-
+    $table_mapping = $this->getTableMapping();
     if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
       // Mark all data associated with the field for deletion.
       $table = $table_mapping->getDedicatedDataTableName($storage_definition);
@@ -1733,7 +1707,7 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
     // storage definition is added, so bypass the internal storage definitions
     // and fetch the table mapping using the passed in storage definition.
     // @todo Fix this in https://www.drupal.org/node/2705205.
-    $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
+    $storage_definitions = $this->fieldStorageDefinitions;
     $storage_definitions[$storage_definition->getName()] = $storage_definition;
     $table_mapping = $this->getTableMapping($storage_definitions);
 
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
index 1459f01c05..3c2f08c36a 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -141,8 +141,8 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Con
     }
     $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
 
-    $this->entityType = $entity_last_installed_schema_repository->getLastInstalledDefinition($entity_type->id());
-    $this->fieldStorageDefinitions = $entity_last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type->id());
+    $this->entityType = $entity_last_installed_schema_repository->getLastInstalledDefinition($entity_type->id()) ?: $entity_type;
+    $this->fieldStorageDefinitions = $entity_last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type->id()) ?: $this->entityFieldManager->getFieldStorageDefinitions($entity_type->id());
   }
 
   /**
@@ -337,9 +337,7 @@ public function requiresEntityDataMigration(EntityTypeInterface $entity_type, En
       return FALSE;
     }
 
-    // Use the original entity type since the storage has not been updated.
-    $original_storage = $this->entityTypeManager->createHandlerInstance($original_storage_class, $original);
-    return $original_storage->hasData();
+    return $this->storage->hasData();
   }
 
   /**
@@ -441,8 +439,6 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
    * {@inheritdoc}
    */
   public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) {
-    $this->entityType = $entity_type;
-    $this->fieldStorageDefinitions = $field_storage_definitions;
     $this->traitOnFieldableEntityTypeUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
   }
 
@@ -450,47 +446,54 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En
    * {@inheritdoc}
    */
   protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) {
-    // Make sure that each storage object has a proper table mapping.
-    /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $temporary_storage */
-    $temporary_storage = &$sandbox['temporary_storage'];
     $temporary_prefix = static::getTemporaryTableMappingPrefix($entity_type, $field_storage_definitions);
-    $temporary_table_mapping = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions, $temporary_prefix);
-    $temporary_storage->setTableMapping($temporary_table_mapping);
-
-    /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $original_storage */
-    $original_storage = &$sandbox['original_storage'];
-    $original_table_mapping = $original_storage->getCustomTableMapping($original, $original_field_storage_definitions);
-    $original_storage->setTableMapping($original_table_mapping);
-
-    $sandbox['new_table_mapping'] = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions);
-    $sandbox['backup_table_mapping'] = $original_storage->getCustomTableMapping($original, $original_field_storage_definitions, 'old_');
+    $sandbox['temporary_table_mapping'] = $this->storage->getCustomTableMapping($entity_type, $field_storage_definitions, $temporary_prefix);
+    $sandbox['new_table_mapping'] = $this->storage->getCustomTableMapping($entity_type, $field_storage_definitions);
+    $sandbox['original_table_mapping'] = $this->storage->getCustomTableMapping($original, $original_field_storage_definitions);
+    $sandbox['backup_table_mapping'] = $this->storage->getCustomTableMapping($original, $original_field_storage_definitions, 'old_');
 
     // Create temporary tables based on the new entity type and field storage
     // definitions.
-    $schema_handler = $this->database->schema();
+    $temporary_table_names = array_combine($sandbox['new_table_mapping']->getTableNames(), $sandbox['temporary_table_mapping']->getTableNames());
+    $this->entityType = $entity_type;
+    $this->fieldStorageDefinitions = $field_storage_definitions;
+
+    // Update the storage's entity type and field storage definitions because
+    // ::getEntitySchema() and ::getSharedTableFieldSchema() overrides are
+    // retrieving table names from these definitions.
+    $this->storage->setEntityType($entity_type);
+    $this->storage->setFieldStorageDefinitions($field_storage_definitions);
+    $this->storage->setTableMapping($sandbox['new_table_mapping']);
 
-    // Create entity tables.
-    $temporary_table_names = array_combine($sandbox['new_table_mapping']->getTableNames(), $temporary_table_mapping->getTableNames());
     $schema = $this->getEntitySchema($entity_type, TRUE);
+    $sandbox['new_entity_schema'] = $schema;
 
     // Filter out tables which are not part of the table mapping.
     $schema = array_intersect_key($schema, $temporary_table_names);
+
+    // Create entity tables.
     foreach ($schema as $table_name => $table_schema) {
-      $schema_handler->createTable($temporary_table_names[$table_name], $table_schema);
+      $this->database->schema()->createTable($temporary_table_names[$table_name], $table_schema);
     }
 
     // Create dedicated field tables.
     foreach ($field_storage_definitions as $field_storage_definition) {
-      if ($temporary_table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
+      if ($sandbox['temporary_table_mapping']->requiresDedicatedTableStorage($field_storage_definition)) {
         $schema = $this->getDedicatedTableSchema($field_storage_definition, $entity_type);
 
         // Filter out tables which are not part of the table mapping.
         $schema = array_intersect_key($schema, $temporary_table_names);
         foreach ($schema as $table_name => $table_schema) {
-          $schema_handler->createTable($temporary_table_names[$table_name], $table_schema);
+          $this->database->schema()->createTable($temporary_table_names[$table_name], $table_schema);
         }
       }
     }
+
+    // Restore the original definitions and table mapping so the data copying
+    // step can load existing data properly.
+    $this->storage->setEntityType($original);
+    $this->storage->setFieldStorageDefinitions($original_field_storage_definitions);
+    $this->storage->setTableMapping($sandbox['original_table_mapping']);
   }
 
   /**
@@ -498,9 +501,9 @@ protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, E
    */
   protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) {
     /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $temporary_table_mapping */
-    $temporary_table_mapping = $sandbox['temporary_storage']->getTableMapping();
+    $temporary_table_mapping = $sandbox['temporary_table_mapping'];
     /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $original_table_mapping */
-    $original_table_mapping = $sandbox['original_storage']->getTableMapping();
+    $original_table_mapping = $sandbox['original_table_mapping'];
     /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $new_table_mapping */
     $new_table_mapping = $sandbox['new_table_mapping'];
     /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $backup_table_mapping */
@@ -534,8 +537,11 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type,
       }
 
       // Store the updated entity schema.
-      $entity_schema = $this->getEntitySchema($entity_type, TRUE);
-      $this->saveEntitySchemaData($entity_type, $entity_schema);
+      $new_entity_schema = $sandbox['new_entity_schema'];
+      $this->schema[$entity_type->id()] = $new_entity_schema;
+      $this->entityType = $entity_type;
+      $this->fieldStorageDefinitions = $field_storage_definitions;
+      $this->saveEntitySchemaData($entity_type, $new_entity_schema);
 
       // Store the updated field schema for each field storage.
       foreach ($field_storage_definitions as $field_storage_definition) {
@@ -581,7 +587,7 @@ protected function handleEntityTypeSchemaUpdateExceptionOnDataCopy(EntityTypeInt
     // In case of an error during the save process, we need to clean up the
     // temporary tables.
     /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $temporary_table_mapping */
-    $temporary_table_mapping = $sandbox['temporary_storage']->getTableMapping();
+    $temporary_table_mapping = $sandbox['temporary_table_mapping'];
     foreach ($temporary_table_mapping->getTableNames() as $table_name) {
       $this->database->schema()->dropTable($table_name);
     }
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php
index 4b31a185c5..25f1ac0f9a 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php
@@ -58,15 +58,12 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En
         throw new EntityStorageException('Missing revision_translation_affected field.');
       }
 
-      $sandbox['original_storage'] = $original_storage;
-      $sandbox['temporary_storage'] = $this->entityTypeManager->createHandlerInstance($entity_type->getStorageClass(), $entity_type);
-
       $this->preUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
     }
 
     // Copy data from the original storage to the temporary one.
     if ($has_data) {
-      $this->copyData($entity_type, $original, $sandbox);
+      $this->copyData($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
     }
     else {
       // If there is no existing data, we still need to run the
@@ -127,10 +124,14 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type,
    *   The updated entity type definition.
    * @param \Drupal\Core\Entity\EntityTypeInterface $original
    *   The original entity type definition.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
+   *   The updated field storage definitions, including possibly new ones.
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $original_field_storage_definitions
+   *   The original field storage definitions.
    * @param array &$sandbox
    *   The sandbox array from a hook_update_N() implementation.
    */
-  protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterface $original, array &$sandbox) {
+  protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox) {
     /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */
     $id_key = $entity_type->getKey('id');
     $revision_id_key = $entity_type->getKey('revision');
@@ -139,9 +140,6 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac
     $default_langcode_key = $entity_type->getKey('default_langcode');
     $revision_translation_affected_key = $entity_type->getKey('revision_translation_affected');
 
-    $temporary_storage = $sandbox['temporary_storage'];
-    $original_storage = $sandbox['original_storage'];
-
     // If 'progress' is not set, then this will be the first run of the batch.
     if (!isset($sandbox['progress'])) {
       $sandbox['progress'] = 0;
@@ -174,7 +172,13 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac
       ->fetchCol();
 
     /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */
-    $entities = $load_revisions ? $original_storage->loadMultipleRevisions($entity_identifiers) : $original_storage->loadMultiple($entity_identifiers);
+    $entities = $load_revisions ? $this->storage->loadMultipleRevisions($entity_identifiers) : $this->storage->loadMultiple($entity_identifiers);
+
+    $temporary_storage = $this->entityTypeManager->createHandlerInstance($entity_type->getStorageClass(), $entity_type);
+    $temporary_storage->setEntityType($entity_type);
+    $temporary_storage->setFieldStorageDefinitions($field_storage_definitions);
+    $temporary_storage->setTableMapping($sandbox['temporary_table_mapping']);
+
     foreach ($entities as $identifier => $entity) {
       try {
         if (!$original->isRevisionable() && $entity_type->isRevisionable()) {
diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
index e67629b759..81778d9692 100644
--- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
@@ -2,12 +2,10 @@
 
 namespace Drupal\Core\Field;
 
-use Drupal\Core\Database\DatabaseExceptionWrapper;
 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\FieldableEntityStorageInterface;
-use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -82,17 +80,7 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
 
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
-    $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
-
-    // Entity type definition updates can change the schema by adding or
-    // removing entity tables (for example when switching an entity type from
-    // non-revisionable to revisionable), so CRUD operations on a field storage
-    // definition need to use the last installed entity type schema.
-    if ($storage instanceof SqlContentEntityStorage
-       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
-      $storage->setEntityType($last_installed_entity_type);
-    }
-
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionCreate($storage_definition);
     }
@@ -100,6 +88,7 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
     $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::CREATE, new FieldStorageDefinitionEvent($storage_definition));
 
     $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition);
+    $this->entityTypeManager->clearCachedDefinitions();
     $this->entityFieldManager->clearCachedFieldDefinitions();
   }
 
@@ -111,17 +100,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
 
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
-    $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
-
-    // Entity type definition updates can change the schema by adding or
-    // removing entity tables (for example when switching an entity type from
-    // non-revisionable to revisionable), so CRUD operations on a field storage
-    // definition need to use the last installed entity type schema.
-    if ($storage instanceof SqlContentEntityStorage
-       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
-      $storage->setEntityType($last_installed_entity_type);
-    }
-
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionUpdate($storage_definition, $original);
     }
@@ -129,6 +108,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
     $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::UPDATE, new FieldStorageDefinitionEvent($storage_definition, $original));
 
     $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition);
+    $this->entityTypeManager->clearCachedDefinitions();
     $this->entityFieldManager->clearCachedFieldDefinitions();
   }
 
@@ -140,32 +120,15 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
 
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
-    $storage = clone $this->entityTypeManager->getStorage($entity_type_id);
-
-    // Entity type definition updates can change the schema by adding or
-    // removing entity tables (for example when switching an entity type from
-    // non-revisionable to revisionable), so CRUD operations on a field storage
-    // definition need to use the last installed entity type schema.
-    if ($storage instanceof SqlContentEntityStorage
-       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
-      $storage->setEntityType($last_installed_entity_type);
-    }
+    $storage = $this->entityTypeManager->getStorage($entity_type_id);
 
     // Keep the field definition in the deleted fields repository so we can use
     // it later during field_purge_batch(), but only if the field has data.
-    try {
-      if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
-        $deleted_storage_definition = clone $storage_definition;
-        $deleted_storage_definition->setDeleted(TRUE);
-        $this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition);
-        $this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition);
-      }
-    }
-    catch (DatabaseExceptionWrapper $e) {
-      // This may happen when changing field storage schema, since we are not
-      // able to use a table mapping matching the passed storage definition.
-      // @todo Revisit this once we are able to instantiate the table mapping
-      //   properly. See https://www.drupal.org/node/2274017.
+    if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) {
+      $deleted_storage_definition = clone $storage_definition;
+      $deleted_storage_definition->setDeleted(TRUE);
+      $this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition);
+      $this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition);
     }
 
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
@@ -175,6 +138,7 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
     $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::DELETE, new FieldStorageDefinitionEvent($storage_definition));
 
     $this->entityLastInstalledSchemaRepository->deleteLastInstalledFieldStorageDefinition($storage_definition);
+    $this->entityTypeManager->clearCachedDefinitions();
     $this->entityFieldManager->clearCachedFieldDefinitions();
   }
 
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
index eaa4ad64d9..5311d88f5c 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
@@ -531,7 +531,7 @@ public function testNonLangcodeEntityTypeModeration() {
     \Drupal::state()->set('entity_test_rev.entity_type', $entity_type);
 
     // Update the entity type in order to remove the 'langcode' field.
-    $this->applyEntityUpdates('entity_test_rev');
+    \Drupal::entityDefinitionUpdateManager()->updateFieldableEntityType($entity_type, \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type->id()));
 
     $workflow = $this->createEditorialWorkflow();
     $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev');
diff --git a/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php b/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php
index a3d9c4d942..15ed5791ea 100644
--- a/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php
+++ b/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php
@@ -96,7 +96,7 @@ public function testNodeAccess() {
     $this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user);
     $this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user);
 
-    \Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
+    $this->accessHandler->resetCache();
 
     // Tests that access is granted if requested with no language.
     $this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user);
@@ -141,7 +141,7 @@ public function testNodeAccessPrivate() {
     $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user);
 
     // Reset the node access cache and turn on our test node access code.
-    \Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
+    $this->accessHandler->resetCache();
     \Drupal::state()->set('node_access_test_secret_catalan', 1);
 
     // Tests that access is not granted if requested with no language.
@@ -159,7 +159,7 @@ public function testNodeAccessPrivate() {
     $this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $private_ca_user);
     $this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $private_ca_user);
 
-    \Drupal::entityManager()->getAccessControlHandler('node')->resetCache();
+    $this->accessHandler->resetCache();
     \Drupal::state()->set('node_access_test_secret_catalan', 0);
 
     // Tests that Catalan is still not accessible for a user with no access to
diff --git a/core/modules/workspaces/src/EntityQuery/Tables.php b/core/modules/workspaces/src/EntityQuery/Tables.php
index 9ae727b5e7..01cce34f6c 100644
--- a/core/modules/workspaces/src/EntityQuery/Tables.php
+++ b/core/modules/workspaces/src/EntityQuery/Tables.php
@@ -97,7 +97,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N
 
       if (isset($this->baseTablesEntityType[$base_table])) {
         $entity_type_id = $this->baseTablesEntityType[$base_table];
-        $revision_key = $this->entityManager->getDefinition($entity_type_id)->getKey('revision');
+        $revision_key = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id)->getKey('revision');
 
         if ($id_field === $revision_key || $id_field === 'revision_id') {
           $workspace_association_table = $this->contentWorkspaceTables[$base_table];
@@ -141,7 +141,7 @@ protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column
    */
   public function addWorkspaceAssociationJoin($entity_type_id, $base_table_alias, $active_workspace_id) {
     if (!isset($this->contentWorkspaceTables[$base_table_alias])) {
-      $entity_type = $this->entityManager->getDefinition($entity_type_id);
+      $entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id);
       $id_field = $entity_type->getKey('id');
 
       // LEFT join the Workspace association entity's table so we can properly
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php
index 1b1ffd03de..1bdbb48afd 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityQueryRelationshipTest.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\KernelTests\Core\Entity;
 
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Entity\Query\QueryException;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\taxonomy\Entity\Vocabulary;
 use Drupal\taxonomy\Entity\Term;
@@ -195,7 +195,7 @@ public function testQuery() {
    * Tests the invalid specifier in the query relationship.
    */
   public function testInvalidSpecifier() {
-    $this->setExpectedException(PluginNotFoundException::class);
+    $this->setExpectedException(QueryException::class);
     $this->container
       ->get('entity_type.manager')
       ->getStorage('taxonomy_term')
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php
index bff60a78f9..d3beb19f73 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php
@@ -144,6 +144,12 @@ public function testFieldableEntityTypeUpdates($initial_rev, $initial_mul, $new_
       $this->setExpectedException(EntityStorageException::class, 'Converting an entity type from revisionable to non-revisionable or from translatable to non-translatable is not supported.');
     }
 
+    // Check that existing data can be retrieved from the storage before the
+    // entity schema is updated.
+    if ($data_migration_supported) {
+      $this->assertEntityData($initial_rev, $initial_mul);
+    }
+
     // Simulate a batch run since we are converting the entities one by one.
     $sandbox = [];
     do {
@@ -153,6 +159,9 @@ public function testFieldableEntityTypeUpdates($initial_rev, $initial_mul, $new_
     $this->assertEntityTypeSchema($new_rev, $new_mul);
     $this->assertEntityData($initial_rev, $initial_mul);
 
+    $change_list = $this->entityDefinitionUpdateManager->getChangeList();
+    $this->assertArrayNotHasKey('entity_test_update', $change_list, "There are no remaining updates for the 'entity_test_update' entity type.");
+
     // Check that we can still save new entities after the schema has been
     // updated.
     $this->insertData($new_rev, $new_mul);
@@ -283,8 +292,14 @@ protected function insertData($revisionable, $translatable) {
           'value2' => 'shared table - ' . $i . ' - value 2 - en',
         ],
         'test_multiple_properties_multiple_values' => [
-          'value1' => 'dedicated table - ' . $i . ' - value 1 - en',
-          'value2' => 'dedicated table - ' . $i . ' - value 2 - en',
+          [
+            'value1' => 'dedicated table - ' . $i . ' - delta 0 - value 1 - en',
+            'value2' => 'dedicated table - ' . $i . ' - delta 0 - value 2 - en',
+          ],
+          [
+            'value1' => 'dedicated table - ' . $i . ' - delta 1 - value 1 - en',
+            'value2' => 'dedicated table - ' . $i . ' - delta 1 - value 2 - en',
+          ],
         ],
       ]);
       $entity->save();
@@ -297,8 +312,14 @@ protected function insertData($revisionable, $translatable) {
             'value2' => 'shared table - ' . $i . ' - value 2 - ro',
           ],
           'test_multiple_properties_multiple_values' => [
-            'value1' => 'dedicated table - ' . $i . ' - value 1 - ro',
-            'value2' => 'dedicated table - ' . $i . ' - value 2 - ro',
+            [
+              'value1' => 'dedicated table - ' . $i . ' - delta 0 - value 1 - ro',
+              'value2' => 'dedicated table - ' . $i . ' - delta 0 - value 2 - ro',
+            ],
+            [
+              'value1' => 'dedicated table - ' . $i . ' - delta 1 - value 1 - ro',
+              'value2' => 'dedicated table - ' . $i . ' - delta 1 - value 2 - ro',
+            ],
           ],
         ]);
         $translation->save();
@@ -309,15 +330,23 @@ protected function insertData($revisionable, $translatable) {
         // Create a new pending revision.
         $revision_2 = $storage->createRevision($entity, FALSE);
         $revision_2->name = 'test entity - ' . $i . ' - en - rev2';
-        $revision_2->test_multiple_properties_multiple_values->value1 = 'dedicated table - ' . $i . ' - value 1 - en - rev2';
-        $revision_2->test_multiple_properties_multiple_values->value2 = 'dedicated table - ' . $i . ' - value 2 - en - rev2';
+        $revision_2->test_multiple_properties->value1 = 'shared table - ' . $i . ' - value 1 - en - rev2';
+        $revision_2->test_multiple_properties->value2 = 'shared table - ' . $i . ' - value 2 - en - rev2';
+        $revision_2->test_multiple_properties_multiple_values[0]->value1 = 'dedicated table - ' . $i . ' - delta 0 - value 1 - en - rev2';
+        $revision_2->test_multiple_properties_multiple_values[0]->value2 = 'dedicated table - ' . $i . ' - delta 0 - value 2 - en - rev2';
+        $revision_2->test_multiple_properties_multiple_values[1]->value1 = 'dedicated table - ' . $i . ' - delta 1 - value 1 - en - rev2';
+        $revision_2->test_multiple_properties_multiple_values[1]->value2 = 'dedicated table - ' . $i . ' - delta 1 - value 2 - en - rev2';
         $revision_2->save();
 
         if ($translatable) {
           $revision_2_translation = $storage->createRevision($entity->getTranslation('ro'), FALSE);
           $revision_2_translation->name = 'test entity - ' . $i . ' - ro - rev2';
-          $revision_2_translation->test_multiple_properties_multiple_values->value1 = 'dedicated table - ' . $i . ' - value 1 - ro - rev2';
-          $revision_2_translation->test_multiple_properties_multiple_values->value2 = 'dedicated table - ' . $i . ' - value 2 - ro - rev2';
+          $revision_2->test_multiple_properties->value1 = 'shared table - ' . $i . ' - value 1 - ro - rev2';
+          $revision_2->test_multiple_properties->value2 = 'shared table - ' . $i . ' - value 2 - ro - rev2';
+          $revision_2_translation->test_multiple_properties_multiple_values[0]->value1 = 'dedicated table - ' . $i . ' - delta 0 - value 1 - ro - rev2';
+          $revision_2_translation->test_multiple_properties_multiple_values[0]->value2 = 'dedicated table - ' . $i . ' - delta 0 - value 2 - ro - rev2';
+          $revision_2_translation->test_multiple_properties_multiple_values[1]->value1 = 'dedicated table - ' . $i . ' - delta 1 - value 1 - ro - rev2';
+          $revision_2_translation->test_multiple_properties_multiple_values[1]->value2 = 'dedicated table - ' . $i . ' - delta 1 - value 2 - ro - rev2';
           $revision_2_translation->save();
         }
       }
@@ -338,14 +367,22 @@ protected function assertEntityData($revisionable, $translatable) {
     foreach ($entities as $entity_id => $entity) {
       /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
       $this->assertEquals("test entity - {$entity->id()} - en", $entity->label());
-      $this->assertEquals("dedicated table - {$entity->id()} - value 1 - en", $entity->test_multiple_properties_multiple_values->value1);
-      $this->assertEquals("dedicated table - {$entity->id()} - value 2 - en", $entity->test_multiple_properties_multiple_values->value2);
+      $this->assertEquals("shared table - {$entity->id()} - value 1 - en", $entity->test_multiple_properties->value1);
+      $this->assertEquals("shared table - {$entity->id()} - value 2 - en", $entity->test_multiple_properties->value2);
+      $this->assertEquals("dedicated table - {$entity->id()} - delta 0 - value 1 - en", $entity->test_multiple_properties_multiple_values[0]->value1);
+      $this->assertEquals("dedicated table - {$entity->id()} - delta 0 - value 2 - en", $entity->test_multiple_properties_multiple_values[0]->value2);
+      $this->assertEquals("dedicated table - {$entity->id()} - delta 1 - value 1 - en", $entity->test_multiple_properties_multiple_values[1]->value1);
+      $this->assertEquals("dedicated table - {$entity->id()} - delta 1 - value 2 - en", $entity->test_multiple_properties_multiple_values[1]->value2);
 
       if ($translatable) {
         $translation = $entity->getTranslation('ro');
-        $this->assertEquals("test entity - {$entity->id()} - ro", $translation->label());
-        $this->assertEquals("dedicated table - {$translation->id()} - value 1 - ro", $translation->test_multiple_properties_multiple_values->value1);
-        $this->assertEquals("dedicated table - {$translation->id()} - value 2 - ro", $translation->test_multiple_properties_multiple_values->value2);
+        $this->assertEquals("test entity - {$translation->id()} - ro", $translation->label());
+        $this->assertEquals("shared table - {$translation->id()} - value 1 - ro", $translation->test_multiple_properties->value1);
+        $this->assertEquals("shared table - {$translation->id()} - value 2 - ro", $translation->test_multiple_properties->value2);
+        $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 1 - ro", $translation->test_multiple_properties_multiple_values[0]->value1);
+        $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 2 - ro", $translation->test_multiple_properties_multiple_values[0]->value2);
+        $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 1 - ro", $translation->test_multiple_properties_multiple_values[1]->value1);
+        $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 2 - ro", $translation->test_multiple_properties_multiple_values[1]->value2);
       }
     }
 
@@ -358,14 +395,22 @@ protected function assertEntityData($revisionable, $translatable) {
         /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
         $revision_label = $revision->isDefaultRevision() ? NULL : ' - rev2';
         $this->assertEquals("test entity - {$revision->id()} - en{$revision_label}", $revision->label());
-        $this->assertEquals("dedicated table - {$revision->id()} - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values->value1);
-        $this->assertEquals("dedicated table - {$revision->id()} - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values->value2);
+        $this->assertEquals("shared table - {$revision->id()} - value 1 - en{$revision_label}", $revision->test_multiple_properties->value1);
+        $this->assertEquals("shared table - {$revision->id()} - value 2 - en{$revision_label}", $revision->test_multiple_properties->value2);
+        $this->assertEquals("dedicated table - {$revision->id()} - delta 0 - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[0]->value1);
+        $this->assertEquals("dedicated table - {$revision->id()} - delta 0 - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[0]->value2);
+        $this->assertEquals("dedicated table - {$revision->id()} - delta 1 - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[1]->value1);
+        $this->assertEquals("dedicated table - {$revision->id()} - delta 1 - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[1]->value2);
 
         if ($translatable) {
           $translation = $revision->getTranslation('ro');
           $this->assertEquals("test entity - {$translation->id()} - ro{$revision_label}", $translation->label());
-          $this->assertEquals("dedicated table - {$translation->id()} - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values->value1);
-          $this->assertEquals("dedicated table - {$translation->id()} - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values->value2);
+          $this->assertEquals("shared table - {$revision->id()} - value 1 - ro{$revision_label}", $translation->test_multiple_properties->value1);
+          $this->assertEquals("shared table - {$revision->id()} - value 2 - ro{$revision_label}", $translation->test_multiple_properties->value2);
+          $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[0]->value1);
+          $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[0]->value2);
+          $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[1]->value1);
+          $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[1]->value2);
         }
       }
     }
@@ -659,18 +704,22 @@ public function testFieldableEntityTypeUpdatesErrorHandling() {
         ->condition('entity_id', 1, '=')
         ->condition('langcode', 'en', '=')
         ->execute()
-        ->fetchAllAssoc('entity_id');
-      $this->assertEquals('dedicated table - 1 - value 1 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1);
-      $this->assertEquals('dedicated table - 1 - value 2 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2);
+        ->fetchAllAssoc('delta');
+      $this->assertEquals('dedicated table - 1 - delta 0 - value 1 - en', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value1);
+      $this->assertEquals('dedicated table - 1 - delta 0 - value 2 - en', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value2);
+      $this->assertEquals('dedicated table - 1 - delta 1 - value 1 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1);
+      $this->assertEquals('dedicated table - 1 - delta 1 - value 2 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2);
 
       $dedicated_table_row = $this->database->select($dedicated_table_name)
         ->fields($dedicated_table_name)
         ->condition('entity_id', 1, '=')
         ->condition('langcode', 'ro', '=')
         ->execute()
-        ->fetchAllAssoc('entity_id');
-      $this->assertEquals('dedicated table - 1 - value 1 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1);
-      $this->assertEquals('dedicated table - 1 - value 2 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2);
+        ->fetchAllAssoc('delta');
+      $this->assertEquals('dedicated table - 1 - delta 0 - value 1 - ro', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value1);
+      $this->assertEquals('dedicated table - 1 - delta 0 - value 2 - ro', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value2);
+      $this->assertEquals('dedicated table - 1 - delta 1 - value 1 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1);
+      $this->assertEquals('dedicated table - 1 - delta 1 - value 2 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2);
     }
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
index 4c2d0893d4..70cac60011 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
@@ -14,6 +14,7 @@
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityHandlerBase;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\EntityTypeManager;
@@ -66,6 +67,13 @@ class EntityTypeManagerTest extends UnitTestCase {
    */
   protected $cacheBackend;
 
+  /**
+   * The entity last installed schema repository.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $entityLastInstalledSchemaRepository;
+
   /**
    * {@inheritdoc}
    */
@@ -78,8 +86,9 @@ protected function setUp() {
 
     $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
     $this->translationManager = $this->prophesize(TranslationInterface::class);
+    $this->entityLastInstalledSchemaRepository = $this->prophesize(EntityLastInstalledSchemaRepositoryInterface::class);
 
-    $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub());
+    $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub(), $this->entityLastInstalledSchemaRepository->reveal());
     $this->discovery = $this->prophesize(DiscoveryInterface::class);
     $this->entityTypeManager->setDiscovery($this->discovery->reveal());
   }
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
index f3dec947b8..d624f84b51 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
@@ -1203,26 +1203,14 @@ public function testRequiresEntityDataMigration($updated_entity_type_definition,
       'entity_keys' => ['id' => 'id'],
     ]);
 
-    $original_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
-      ->disableOriginalConstructor()
-      ->getMock();
-
-    $original_storage->expects($this->exactly(is_null($original_storage_has_data) || !$shared_table_structure_changed ? 0 : 1))
+    $this->storage->expects($this->exactly(is_null($original_storage_has_data) || !$shared_table_structure_changed ? 0 : 1))
       ->method('hasData')
       ->willReturn($original_storage_has_data);
 
-    // Assert hasData() is never called on the new storage definition.
-    $this->storage->expects($this->never())
-      ->method('hasData');
-
     $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
       ->disableOriginalConstructor()
       ->getMock();
 
-    $this->entityTypeManager->expects($this->any())
-      ->method('createHandlerInstance')
-      ->willReturn($original_storage);
-
     $this->entityLastInstalledSchemaRepository
       ->expects($this->any())
       ->method('getLastInstalledDefinition')
@@ -1372,11 +1360,21 @@ protected function setUpStorageSchema(array $expected = []) {
       ->with($this->entityType->id())
       ->will($this->returnValue($this->entityType));
 
+    $this->entityTypeManager->expects($this->any())
+      ->method('getActiveDefinition')
+      ->with($this->entityType->id())
+      ->will($this->returnValue($this->entityType));
+
     $this->entityFieldManager->expects($this->any())
       ->method('getFieldStorageDefinitions')
       ->with($this->entityType->id())
       ->will($this->returnValue($this->storageDefinitions));
 
+    $this->entityFieldManager->expects($this->any())
+      ->method('getActiveFieldStorageDefinitions')
+      ->with($this->entityType->id())
+      ->will($this->returnValue($this->storageDefinitions));
+
     $this->dbSchemaHandler = $this->getMockBuilder('Drupal\Core\Database\Schema')
       ->disableOriginalConstructor()
       ->getMock();
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 4ff4e3ad6b..1f39be1a7c 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -12,7 +12,6 @@
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
@@ -70,13 +69,6 @@ class SqlContentEntityStorageTest extends UnitTestCase {
    */
   protected $entityFieldManager;
 
-  /**
-   * The mocked entity last installed schema repository used in this test.
-   *
-   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityLastInstalledSchemaRepository;
-
   /**
    * The entity type ID.
    *
@@ -134,7 +126,6 @@ protected function setUp() {
     $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
     $this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class);
     $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class);
-    $this->entityLastInstalledSchemaRepository = $this->createMock(EntityLastInstalledSchemaRepositoryInterface::class);
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
     $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
     $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
@@ -147,7 +138,6 @@ protected function setUp() {
 
     $this->container->set('entity_type.manager', $this->entityTypeManager);
     $this->container->set('entity_field.manager', $this->entityFieldManager);
-    $this->container->set('entity.last_installed_schema.repository', $this->entityLastInstalledSchemaRepository);
   }
 
   /**
@@ -439,13 +429,13 @@ public function testOnEntityTypeCreate() {
       ->will($this->returnValue($schema_handler));
 
     $storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
-      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository])
+      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager])
       ->setMethods(['getStorageSchema'])
       ->getMock();
 
     $key_value = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
     $schema_handler = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema')
-      ->setConstructorArgs([$this->entityTypeManager, $this->entityType, $storage, $this->connection, $this->entityFieldManager, $this->entityLastInstalledSchemaRepository])
+      ->setConstructorArgs([$this->entityTypeManager, $this->entityType, $storage, $this->connection, $this->entityFieldManager])
       ->setMethods(['installedStorageSchema', 'createSharedTableSchema'])
       ->getMock();
     $schema_handler
@@ -1175,15 +1165,19 @@ protected function setUpEntityStorage() {
       ->method('getDefinition')
       ->will($this->returnValue($this->entityType));
 
+    $this->entityTypeManager->expects($this->any())
+      ->method('getActiveDefinition')
+      ->will($this->returnValue($this->entityType));
+
     $this->entityFieldManager->expects($this->any())
       ->method('getFieldStorageDefinitions')
       ->will($this->returnValue($this->fieldDefinitions));
 
     $this->entityFieldManager->expects($this->any())
-      ->method('getBaseFieldDefinitions')
+      ->method('getActiveFieldStorageDefinitions')
       ->will($this->returnValue($this->fieldDefinitions));
 
-    $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository);
+    $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager);
     $this->entityStorage->setModuleHandler($this->moduleHandler);
   }
 
@@ -1258,8 +1252,12 @@ public function testLoadMultipleNoPersistentCache() {
     $this->cache->expects($this->never())
       ->method('set');
 
+    $this->entityTypeManager->expects($this->any())
+      ->method('getActiveDefinition')
+      ->will($this->returnValue($this->entityType));
+
     $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
-      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository])
+      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager])
       ->setMethods(['getFromStorage', 'invokeStorageLoadHook', 'initTableLayout'])
       ->getMock();
     $entity_storage->method('invokeStorageLoadHook')
@@ -1312,8 +1310,12 @@ public function testLoadMultiplePersistentCacheMiss() {
       ->method('set')
       ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, [$this->entityTypeId . '_values', 'entity_field_info']);
 
+    $this->entityTypeManager->expects($this->any())
+      ->method('getActiveDefinition')
+      ->will($this->returnValue($this->entityType));
+
     $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
-      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository])
+      ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager])
       ->setMethods(['getFromStorage', 'invokeStorageLoadHook', 'initTableLayout'])
       ->getMock();
     $entity_storage->method('invokeStorageLoadHook')
@@ -1362,15 +1364,19 @@ public function testHasData() {
       ->method('getDefinition')
       ->will($this->returnValue($this->entityType));
 
+    $this->entityTypeManager->expects($this->any())
+      ->method('getActiveDefinition')
+      ->will($this->returnValue($this->entityType));
+
     $this->entityFieldManager->expects($this->any())
       ->method('getFieldStorageDefinitions')
       ->will($this->returnValue($this->fieldDefinitions));
 
     $this->entityFieldManager->expects($this->any())
-      ->method('getBaseFieldDefinitions')
+      ->method('getActiveFieldStorageDefinitions')
       ->will($this->returnValue($this->fieldDefinitions));
 
-    $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository);
+    $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager);
 
     $result = $this->entityStorage->hasData();
 
@@ -1446,6 +1452,18 @@ public function testCleanIds() {
 
   }
 
+  /**
+   * @covers ::getFieldStorageDefinitions
+   *
+   * @expectedDeprecation SqlContentEntityStorage::getFieldStorageDefinitions() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.
+   *
+   * @group legacy
+   */
+  public function testGetFieldStorageDefinitions() {
+    $this->setUpEntityStorage();
+    $this->entityStorage->getFieldStorageDefinitions();
+  }
+
   /**
    * Sets up the module handler with no implementations.
    */
