diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index e3150e3e5fe0305a3df3c1498bbdc9dc4e4034c4..eadd0b06830f7b050716fab71b5efaf155ce53c2 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -128,7 +128,7 @@ function get_base_table() {
         $relationships = $this->view->display_handler->getOption('relationships');
         if (!empty($relationships[$this->options['relationship']])) {
           $options = $relationships[$this->options['relationship']];
-          $data = views_fetch_data($options['table']);
+          $data = drupal_container()->get('views.views_data')->get($options['table']);
           $this->base_table = $data[$options['field']]['relationship']['base'];
         }
       }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
index bc9abf365d08708340afad49597c8f8ff25827ad..223c68353bcffa351880bd67e46d4034b9ab1820 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/relationship/EntityReverse.php
@@ -40,7 +40,7 @@ public function query() {
     $this->ensureMyTable();
     // First, relate our base table to the current base table to the
     // field, using the base table's id field to the field's column.
-    $views_data = views_fetch_data($this->table);
+    $views_data = drupal_container()->get('views.views_data')->get($this->table);
     $left_field = $views_data['table']['base']['field'];
 
     $first = array(
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
index 6056996388818c34d25c0401f7adbc97b3acd6d7..00c48d9dd3831cc59115cc0a657c38423a1fb5c0 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/ApiDataTest.php
@@ -71,9 +71,6 @@ function setUp() {
       );
       $this->nodes[] = $this->drupalCreateNode($edit);
     }
-
-    // Reset views data cache.
-    $this->clearViewsCaches();
   }
 
   /**
@@ -82,7 +79,7 @@ function setUp() {
    * We check data structure for both node and node revision tables.
    */
   function testViewsData() {
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
 
     // Check the table and the joins of the first field.
     // Attached to node only.
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
index d63272ae41f760cb70d911e9e9fdb1834cc380c5..c0f500b7c643b9dec3dffcfe603aa26f08454ab2 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/FieldTestBase.php
@@ -74,14 +74,4 @@ function setUpInstances($bundle = 'page') {
     }
   }
 
-  /**
-   * Clear all views caches and static caches which are required for the patch.
-   */
-  function clearViewsCaches() {
-    // Reset views data cache.
-    drupal_static_reset('_views_fetch_data_cache');
-    drupal_static_reset('_views_fetch_data_recursion_protected');
-    drupal_static_reset('_views_fetch_data_fully_loaded');
-  }
-
 }
