From 63d6b17ee45630a1e25c50c96619002ecdc900d4 Mon Sep 17 00:00:00 2001
From: Francesco Placella <plach@183211.no-reply.drupal.org>
Date: Fri, 22 Mar 2019 15:58:51 +0100
Subject: [PATCH] Issue #2554235 by amateescu, plach, Berdir, Sam152,
 joelpittet, mbaynton, catch, jibran: Make the content entity storage and
 entity query use the last installed definitions instead of the ones living in
 code

(cherry picked from commit 5d3322ebdab9c36ad1d06435c3ade0d8fa1e8f0a)
---
 core/core.services.yml                        |  2 +-
 .../Core/Entity/ContentEntityStorageBase.php  |  2 +-
 .../Entity/EntityDefinitionUpdateManager.php  |  1 +
 .../Drupal/Core/Entity/EntityFieldManager.php | 28 ++++++
 core/lib/Drupal/Core/Entity/EntityManager.php | 28 ++++++
 .../Drupal/Core/Entity/EntityTypeManager.php  | 40 +++++++-
 .../Drupal/Core/Entity/Query/Sql/Tables.php   | 40 +++++---
 .../Entity/Sql/SqlContentEntityStorage.php    | 94 ++++++++++--------
 .../Sql/SqlContentEntityStorageSchema.php     | 89 ++++++++---------
 .../SqlFieldableEntityTypeListenerTrait.php   | 22 +++--
 .../Field/FieldStorageDefinitionListener.php  | 55 ++---------
 .../src/Kernel/ContentModerationStateTest.php |  2 +-
 .../workspaces/src/EntityQuery/Tables.php     |  4 +-
 .../FieldableEntityDefinitionUpdateTest.php   | 95 ++++++++++++++-----
 .../Tests/Core/Entity/EntityManagerTest.php   | 26 ++++-
 .../Core/Entity/EntityTypeManagerTest.php     | 11 ++-
 .../Sql/SqlContentEntityStorageSchemaTest.php | 32 +++----
 .../Sql/SqlContentEntityStorageTest.php       | 62 +++++++-----
 18 files changed, 404 insertions(+), 229 deletions(-)

diff --git a/core/core.services.yml b/core/core.services.yml
index 20e5da30d887..08b09d99534a 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -552,7 +552,7 @@ services:
     class: Drupal\Core\Cache\MemoryCache\MemoryCache
   entity_type.manager:
     class: Drupal\Core\Entity\EntityTypeManager
-    arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver']
+    arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver', '@entity.last_installed_schema.repository']
     parent: container.trait
     tags:
       - { name: plugin_manager_cache_clear }
diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
index 0040ae076af3..3ec93063adff 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php
@@ -985,7 +985,7 @@ 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);
+    $definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($this->entityTypeId);
     $field_name = $this->entityType->getKey($entity_key);
     if ($field_name && $definitions[$field_name]->getType() == 'integer') {
       $ids = array_filter($ids, function ($id) {
diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
index 5baf85fd1f30..58aede1920a2 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/EntityFieldManager.php b/core/lib/Drupal/Core/Entity/EntityFieldManager.php
index cad945cddf49..5919f9f243f4 100644
--- a/core/lib/Drupal/Core/Entity/EntityFieldManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityFieldManager.php
@@ -55,6 +55,13 @@ class EntityFieldManager implements EntityFieldManagerInterface {
    */
   protected $fieldStorageDefinitions;
 
+  /**
+   * Static cache of active field storage definitions per entity type.
+   *
+   * @var array
+   */
+  protected $activeFieldStorageDefinitions;
+
   /**
    * An array keyed by entity type. Each value is an array whose keys are
    * field names and whose value is an array with two entries:
@@ -445,6 +452,25 @@ public function getFieldStorageDefinitions($entity_type_id) {
     return $this->fieldStorageDefinitions[$entity_type_id];
   }
 
+  /**
+   * Gets the active field storage definitions for a content entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID. Only content entities are supported.
+   *
+   * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+   *   An array of field storage definitions that are active in the current
+   *   request, keyed by field name.
+   *
+   * @internal
+   */
+  public function getActiveFieldStorageDefinitions($entity_type_id) {
+    if (!isset($this->activeFieldStorageDefinitions[$entity_type_id])) {
+      $this->activeFieldStorageDefinitions[$entity_type_id] = $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', []);
+    }
+    return $this->activeFieldStorageDefinitions[$entity_type_id] ?: $this->getFieldStorageDefinitions($entity_type_id);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -569,6 +595,7 @@ public function clearCachedFieldDefinitions() {
     $this->baseFieldDefinitions = [];
     $this->fieldDefinitions = [];
     $this->fieldStorageDefinitions = [];
+    $this->activeFieldStorageDefinitions = [];
     $this->fieldMap = [];
     $this->fieldMapByFieldType = [];
     $this->entityDisplayRepository->clearDisplayModeInfo();
@@ -588,6 +615,7 @@ public function useCaches($use_caches = FALSE) {
       $this->fieldDefinitions = [];
       $this->baseFieldDefinitions = [];
       $this->fieldStorageDefinitions = [];
+      $this->activeFieldStorageDefinitions = [];
     }
   }
 
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 91a904da5e65..475e051fa053 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -209,6 +209,20 @@ public function getFieldStorageDefinitions($entity_type_id) {
     return $this->container->get('entity_field.manager')->getFieldStorageDefinitions($entity_type_id);
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated in Drupal 8.0.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 getActiveFieldStorageDefinitions($entity_type_id) {
+    @trigger_error('EntityManagerInterface::getActiveFieldStorageDefinitions() is deprecated in 8.0.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->container->get('entity_field.manager')->getActiveFieldStorageDefinitions($entity_type_id);
+  }
+
   /**
    * {@inheritdoc}
    *
@@ -795,6 +809,20 @@ public function hasDefinition($plugin_id) {
     return $this->container->get('entity_type.manager')->hasDefinition($plugin_id);
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
+   *   Use \Drupal\Core\Entity\EntityTypeManagerInterface::getActiveDefinition()
+   *   instead.
+   *
+   * @see https://www.drupal.org/node/3040966
+   */
+  public function getActiveDefinition($entity_type_id) {
+    @trigger_error('EntityManagerInterface::getActiveDefinition() is deprecated in 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityTypeManagerInterface::getActiveDefinition() instead. See https://www.drupal.org/node/3040966.', E_USER_DEPRECATED);
+    return $this->container->get('entity_type.manager')->getActiveDefinition($entity_type_id);
+  }
+
   /**
    * {@inheritdoc}
    *
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeManager.php b/core/lib/Drupal/Core/Entity/EntityTypeManager.php
index 9fecdb542a58..5544e4a43db5 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeManager.php
@@ -58,6 +58,20 @@ class EntityTypeManager extends DefaultPluginManager implements EntityTypeManage
    */
   protected $classResolver;
 
+  /**
+   * The entity last installed schema repository.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $entityLastInstalledSchemaRepository;
+
+  /**
+   * A list of entity type definitions that are active for the current request.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeInterface[]
+   */
+  protected $activeDefinitions;
+
   /**
    * Constructs a new Entity plugin manager.
    *
@@ -72,8 +86,10 @@ class EntityTypeManager extends DefaultPluginManager implements EntityTypeManage
    *   The string translation.
    * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
    *   The class resolver.
+   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
+   *   The entity last installed schema repository.
    */
-  public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $string_translation, ClassResolverInterface $class_resolver) {
+  public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $string_translation, ClassResolverInterface $class_resolver, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository) {
     parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
 
     $this->setCacheBackend($cache, 'entity_type', ['entity_types']);
@@ -82,6 +98,7 @@ public function __construct(\Traversable $namespaces, ModuleHandlerInterface $mo
     $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
     $this->stringTranslation = $string_translation;
     $this->classResolver = $class_resolver;
+    $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
   }
 
   /**
@@ -133,11 +150,31 @@ public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
     throw new PluginNotFoundException($entity_type_id, sprintf('The "%s" entity type does not exist.', $entity_type_id));
   }
 
+  /**
+   * Gets the active definition for a content entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface
+   *   The active entity type definition.
+   *
+   * @internal
+   */
+  public function getActiveDefinition($entity_type_id) {
+    if (!isset($this->activeDefinitions[$entity_type_id])) {
+      $this->activeDefinitions[$entity_type_id] = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id);
+    }
+
+    return $this->activeDefinitions[$entity_type_id] ?: $this->getDefinition($entity_type_id);
+  }
+
   /**
    * {@inheritdoc}
    */
   public function clearCachedDefinitions() {
     parent::clearCachedDefinitions();
+    $this->activeDefinitions = [];
     $this->handlers = [];
   }
 
@@ -147,6 +184,7 @@ public function clearCachedDefinitions() {
   public function useCaches($use_caches = FALSE) {
     parent::useCaches($use_caches);
     if (!$use_caches) {
+      $this->activeDefinitions = [];
       $this->handlers = [];
       $this->container->get('entity.memory_cache')->reset();
     }
diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php
index f550b62a80ec..7257caa7abeb 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\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity field manager.
    *
-   * @var \Drupal\Core\Entity\EntityManager
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
    */
-  protected $entityManager;
+  protected $entityFieldManager;
 
   /**
    * 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->entityFieldManager = \Drupal::service('entity_field.manager');
   }
 
   /**
@@ -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->entityTypeManager->getActiveDefinition($entity_type_id);
 
-    $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
+    $field_storage_definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($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->entityTypeManager->getActiveDefinition($entity_type_id);
+          $field_storage_definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($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 = [];
@@ -364,7 +380,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 +411,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->entityTypeManager->getActiveDefinition($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 +439,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 2f770aede179..832ca213e953 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -14,7 +14,6 @@
 use Drupal\Core\Entity\EntityBundleListenerInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\EntityStorageException;
@@ -42,6 +41,13 @@
  */
 class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEntityStorageInterface, DynamicallyFieldableEntityStorageSchemaInterface, EntityBundleListenerInterface {
 
+  /**
+   * The entity type's field storage definitions.
+   *
+   * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
+   */
+  protected $fieldStorageDefinitions;
+
   /**
    * The mapping of field columns to SQL tables.
    *
@@ -128,13 +134,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.
    *
@@ -154,8 +153,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
       $container->get('language_manager'),
       $container->get('entity.memory_cache'),
       $container->get('entity_type.bundle.info'),
-      $container->get('entity_type.manager'),
-      $container->get('entity.last_installed_schema.repository')
+      $container->get('entity_type.manager')
     );
   }
 
@@ -165,8 +163,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);
   }
 
@@ -189,23 +194,18 @@ public function getFieldStorageDefinitions() {
    *   The entity type bundle info.
    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    *   The entity type manager.
-   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
-   *   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) {
+  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) {
     parent::__construct($entity_type, $entity_field_manager, $cache, $memory_cache, $entity_type_bundle_info);
     $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();
     }
     $this->entityTypeManager = $entity_type_manager;
+    $this->entityType = $this->entityTypeManager->getActiveDefinition($entity_type->id());
+    $this->fieldStorageDefinitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($entity_type->id());
 
     $this->initTableLayout();
   }
@@ -289,7 +289,7 @@ public function getRevisionDataTable() {
   protected function getStorageSchema() {
     if (!isset($this->storageSchema)) {
       $class = $this->entityType->getHandlerClass('storage_schema') ?: 'Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema';
-      $this->storageSchema = new $class($this->entityTypeManager, $this->entityType, $this, $this->database, $this->entityFieldManager, $this->entityLastInstalledSchemaRepository);
+      $this->storageSchema = new $class($this->entityTypeManager, $this->entityType, $this, $this->database, $this->entityFieldManager);
     }
     return $this->storageSchema;
   }
@@ -313,6 +313,24 @@ public function setEntityType(EntityTypeInterface $entity_type) {
     }
   }
 
+  /**
+   * 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;
+  }
+
   /**
    * Sets the wrapped table mapping definition.
    *
@@ -357,9 +375,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 +479,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 +489,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 +501,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 +597,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 +608,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 +912,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 +1072,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 +1433,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 +1460,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;
         }
@@ -1538,6 +1549,8 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En
   public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
     $this->wrapSchemaException(function () use ($storage_definition) {
       $this->getStorageSchema()->onFieldStorageDefinitionCreate($storage_definition);
+      $this->fieldStorageDefinitions[$storage_definition->getName()] = $storage_definition;
+      $this->tableMapping = NULL;
     });
   }
 
@@ -1547,6 +1560,8 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
   public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
     $this->wrapSchemaException(function () use ($storage_definition, $original) {
       $this->getStorageSchema()->onFieldStorageDefinitionUpdate($storage_definition, $original);
+      $this->fieldStorageDefinitions[$storage_definition->getName()] = $storage_definition;
+      $this->tableMapping = NULL;
     });
   }
 
@@ -1554,10 +1569,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);
@@ -1575,6 +1587,8 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
     // Update the field schema.
     $this->wrapSchemaException(function () use ($storage_definition) {
       $this->getStorageSchema()->onFieldStorageDefinitionDelete($storage_definition);
+      unset($this->fieldStorageDefinitions[$storage_definition->getName()]);
+      $this->tableMapping = NULL;
     });
   }
 
@@ -1733,7 +1747,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 1459f01c0575..f3e19e93e211 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php
@@ -7,7 +7,6 @@
 use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
 use Drupal\Core\Entity\ContentEntityTypeInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
-use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\EntityPublishedInterface;
 use Drupal\Core\Entity\EntityStorageException;
@@ -54,13 +53,6 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
    */
   protected $entityFieldManager;
 
-  /**
-   * The entity last installed schema repository.
-   *
-   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
-   */
-  protected $entityLastInstalledSchemaRepository;
-
   /**
    * The entity type this schema builder is responsible for.
    *
@@ -123,26 +115,19 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
    *   The database connection to be used.
    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
    *   The entity field manager.
-   * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
-   *   The entity last installed schema repository.
    */
-  public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentEntityTypeInterface $entity_type, SqlContentEntityStorage $storage, Connection $database, EntityFieldManagerInterface $entity_field_manager = NULL, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) {
+  public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentEntityTypeInterface $entity_type, SqlContentEntityStorage $storage, Connection $database, EntityFieldManagerInterface $entity_field_manager = NULL) {
     $this->entityTypeManager = $entity_type_manager;
-    $this->storage = $storage;
+    $this->storage = clone $storage;
     $this->database = $database;
     if (!$entity_field_manager) {
       @trigger_error('Calling SqlContentEntityStorageSchema::__construct() with the $entity_field_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_field_manager = \Drupal::service('entity_field.manager');
     }
     $this->entityFieldManager = $entity_field_manager;
-    if (!$entity_last_installed_schema_repository) {
-      @trigger_error('Calling SqlContentEntityStorageSchema::__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 = $entity_last_installed_schema_repository->getLastInstalledDefinition($entity_type->id());
-    $this->fieldStorageDefinitions = $entity_last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type->id());
+    $this->entityType = $entity_type_manager->getActiveDefinition($entity_type->id());
+    $this->fieldStorageDefinitions = $entity_field_manager->getActiveFieldStorageDefinitions($entity_type->id());
   }
 
   /**
@@ -337,9 +322,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 +424,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 +431,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 +486,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 +522,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 +572,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);
     }
@@ -625,6 +616,7 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
    * {@inheritdoc}
    */
   public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
+    $this->fieldStorageDefinitions[$storage_definition->getName()] = $storage_definition;
     $this->performFieldSchemaOperation('update', $storage_definition, $original);
   }
 
@@ -728,6 +720,7 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
       // Delete the field from the shared tables.
       $this->deleteSharedTableSchema($storage_definition);
     }
+    unset($this->fieldStorageDefinitions[$storage_definition->getName()]);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php
index 4b31a185c59c..25f1ac0f9a37 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 e67629b75932..fda621b92915 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);
     }
@@ -111,17 +99,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);
     }
@@ -140,32 +118,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) {
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
index eaa4ad64d9e3..5311d88f5c9b 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/workspaces/src/EntityQuery/Tables.php b/core/modules/workspaces/src/EntityQuery/Tables.php
index 9ae727b5e774..b791b92e2e8c 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->entityTypeManager->getActiveDefinition($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->entityTypeManager->getActiveDefinition($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/FieldableEntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php
index bff60a78f974..d3beb19f73a3 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/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 0b1a3329afc7..e59528e51d90 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -4,14 +4,14 @@
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityFieldManager;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityManager;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Entity\EntityType;
 use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\EntityTypeManager;
 use Drupal\Core\Entity\EntityTypeRepositoryInterface;
 use Drupal\Tests\UnitTestCase;
 
@@ -84,10 +84,10 @@ class EntityManagerTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
+    $this->entityTypeManager = $this->prophesize(EntityTypeManager::class);
     $this->entityTypeRepository = $this->prophesize(EntityTypeRepositoryInterface::class);
     $this->entityTypeBundleInfo = $this->prophesize(EntityTypeBundleInfoInterface::class);
-    $this->entityFieldManager = $this->prophesize(EntityFieldManagerInterface::class);
+    $this->entityFieldManager = $this->prophesize(EntityFieldManager::class);
     $this->entityRepository = $this->prophesize(EntityRepositoryInterface::class);
     $this->entityDisplayRepository = $this->prophesize(EntityDisplayRepositoryInterface::class);
     $this->entityLastInstalledSchemaRepository = $this->prophesize(EntityLastInstalledSchemaRepositoryInterface::class);
@@ -452,4 +452,22 @@ public function testGetCanonicalMultiple() {
     $this->entityManager->getCanonicalMultiple($entity_type_id, $entity_ids, $contexts);
   }
 
+  /**
+   * @covers ::getActiveDefinition
+   *
+   * @expectedDeprecation EntityManagerInterface::getActiveDefinition() is deprecated in 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityTypeManagerInterface::getActiveDefinition() instead. See https://www.drupal.org/node/3040966.
+   */
+  public function testGetActiveDefinition() {
+    $this->entityManager->getActiveDefinition('entity_test');
+  }
+
+  /**
+   * @covers ::getActiveFieldStorageDefinitions
+   *
+   * @expectedDeprecation EntityManagerInterface::getActiveFieldStorageDefinitions() is deprecated in 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.
+   */
+  public function testGetActiveFieldStorageDefinitions() {
+    $this->entityManager->getActiveFieldStorageDefinitions('entity_test');
+  }
+
 }
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
index 4c2d0893d474..70cac6001184 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 f3dec947b884..ad30bcae795f 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php
@@ -4,9 +4,9 @@
 
 use Drupal\Core\Entity\ContentEntityType;
 use Drupal\Core\Entity\ContentEntityTypeInterface;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityFieldManager;
 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\EntityTypeManager;
 use Drupal\Core\Entity\Sql\DefaultTableMapping;
 use Drupal\Tests\UnitTestCase;
 
@@ -76,8 +76,8 @@ class SqlContentEntityStorageSchemaTest extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
-    $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
-    $this->entityFieldManager = $this->createMock(EntityFieldManagerInterface::class);
+    $this->entityTypeManager = $this->createMock(EntityTypeManager::class);
+    $this->entityFieldManager = $this->createMock(EntityFieldManager::class);
     $this->entityLastInstalledSchemaRepository = $this->createMock(EntityLastInstalledSchemaRepositoryInterface::class);
     $this->storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
       ->disableOriginalConstructor()
@@ -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 4ff4e3ad6b8b..a548c74ddb3f 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -10,12 +10,11 @@
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\MemoryCache\MemoryCache;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityFieldManager;
 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;
+use Drupal\Core\Entity\EntityTypeManager;
 use Drupal\Core\Entity\Query\QueryFactoryInterface;
 use Drupal\Core\Entity\Sql\DefaultTableMapping;
 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
@@ -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.
    *
@@ -131,10 +123,9 @@ protected function setUp() {
     $this->container = new ContainerBuilder();
     \Drupal::setContainer($this->container);
 
-    $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
+    $this->entityTypeManager = $this->createMock(EntityTypeManager::class);
     $this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class);
-    $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class);
-    $this->entityLastInstalledSchemaRepository = $this->createMock(EntityLastInstalledSchemaRepositoryInterface::class);
+    $this->entityFieldManager = $this->createMock(EntityFieldManager::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.
    */
-- 
GitLab