diff --git a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
index caacce247fe511ac76a923d70afe6d9d6ed6ea5c..3f5aea2f43ef39cb5771f1202bea817cedb36bbe 100644
--- a/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/Views/HandlerFieldFieldTest.php
@@ -52,8 +52,6 @@ protected function setUp() {
 
     $this->setUpInstances();
 
-    $this->clearViewsCaches();
-
     // Create some nodes.
     $this->nodes = array();
     for ($i = 0; $i < 3; $i++) {
diff --git a/core/modules/views/includes/cache.inc b/core/modules/views/includes/cache.inc
deleted file mode 100644
index 135787ffb5173726e9db036d50edec4f3056b50a..0000000000000000000000000000000000000000
--- a/core/modules/views/includes/cache.inc
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-
-/**
- * @file
- * Load Views' data so that it knows what is available to build queries from.
- */
-
-/**
- * Fetch Views' data from the cache.
- *
- * $param string|null $table
- *   (optional) The name of the table for which to fetch Views' data. If
- *   NULL, data for all tables will be retrieved
- * @param bool $reset
- *   (optional) Whether to rebuild the cache. Defaults to FALSE.
- *
- * @return array
- *   An associative array of views data for the given table. If $table is
- *   NULL, the array will be keyed by table name, with each key corresponding
- *   to the data array for that table.
- *
- * @see hook_views_data()
- */
-function _views_fetch_data($table = NULL, $reset = FALSE) {
-  $cache = &drupal_static(__FUNCTION__ . '_cache');
-  $recursion_protection = &drupal_static(__FUNCTION__ . '_recursion_protected');
-  $fully_loaded = &drupal_static(__FUNCTION__ . '_fully_loaded');
-  if ($reset) {
-    $cache = NULL;
-    $fully_loaded = FALSE;
-  }
-  if ($table) {
-    if (!isset($cache[$table])) {
-      $cid = 'views_data:' . $table;
-      $data = views_cache_get($cid, TRUE);
-      if (!empty($data->data)) {
-        $cache[$table] = $data->data;
-      }
-      else {
-        // No cache entry, rebuild.
-        $cache = _views_fetch_data_build();
-        $fully_loaded = TRUE;
-      }
-    }
-    if (isset($cache[$table])) {
-      return $cache[$table];
-    }
-  }
-  else {
-    if (!$fully_loaded) {
-      $data = views_cache_get('views_data', TRUE);
-      if (!empty($data->data)) {
-        $cache = $data->data;
-      }
-
-      if (empty($cache)) {
-        $cache = _views_fetch_data_build();
-      }
-      $fully_loaded = TRUE;
-    }
-    return $cache;
-  }
-  // Return an empty array if there is no match.
-  return array();
-}
-
-/**
- * Build, set the views data cache if empty and return the views data.
- *
- * @return array
- *   The views_data of all tables.
- */
-function _views_fetch_data_build() {
-  $cache = module_invoke_all('views_data');
-  foreach (module_implements('views_data_alter') as $module) {
-    $function = $module . '_views_data_alter';
-    $function($cache);
-  }
-  _views_data_process_entity_types($cache);
-
-  // Keep a record with all data.
-  views_cache_set('views_data', $cache, TRUE);
-  // Save data in seperate cache entries.
-  foreach ($cache as $key => $data) {
-    $cid = 'views_data:' . $key;
-    views_cache_set($cid, $data, TRUE);
-  }
-  return $cache;
-}
-
-/**
- * Links tables having an 'entity type' specified to the respective generic entity-type tables.
- */
-function _views_data_process_entity_types(&$data) {
-  foreach ($data as $table_name => $table_info) {
-    // Add in a join from the entity-table if an entity-type is given.
-    if (!empty($table_info['table']['entity type'])) {
-      $entity_table = 'views_entity_' . $table_info['table']['entity type'];
-
-      $data[$entity_table]['table']['join'][$table_name] = array(
-        'left_table' => $table_name,
-      );
-      $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
-      // Copy over the default table group if we have none yet.
-      if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
-        $data[$entity_table]['table']['group'] = $table_info['table']['group'];
-      }
-    }
-  }
-}
-
-/**
- * Set a cached item in the views cache.
- *
- * This is just a convenience wrapper around cache_set().
- *
- * @param $cid
- *   The cache ID of the data to store.
- * @param $data
- *   The data to store in the cache. Complex data types will be automatically serialized before insertion.
- *   Strings will be stored as plain text and not serialized.
- * @param $use_language
- *   If TRUE, the data will be cached specific to the currently active language.
- */
-function views_cache_set($cid, $data, $use_language = FALSE) {
-  if (config('views.settings')->get('skip_cache')) {
-    return;
-  }
-  if ($use_language) {
-    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
-  }
-
-  cache('views_info')->set($cid, $data);
-}
-
-/**
- * Return data from the persistent views cache.
- *
- * This is just a convenience wrapper around cache_get().
- *
- * @param int $cid
- *   The cache ID of the data to retrieve.
- * @param bool $use_language
- *   If TRUE, the data will be requested specific to the currently active language.
- *
- * @return stdClass|bool
- *   The cache or FALSE on failure.
- */
-function views_cache_get($cid, $use_language = FALSE) {
-  if (config('views.settings')->get('skip_cache')) {
-    return FALSE;
-  }
-  if ($use_language) {
-    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
-  }
-
-  return cache('views_info')->get($cid);
-}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Derivative/DefaultWizardDeriver.php b/core/modules/views/lib/Drupal/views/Plugin/Derivative/DefaultWizardDeriver.php
index f0ac7b45739ffeb98a2bb62660158d3b48af9e40..aae73e4aca93823ca30228afd79605834ec007c9 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Derivative/DefaultWizardDeriver.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Derivative/DefaultWizardDeriver.php
@@ -37,8 +37,9 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin
   public function getDerivativeDefinitions(array $base_plugin_definition) {
     $base_tables = array_keys(views_fetch_base_tables());
     $this->derivatives = array();
+    $views_data = drupal_container()->get('views.views_data');
     foreach ($base_tables as $table) {
-      $views_info = views_fetch_data($table);
+      $views_info = $views_data->get($table);
       if (empty($views_info['table']['wizard_id'])) {
         $this->derivatives[$table] = array(
           'id' => 'standard',
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
index 579e71dda4435ad03954a172ab8fc9856c130204..31601fd74c23bc17cf34ec56cbafa6f8a02b9a75 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/HandlerBase.php
@@ -770,7 +770,7 @@ public static function getTimezone() {
    * @return Drupal\views\Plugin\views\join\JoinPluginBase
    */
   public static function getTableJoin($table, $base_table) {
-    $data = views_fetch_data($table);
+    $data = drupal_container()->get('views.views_data')->get($table);
     if (isset($data['table']['join'][$base_table])) {
       $join_info = $data['table']['join'][$base_table];
       if (!empty($join_info['join_id'])) {
@@ -815,10 +815,10 @@ public function getEntityType() {
     // If the user has configured a relationship on the handler take that into
     // account.
     if (!empty($this->options['relationship']) && $this->options['relationship'] != 'none') {
-      $views_data = views_fetch_data($this->view->relationship->table);
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->relationship->table);
     }
     else {
-      $views_data = views_fetch_data($this->view->storage->get('base_table'));
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
     }
 
     if (isset($views_data['table']['entity type'])) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 50a0eb7e63e883782a28968107b96a264bbac331..fa8207d6e9d35d1d6e71ebff3cd448a357b9150c 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -127,7 +127,6 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
       unset($options['defaults']);
     }
 
-    views_include('cache');
     // Cache for unpackOptions, but not if we are in the ui.
     static $unpack_options = array();
     if (empty($view->editing)) {
@@ -799,7 +798,7 @@ public function getPlugin($type) {
 
     // Query plugins allow specifying a specific query class per base table.
     if ($type == 'query') {
-      $views_data = views_fetch_data($this->view->storage->get('base_table'));
+      $views_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
       $name = isset($views_data['table']['base']['query_id']) ? $views_data['table']['base']['query_id'] : 'views_query';
     }
 
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
index 8d0c5855ab346ccacdf298aa19ac30e2210c488d..12b97956c9cb1e9a725b0b53fde7de5aa1a1f83a 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php
@@ -1444,7 +1444,7 @@ function execute(ViewExecutable $view) {
     $count_query->addMetaData('view', $view);
 
     if (empty($this->options['disable_sql_rewrite'])) {
-      $base_table_data = views_fetch_data($this->view->storage->get('base_table'));
+      $base_table_data = drupal_container()->get('views.views_data')->get($this->view->storage->get('base_table'));
       if (isset($base_table_data['table']['base']['access query tag'])) {
         $access_tag = $base_table_data['table']['base']['access query tag'];
         $query->addTag($access_tag);
@@ -1542,7 +1542,8 @@ function execute(ViewExecutable $view) {
   function get_entity_tables() {
     // Start with the base table.
     $entity_tables = array();
-    $base_table_data = views_fetch_data($this->view->storage->get('base_table'));
+    $views_data = drupal_container()->get('views.views_data');
+    $base_table_data = $views_data->get($this->view->storage->get('base_table'));
     if (isset($base_table_data['table']['entity type'])) {
       $entity_tables[$this->view->storage->get('base_table')] = array(
         'base' => $this->view->storage->get('base_table'),
@@ -1553,7 +1554,7 @@ function get_entity_tables() {
     }
     // Include all relationships.
     foreach ($this->view->relationship as $relationship_id => $relationship) {
-      $table_data = views_fetch_data($relationship->definition['base']);
+      $table_data = $views_data->get($relationship->definition['base']);
       if (isset($table_data['table']['entity type'])) {
         $entity_tables[$relationship->alias] = array(
           'base' => $relationship->definition['base'],
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
index f3a1f4d3616d158a2c64f3e9926f2c9e2ea44d59..0b085c6eed3cf241ba47c94fe6c374062fb5befd 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/GroupwiseMax.php
@@ -93,7 +93,7 @@ public function buildOptionsForm(&$form, &$form_state) {
     foreach ($sorts as $sort_id => $sort) {
       $sort_options[$sort_id] = "$sort[group]: $sort[title]";
     }
-    $base_table_data = views_fetch_data($this->definition['base']);
+    $base_table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
 
     $form['subquery_sort'] = array(
       '#type' => 'select',
@@ -219,7 +219,7 @@ function left_query($options) {
     $temp_view->args[] = '**CORRELATED**';
 
     // Add the base table ID field.
-    $views_data = views_fetch_data($this->definition['base']);
+    $views_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = $views_data['table']['base']['field'];
     $temp_view->addItem('default', 'field', $this->definition['base'], $this->definition['field']);
 
@@ -341,7 +341,7 @@ function condition_namespace($string) {
    */
   public function query() {
     // Figure out what base table this relationship brings to the party.
-    $table_data = views_fetch_data($this->definition['base']);
+    $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
 
     $this->ensureMyTable();
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
index 47b16d4933820b461335f0a17b81b0cc9e1f030b..e2e814c090653b7ff6505a88847ca2535b60762c 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/relationship/RelationshipPluginBase.php
@@ -119,7 +119,7 @@ public function buildOptionsForm(&$form, &$form_state) {
    */
   public function query() {
     // Figure out what base table this relationship brings to the party.
-    $table_data = views_fetch_data($this->definition['base']);
+    $table_data = drupal_container()->get('views.views_data')->get($this->definition['base']);
     $base_field = empty($this->definition['base field']) ? $table_data['table']['base']['field'] : $this->definition['base field'];
 
     $this->ensureMyTable();
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
index 23ab931a4f5a727a5f98b90caf473187dc1706c7..25f546e06103b69b51b2e1aa668ce185e2c34dbd 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
@@ -73,7 +73,7 @@ public function buildOptionsForm(&$form, &$form_state) {
         $relationship_handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
 
         // If this relationship is valid for this type, add it to the list.
-        $data = views_fetch_data($relationship['table']);
+        $data = drupal_container()->get('views.views_data')->get($relationship['table']);
         $base = $data[$relationship['field']]['relationship']['base'];
         if ($base == $this->base_table) {
           $relationship_handler->init($executable, $relationship);
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
index 37c00096dfcfb0df37c1130ecc8a16821ecdfb50..c977e84d9e8fb962ed8cd7bf038c65a66f762ebc 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/wizard/WizardPluginBase.php
@@ -755,7 +755,7 @@ protected function default_display_options() {
     // Add a least one field so the view validates and the user has a preview.
     // The base field can provide a default in its base settings; otherwise,
     // choose the first field with a field handler.
-    $data = views_fetch_data($this->base_table);
+    $data = drupal_container()->get('views.views_data')->get($this->base_table);
     if (isset($data['table']['base']['defaults']['field'])) {
       $field = $data['table']['base']['defaults']['field'];
     }
@@ -836,7 +836,7 @@ protected function default_display_filters_user(array $form, array &$form_state)
           }
         }
       }
-      $table_data = views_fetch_data($table);
+      $table_data = drupal_container()->get('views.views_data')->get($table);
       // If the 'in' operator is being used, map the values to an array.
       $handler = $table_data[$bundle_key]['filter']['id'];
       $handler_definition = drupal_container()->get('plugin.manager.views.filter')->getDefinition($handler);
@@ -922,7 +922,7 @@ protected function default_display_sorts_user($form, $form_state) {
       // created from node, but the wizard type is another base table, make
       // sure it is not added. This usually don't happen if you have js
       // enabled.
-      $data = views_fetch_data($table);
+      $data = drupal_container()->get('views.views_data')->get($table);
       if (isset($data[$column]['sort'])) {
         $sorts[$column] = array(
           'id' => $column,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
index 85861cd28e2d5e2d544414079bf589fabe02e954..7be896aadc218d8fed8d86c2aa0104c0cc0897c0 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/HandlerAllTest.php
@@ -53,7 +53,7 @@ public static function getInfo() {
    */
   public function testHandlers() {
     $object_types = array_keys(ViewExecutable::viewsHandlerTypes());
-    foreach (views_fetch_data() as $base_table => $info) {
+    foreach (drupal_container()->get('views.views_data')->get() as $base_table => $info) {
       if (!isset($info['table']['base'])) {
         continue;
       }
diff --git a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
index 9034e7507c32963af34c9bb49fdb4969ecf94f49..5ecedec301331a430a2e7ca767b4453d15d9e1f9 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ModuleTest.php
@@ -219,7 +219,7 @@ protected function formatViewOptions(array $views = array()) {
    * Ensure that a certain handler is a instance of a certain table/field.
    */
   function assertInstanceHandler($handler, $table, $field, $id) {
-    $table_data = views_fetch_data($table);
+    $table_data = drupal_container()->get('views.views_data')->get($table);
     $field_data = $table_data[$field][$id];
 
     $this->assertEqual($field_data['id'], $handler->getPluginId());
diff --git a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
index d86bd81d0eb1c8db8ad85aebd88286abc459be44..031a9496f1a18e049019646a461d4e166954a674 100644
--- a/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/ViewsDataTest.php
@@ -31,16 +31,12 @@ public function testViewsFetchData() {
     $table_name = 'views_test_data';
     $expected_data = $this->viewsData();
 
-    $data = views_fetch_data($table_name);
+    $data = drupal_container()->get('views.views_data')->get($table_name);
     $this->assertEqual($data, $expected_data[$table_name], 'Make sure fetching views data by table works as expected.');
 
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     $this->assertTrue(isset($data[$table_name]), 'Make sure the views_test_data info appears in the total views data.');
     $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
-
-    $data = views_fetch_data(NULL, TRUE);
-    $this->assertTrue(isset($data[$table_name]), 'Make sure the views_fetch_data appears in the total views data with reset = TRUE.');
-    $this->assertEqual($data[$table_name], $expected_data[$table_name], 'Make sure the views_test_data has the expected values.');
   }
 
   /**
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index d5aef7d8bf406a479c821e59dc727d0bda4e686c..25a47ac1d6879408734a69d942765d7aeaeced15 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -946,7 +946,7 @@ public function initQuery() {
     }
 
     // Create and initialize the query object.
-    $views_data = views_fetch_data($this->storage->get('base_table'));
+    $views_data = drupal_container()->get('views.views_data')->get($this->storage->get('base_table'));
     $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '');
     if (!empty($views_data['table']['base']['database'])) {
       $this->base_database = $views_data['table']['base']['database'];
diff --git a/core/modules/views/lib/Drupal/views/ViewsBundle.php b/core/modules/views/lib/Drupal/views/ViewsBundle.php
index 5b32a706e238323021735c3768f46c0871e8bec6..544b8c3e88055ecfccd829cbec008cfd4f64d1d7 100644
--- a/core/modules/views/lib/Drupal/views/ViewsBundle.php
+++ b/core/modules/views/lib/Drupal/views/ViewsBundle.php
@@ -9,6 +9,7 @@
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\HttpKernel\Bundle\Bundle;
+use Symfony\Component\DependencyInjection\Reference;
 use Drupal\views\ViewExecutable;
 
 /**
@@ -24,6 +25,16 @@ public function build(ContainerBuilder $container) {
       $container->register("plugin.manager.views.$type", 'Drupal\views\Plugin\ViewsPluginManager')
         ->addArgument($type);
     }
+
+    $container
+      ->register('cache.views_info', 'Drupal\Core\Cache\CacheBackendInterface')
+      ->setFactoryClass('Drupal\Core\Cache\CacheFactory')
+      ->setFactoryMethod('get')
+      ->addArgument('views_info');
+
+    $container->register('views.views_data', 'Drupal\views\ViewsDataCache')
+      ->addArgument(new Reference('cache.views_info'))
+      ->addArgument(new Reference('config.factory'));
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/ViewsDataCache.php b/core/modules/views/lib/Drupal/views/ViewsDataCache.php
new file mode 100644
index 0000000000000000000000000000000000000000..4df7ff5a55c309ac416fc89eb32b5783ff2929c3
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/ViewsDataCache.php
@@ -0,0 +1,219 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\ViewsDataCache.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * Class to manage and lazy load cached views data.
+ */
+class ViewsDataCache {
+
+  /**
+   * The base cache ID to use.
+   *
+   * @var string
+   */
+  protected $baseCid = 'views_data';
+
+  /**
+   * The cache backend to use.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
+   * Storage for the data itself.
+   *
+   * @var array
+   */
+  protected $storage = array();
+
+  /**
+   * The configuration factory object.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * The current language code.
+   *
+   * @var string
+   */
+  protected $langcode;
+
+  /**
+   * Whether the data has been fully loaded in this request.
+   *
+   * @var bool
+   */
+  protected $fullyLoaded = FALSE;
+
+  /**
+   * Whether or not to skip data caching and rebuild data each time.
+   *
+   * @var bool
+   */
+  protected $skipCache;
+
+  /**
+   * Whether the cache should be rebuilt. This is set when getData() is called.
+   *
+   * @var bool
+   */
+  protected $rebuildCache;
+
+  public function __construct(CacheBackendInterface $cache_backend, ConfigFactory $config) {
+    $this->config = $config;
+    $this->cacheBackend = $cache_backend;
+
+    $this->langcode = language(LANGUAGE_TYPE_INTERFACE)->langcode;
+    $this->skipCache = $this->config->get('views.settings')->get('skip_cache');
+  }
+
+  /**
+   * Gets cached data for a particular key, or rebuilds if necessary.
+   *
+   * @param string|null $key
+   *   The key of the cache entry to retrieve. Defaults to NULL.
+   *
+   * @return array $data
+   *   The cached data.
+   */
+  public function get($key = NULL) {
+    if ($key) {
+      if (!isset($this->storage[$key])) {
+        $cid = $this->baseCid . ':' . $key;
+        $data = $this->cacheGet($cid);
+        if (!empty($data->data)) {
+          $this->storage[$key] = $data->data;
+        }
+        else {
+          // No cache entry, rebuild.
+          $this->storage = $this->getData();
+          $this->fullyLoaded = TRUE;
+        }
+      }
+      if (isset($this->storage[$key])) {
+        return $this->storage[$key];
+      }
+    }
+    else {
+      if (!$this->fullyLoaded) {
+        $data = $this->cacheGet($this->baseCid);
+        if (!empty($data->data)) {
+          $this->storage = $data->data;
+        }
+        else {
+          $this->storage = $this->getData();
+        }
+        $this->fullyLoaded = TRUE;
+      }
+    }
+
+    return $this->storage;
+  }
+
+  /**
+   * Sets the data in the cache backend for a cache key.
+   *
+   * @param string $key
+   *   The cache key to set.
+   * @param mixed $value
+   *   The value to set for this key.
+   */
+  public function set($key, $value) {
+    if ($this->skipCache) {
+      return FALSE;
+    }
+
+    $key .= ':' . $this->langcode;
+
+    $this->cacheBackend->set($key, $value);
+  }
+
+  /**
+   * Gets data from the cache backend.
+   *
+   * @param string $cid
+   *   The cache ID to return.
+   *
+   * @return mixed
+   *   The cached data, if any. This will immediately return FALSE if the
+   *   $skipCache property is TRUE.
+   */
+  protected function cacheGet($cid) {
+    if ($this->skipCache) {
+      return FALSE;
+    }
+
+    $cid .= ':' . $this->langcode;
+
+    return $this->cacheBackend->get($cid);
+  }
+
+  /**
+   * Gets all data invoked by hook_views_data().
+   *
+   * @return array
+   *   An array of all data.
+   */
+  protected function getData() {
+    $data = module_invoke_all('views_data');
+    drupal_alter('views_data', $data);
+
+    $this->processEntityTypes($data);
+
+    $this->rebuildCache = TRUE;
+
+    return $data;
+  }
+
+  /**
+   * Links tables with 'entity type' to respective generic entity-type tables.
+   *
+   * @param array $data
+   *   The array of data to alter entity data for, passed by reference.
+   */
+  protected function processEntityTypes(array &$data) {
+    foreach ($data as $table_name => $table_info) {
+      // Add in a join from the entity-table if an entity-type is given.
+      if (!empty($table_info['table']['entity type'])) {
+        $entity_table = 'views_entity_' . $table_info['table']['entity type'];
+
+        $data[$entity_table]['table']['join'][$table_name] = array(
+          'left_table' => $table_name,
+        );
+        $data[$entity_table]['table']['entity type'] = $table_info['table']['entity type'];
+        // Copy over the default table group if we have none yet.
+        if (!empty($table_info['table']['group']) && empty($data[$entity_table]['table']['group'])) {
+          $data[$entity_table]['table']['group'] = $table_info['table']['group'];
+        }
+      }
+    }
+  }
+
+  /**
+   * Destructs the ViewDataCache object.
+   */
+  public function __destruct() {
+    if ($this->rebuildCache && !empty($this->storage)) {
+      // Keep a record with all data.
+      $this->set($this->baseCid, $this->storage);
+      // Save data in seperate cache entries.
+      foreach ($this->storage as $table => $data) {
+        $cid = $this->baseCid . ':' . $table;
+        $this->set($cid, $data);
+      }
+    }
+  }
+
+}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 5906e76bdea73d033586d5f07884231684e7b2f9..6e9dae8cb15c0acc7878d8334fbd7dc372c65cb9 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -101,7 +101,7 @@
  * Describe data tables (or the equivalent) to Views.
  *
  * The data described with this hook is fetched and retrieved by
- * views_fetch_data().
+ * drupal_container()->get('views.views_data')->get().
  *
  * @return array
  *   An associative array describing the data structure. Primary key is the
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 514a78a6c48a2e33890edecaaf6f8f2932620654..1783f164a8f6b93ae8f0761784c06fb2e5e8383d 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -614,7 +614,6 @@ function views_contextual_links_view_alter(&$element, $items) {
  */
 function views_block_info() {
   // Try to avoid instantiating all the views just to get the blocks info.
-  views_include('cache');
   $cache = views_cache_get('views_block_items', TRUE);
   if ($cache && is_array($cache->data)) {
     return $cache->data;
@@ -1154,8 +1153,8 @@ function views_library_info() {
 function views_get_handler($table, $field, $type, $override = NULL) {
   // Get the plugin manager for this type.
   $manager = drupal_container()->get("plugin.manager.views.$type");
+  $data = drupal_container()->get('views.views_data')->get($table);
 
-  $data = views_fetch_data($table);
   if (isset($data[$field][$type])) {
     $definition = $data[$field][$type];
     foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table') as $key) {
@@ -1193,27 +1192,6 @@ function views_get_handler($table, $field, $type, $override = NULL) {
   return $manager->createInstance('broken');
 }
 
-/**
- * Fetch Views' data from the cache
- *
- * $param string|null $table
- *   (optional) The name of the table for which to fetch Views' data. If
- *   NULL, data for all tables will be retrieved
- * @param bool $reset
- *   (optional) Whether to rebuild the cache. Defaults to FALSE.
- *
- * @return array
- *   An associative array of views data for the given table. If $table is
- *   NULL, the array will be keyed by table name, with each key corresponding
- *   to the data array for that table.
- *
- * @see hook_views_data()
- */
-function views_fetch_data($table = NULL, $reset = FALSE) {
-  views_include('cache');
-  return _views_fetch_data($table, $reset);
-}
-
 /**
  * Fetch a list of all base tables available
  *
@@ -1224,7 +1202,7 @@ function views_fetch_base_tables() {
   static $base_tables = array();
   if (empty($base_tables)) {
     $tables = array();
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     foreach ($data as $table => $info) {
       if (!empty($info['table']['base'])) {
         $tables[$table] = array(
@@ -2208,3 +2186,51 @@ function views_config_import_delete($name, $new_config, $old_config) {
   entity_delete($view);
   return TRUE;
 }
+
+/**
+ * Set a cached item in the views cache.
+ *
+ * This is just a convenience wrapper around cache_set().
+ *
+ * @param $cid
+ *   The cache ID of the data to store.
+ * @param $data
+ *   The data to store in the cache. Complex data types will be automatically serialized before insertion.
+ *   Strings will be stored as plain text and not serialized.
+ * @param $use_language
+ *   If TRUE, the data will be cached specific to the currently active language.
+ */
+function views_cache_set($cid, $data, $use_language = FALSE) {
+  if (config('views.settings')->get('skip_cache')) {
+    return;
+  }
+  if ($use_language) {
+    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  cache('views_info')->set($cid, $data);
+}
+
+/**
+ * Return data from the persistent views cache.
+ *
+ * This is just a convenience wrapper around cache_get().
+ *
+ * @param int $cid
+ *   The cache ID of the data to retrieve.
+ * @param bool $use_language
+ *   If TRUE, the data will be requested specific to the currently active language.
+ *
+ * @return stdClass|bool
+ *   The cache or FALSE on failure.
+ */
+function views_cache_get($cid, $use_language = FALSE) {
+  if (config('views.settings')->get('skip_cache')) {
+    return FALSE;
+  }
+  if ($use_language) {
+    $cid .= ':' . language(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+  return cache('views_info')->get($cid);
+}
diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc
index 65d2e8a585d686d6b7493364e63ef35cf0bd5e42..80a0e1f7230852ec8243915229a95a5bc19b9be9 100644
--- a/core/modules/views/views_ui/admin.inc
+++ b/core/modules/views/views_ui/admin.inc
@@ -1441,7 +1441,7 @@ function views_ui_config_item_form($form, &$form_state) {
         }
 
         // If this relationship is valid for this type, add it to the list.
-        $data = views_fetch_data($relationship['table']);
+        $data = drupal_container()->get('views.views_data')->get($relationship['table']);
         $base = $data[$relationship['field']]['relationship']['base'];
         $base_fields = views_fetch_fields($base, $form_state['type'], $executable->display_handler->useGroupBy());
         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
@@ -2156,7 +2156,7 @@ function _views_sort_types($a, $b) {
 function views_fetch_fields($base, $type, $grouping = FALSE, $sub_type = NULL) {
   static $fields = array();
   if (empty($fields)) {
-    $data = views_fetch_data();
+    $data = drupal_container()->get('views.views_data')->get();
     $start = microtime(TRUE);
     // This constructs this ginormous multi dimensional array to
     // collect the important data about fields. In the end,
@@ -2288,6 +2288,7 @@ function views_ui_form_button_was_clicked($element, &$form_state) {
  */
 function views_ui_field_list() {
   $views = views_get_all_views();
+  $data = drupal_container()->get('views.views_data');
 
   // Fetch all fieldapi fields which are used in views
   // Therefore search in all views, displays and handler-types.
@@ -2300,12 +2301,12 @@ function views_ui_field_list() {
       if ($executable->setDisplay($display_id)) {
         foreach ($handler_types as $type => $info) {
           foreach ($executable->getItems($type, $display_id) as $item) {
-            $data = views_fetch_data($item['table']);
-            if (isset($data[$item['field']]) && isset($data[$item['field']][$type])
-              && $data = $data[$item['field']][$type]) {
+            $table_data = $data->get($item['table']);
+            if (isset($table_data[$item['field']]) && isset($table_data[$item['field']][$type])
+              && $field_data = $table_data[$item['field']][$type]) {
               // The final check that we have a fieldapi field now.
-              if (isset($data['field_name'])) {
-                $fields[$data['field_name']][$view->get('name')] = $view->get('name');
+              if (isset($field_data['field_name'])) {
+                $fields[$field_data['field_name']][$view->get('name')] = $view->get('name');
               }
             }
           }