diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 221d4ee..4aad431 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -262,17 +262,9 @@ public function getAccessController($entity_type) {
   }
 
   /**
-   * Creates a new controller instance.
-   *
-   * @param string $entity_type
-   *   The entity type for this access controller.
-   * @param string $controller_type
-   *   The controller type to create an instance for.
-   *
-   * @return mixed.
-   *   A controller instance.
+   * {@inhertidoc}
    */
-  protected function getController($entity_type, $controller_type) {
+  public function getController($entity_type, $controller_type) {
     if (!isset($this->controllers[$controller_type][$entity_type])) {
       $class = $this->getControllerClass($entity_type, $controller_type);
       if (in_array('Drupal\Core\Entity\EntityControllerInterface', class_implements($class))) {
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index e89966a..c8f3e70 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -53,6 +53,19 @@ public function getEntityTypeLabels();
   public function getFieldDefinitions($entity_type, $bundle = NULL);
 
   /**
+   * Creates a new controller instance.
+   *
+   * @param string $entity_type
+   *   The entity type for this access controller.
+   * @param string $controller_type
+   *   The controller type to create an instance for.
+   *
+   * @return mixed.
+   *   A controller instance.
+   */
+  public function getController($entity_type, $controller_type);
+
+  /**
    * Creates a new access controller instance.
    *
    * @param string $entity_type
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index b4ad49b..4ace190 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -26,7 +26,8 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestFormController"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationController"
+ *     "translation" = "Drupal\content_translation\ContentTranslationController",
+ *     "views" = "Drupal\views\EntityViewsController"
  *   },
  *   base_table = "entity_test",
  *   fieldable = TRUE,
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
index e73ff5e..006d942 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
@@ -24,7 +24,8 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestFormController"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationController"
+ *     "translation" = "Drupal\content_translation\ContentTranslationController",
+ *     "views" = "Drupal\views\EntityViewsController"
  *   },
  *   base_table = "entity_test_mul",
  *   data_table = "entity_test_mul_property_data",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
index 5b495f5..4160456 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
@@ -23,7 +23,8 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestFormController"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationController"
+ *     "translation" = "Drupal\content_translation\ContentTranslationController",
+ *     "views" = "Drupal\views\EntityViewsController"
  *   },
  *   base_table = "entity_test_mulrev",
  *   data_table = "entity_test_mulrev_property_data",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
index 4935645..22c8b8a 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
@@ -23,7 +23,8 @@
  *     "form" = {
  *       "default" = "Drupal\entity_test\EntityTestFormController"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationController"
+ *     "translation" = "Drupal\content_translation\ContentTranslationController",
+ *     "views" = "Drupal\views\EntityViewsController"
  *   },
  *   base_table = "entity_test_rev",
  *   revision_table = "entity_test_rev_revision",
diff --git a/core/modules/views/lib/Drupal/views/EntityViewsController.php b/core/modules/views/lib/Drupal/views/EntityViewsController.php
new file mode 100644
index 0000000..9fd9689
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/EntityViewsController.php
@@ -0,0 +1,263 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\EntityViewsController.
+ */
+
+namespace Drupal\views;
+
+use Drupal\Core\Entity\EntityControllerInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityStorageControllerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides generic views integration for entities.
+ */
+class EntityViewsController implements EntityControllerInterface {
+
+  /**
+   * Entity type for this views controller instance.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * Array of information about the entity type.
+   *
+   * @var array
+   *
+   * @see \Drupal\Core\Entity\EntityManager::getDefinition()
+   */
+  protected $entityInfo;
+
+  /**
+   * The storage controller used for this entity type.
+   *
+   * @var \Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storageController;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The translation manager
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface
+   */
+  protected $translationManager;
+
+  /**
+   * Constructs an EntityViewsController object.
+   *
+   * @param string $entity_type
+   *   The entity type to provide views integration for.
+   * @param array $entity_info
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
+   *   The storage controller used for this entity type.
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
+   *   The translation manager.
+   */
+  function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage_controller, EntityManagerInterface $entity_manager, ModuleHandlerInterface $module_handler, TranslationInterface $translation_manager) {
+    $this->entityType = $entity_type;
+    $this->entityInfo = $entity_info;
+    $this->entityManager = $entity_manager;
+    $this->storageController = $storage_controller;
+    $this->moduleHandler = $module_handler;
+    $this->translationManager = $translation_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function createInstance(ContainerInterface $container, $entity_type, array $entity_info) {
+    return new static(
+      $entity_type,
+      $entity_info,
+      $container->get('entity.manager')->getStorageController($entity_type),
+      $container->get('entity.manager'),
+      $container->get('module_handler'),
+      $container->get('string_translation')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewsData() {
+    $data = array();
+
+    // @todo In theory we should use the data table as base table, as this would
+    //   save one pointless join (and one more for every relationship).
+    $base_table = $this->entityInfo['base_table'];
+    $base_field = $this->entityInfo['entity_keys']['id'];
+    $data_table = isset($this->entityInfo['data_table']) ? $this->entityInfo['data_table']: NULL;
+    $revision_table = isset($this->entityInfo['revision_table']) ? $this->entityInfo['revision_table'] : NULL;
+    $revision_data_table = isset($this->entityInfo['revision_data_table']) ? $this->entityInfo['revision_data_table'] : NULL;
+    $revision_field = isset($this->entityInfo['entity_keys']['revision']) ? $this->entityInfo['entity_keys']['revision'] : NULL;
+
+    // Setup base information of the views data.
+    $data[$base_table]['table']['entity_type'] = $this->entityType;
+    $data[$base_table]['table']['group'] = $this->entityInfo['label'];
+    $data[$base_table]['table']['base'] = array(
+      'field' => $base_field,
+      'title' => $this->entityInfo['label'],
+    );
+
+    // Setup relations to the revisions/property data.
+    if ($data_table) {
+      $data[$data_table]['table']['join'][$base_table] = array(
+        'left_field' => $base_field,
+        'field' => $base_field
+      );
+
+    }
+    if ($revision_table) {
+      $data[$revision_table]['table']['entity_type'] = $this->entityType;
+      $data[$revision_table]['table']['group'] = $this->entityInfo['label'];
+      $data[$revision_table]['table']['base'] = array(
+        'field' => $revision_field,
+        'title' => $this->t('@entity_type revisions', array('@entity_type' => $this->entityInfo['label'])),
+      );
+      // Join the revision table to the base table.
+      $data[$revision_table]['table']['join'][$base_table] = array(
+        'left_field' => $base_field,
+        'field' => $base_field,
+      );
+
+      if ($revision_data_table) {
+        $data[$revision_data_table]['table']['join'][$revision_table] = array(
+          'left_field' => $revision_field,
+          'field' => $revision_field,
+        );
+      }
+    }
+
+    // Load all typed data definitions of all fields. This should cover each of
+    // the entity base, revision, data tables.
+    $field_definitions = $this->entityManager->getFieldDefinitions($this->entityType);
+
+    // Iterate over each table we have so far and collect field data for each.
+    // Based on whether the field is in the field_definitions provided by the
+    // entity manager.
+    // @todo We should better just rely on information coming from the entity
+    //   storage controller.
+    foreach (array_keys($data) as $table) {
+      foreach ($this->drupalSchemaFieldsSql($table) as $field_name) {
+        if (isset($field_definitions[$field_name])) {
+          $views_field = &$data[$table][$field_name];
+          // @todo Is translating the right way to handle label/description?
+          //   This is/should be translated in the UI when it's displayed?
+          $views_field['title'] = $field_definitions[$field_name]['label'];
+          $views_field['help'] = $field_definitions[$field_name]['description'];
+          // @todo Find a proper find the mappings between typed data and views
+          //   handlers. Maybe the data types should define it with fallback to
+          //   standard or views should have the same naming scheme.
+          $views_field = $this->mapTypedDataToHandlerId($field_definitions[$field_name], $views_field);
+        }
+      }
+    }
+
+    return $data;
+  }
+
+  /**
+   * Provides a mapping from typed data plugin types to view plugin types.
+   *
+   * @param array $typed_data
+   *   The typed data plugin definition
+   * @param array $views_field
+   *   The views field data definition.
+   *
+   * @return array
+   *   The modified views data field definition.
+   */
+  protected function mapTypedDataToHandlerId($typed_data, $views_field) {
+    switch ($typed_data['type']) {
+      case 'integer_field':
+        $views_field['field']['id'] = 'numeric';
+        $views_field['argument']['id'] = 'numeric';
+        $views_field['filter']['id'] = 'numeric';
+        $views_field['sort']['id'] = 'standard';
+        break;
+      case 'string_field':
+        $views_field['field']['id'] = 'standard';
+        $views_field['argument']['id'] = 'string';
+        $views_field['filter']['id'] = 'string';
+        $views_field['sort']['id'] = 'standard';
+        break;
+      case 'language_field':
+        $views_field['field']['id'] = 'language';
+        $views_field['argument']['id'] = 'language';
+        $views_field['filter']['id'] = 'language';
+        $views_field['sort']['id'] = 'standard';
+        break;
+      case 'boolean_field':
+        $views_field['field']['id'] = 'boolean';
+        $views_field['argument']['id'] = 'numeric';
+        $views_field['filter']['id'] = 'boolean';
+        $views_field['sort']['id'] = 'standard';
+        break;
+      case 'uuid_field':
+        $views_field['field']['id'] = 'standard';
+        $views_field['argument']['id'] = 'string';
+        $views_field['filter']['id'] = 'string';
+        $views_field['sort']['id'] = 'standard';
+        break;
+      case 'entity_reference_field':
+        // @todo No idea to determine how to find out whether the field is a number/string ID.
+        // @todo Should the actual field handler respect that this is just renders a number
+        // @todo Create an optional entity field handler, that can render the
+        //   entity.
+        $views_field['field']['id'] = 'numeric';
+        $views_field['argument']['id'] = 'numeric';
+        $views_field['filter']['id'] = 'numeric';
+        $views_field['sort']['id'] = 'standard';
+
+        $entity_type = $typed_data['settings']['target_type'];
+        $entity_info = $this->entityManager->getDefinition($entity_type);
+        $views_field['relationship'] = array(
+          'base' => $entity_info['base_table'],
+          'base field' => $entity_info['entity_keys']['id'],
+          'label' => $typed_data['label'],
+          'id' => 'standard',
+        );
+        // @todo use the module handler to provide a way to hook in for other
+        //   modules. Therefore the mapping has to be static defined.
+    }
+
+    return $views_field;
+  }
+
+  /**
+   * Translates a string to the current language or to a given language.
+   *
+   * See the t() documentation for details.
+   */
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translationManager->translate($string, $args, $options);
+  }
+
+  /**
+   * Wraps drupal_schema_fields_sql().
+   *
+   * @return array
+   */
+  protected function drupalSchemaFieldsSql($table) {
+    return drupal_schema_fields_sql($table);
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/EntityViewsControllerInterface.php b/core/modules/views/lib/Drupal/views/EntityViewsControllerInterface.php
new file mode 100644
index 0000000..47c3d71
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/EntityViewsControllerInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\EntityViewsControllerInterface.
+ */
+
+namespace Drupal\views;
+
+/**
+ * Defines an interface for a entity views controller.
+ */
+interface EntityViewsControllerInterface {
+
+  /**
+   * Returns views data for a certain entity type.
+   *
+   * @return array
+   *
+   * @see hook_views_data().
+   */
+  public function viewsData();
+}
diff --git a/core/modules/views/tests/Drupal/views/Tests/EntityViewsControllerTest.php b/core/modules/views/tests/Drupal/views/Tests/EntityViewsControllerTest.php
new file mode 100644
index 0000000..67ae76d
--- /dev/null
+++ b/core/modules/views/tests/Drupal/views/Tests/EntityViewsControllerTest.php
@@ -0,0 +1,378 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Tests\EntityViewsControllerTest.
+ */
+
+namespace Drupal\views\Tests {
+
+use Drupal\entity_test\Entity\EntityTest;
+use Drupal\entity_test\Entity\EntityTestMul;
+  use Drupal\entity_test\Entity\EntityTestMulRev;
+  use Drupal\Tests\UnitTestCase;
+use Drupal\views\EntityViewsController;
+
+/**
+ * Tests the generic entity views controller.
+ *
+ * @covers \Drupal\views\EntityViewsController
+ */
+class EntityViewsControllerTest extends UnitTestCase {
+
+  /**
+   * Entity info to use in this test.
+   *
+   * @var array
+   */
+  protected $baseEntityInfo = array(
+    'base_table' => 'entity_test',
+    'label' => 'Entity test',
+    'entity_keys' => array(
+      'id' => 'id',
+    ),
+  );
+
+  /**
+   * The mocked entity storage.
+   *
+   * @var \Drupal\Core\Entity\DatabaseStorageController|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityStorage;
+
+  /**
+   * The mocked entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * The mocked module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $moduleHandler;
+
+  /**
+   * The mocked translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $translationManager;
+
+  /**
+   * The tested entity views controller.
+   *
+   * @var \Drupal\views\Tests\TestEntityViewsController
+   */
+  protected $viewsController;
+
+  protected function setUp() {
+    $this->entityStorage = $this->getMockBuilder('Drupal\Core\Entity\DatabaseStorageController')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+
+    $this->entityManager->expects($this->any())
+      ->method('getDefinition')
+      ->will($this->returnValueMap(array(
+        array('user', static::userEntityInfo()),
+      )));
+
+    $this->translationManager = $this->getStringTranslationStub();
+    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+
+    $this->viewsController = new TestEntityViewsController('entity_test', $this->baseEntityInfo, $this->entityStorage, $this->entityManager, $this->moduleHandler, $this->translationManager);
+  }
+
+  /**
+   * Tests base tables.
+   */
+  public function testBaseTables() {
+    $data = $this->viewsController->viewsData();
+
+    $this->assertEquals('id', $data['entity_test']['table']['base']['field']);
+    $this->assertEquals('Entity test', $data['entity_test']['table']['base']['title']);
+    $this->assertFalse(isset($data['entity_test_mul_property_data']));
+    $this->assertFalse(isset($data['revision_table']));
+    $this->assertFalse(isset($data['revision_data_table']));
+  }
+
+
+  /**
+   * Tests data_table support.
+   */
+  public function testDataTable() {
+    $entity_info = $this->baseEntityInfo += array(
+      'data_table' => 'entity_test_mul_property_data',
+    );
+    $this->viewsController->setEntityInfo($entity_info);
+    $this->viewsController->setEntityType('entity_test_mul');
+
+    // Tests the join definition between the base and the data table.
+    $data = $this->viewsController->viewsData();
+    $field_views_data = $data['entity_test_mul_property_data'];
+    $this->assertEquals(array('entity_test' => array('left_field' => 'id', 'field' => 'id')), $field_views_data['table']['join']);
+    $this->assertFalse(isset($data['revision_table']));
+    $this->assertFalse(isset($data['revision_data_table']));
+  }
+
+  /**
+   * Tests revision table support.
+   */
+  public function testRevisionTable() {
+    $entity_info = $this->baseEntityInfo += array(
+      'revision_table' => 'entity_test_mulrev_revision',
+      'revision_data_table' => 'entity_test_mulrev_property_revision',
+    );
+    $entity_info['entity_keys']['revision'] = 'revision_id';
+    $this->viewsController->setEntityInfo($entity_info);
+    $this->viewsController->setEntityType('entity_test_mulrev');
+
+    $data = $this->viewsController->viewsData();
+
+    // Tests the join definition between the base and the revision table.
+    $revision_data = $data['entity_test_mulrev_revision'];
+    $this->assertEquals(array('entity_test' => array('left_field' => 'id', 'field' => 'id')), $revision_data['table']['join']);
+    $revision_data = $data['entity_test_mulrev_property_revision'];
+    $this->assertEquals(array('entity_test_mulrev_revision' => array('left_field' => 'revision_id', 'field' => 'revision_id')), $revision_data['table']['join']);
+    $this->assertFalse(isset($data['data_table']));
+  }
+
+  /**
+   * Tests fields on the base table.
+   */
+  public function testBaseTableFields() {
+    $this->entityManager->expects($this->any())
+      ->method('getFieldDefinitions')
+      ->with('entity_test')
+      ->will($this->returnCallback(function() {
+        return EntityTest::baseFieldDefinitions('entity_test');
+      }));
+    $this->viewsController->setSchemaFields(array('entity_test' => array('id', 'uuid', 'type', 'langcode', 'name', 'user_id')));
+    $data = $this->viewsController->viewsData();
+
+    $this->assertNumericField($data['entity_test']['id']);
+    $this->assertUuidField($data['entity_test']['uuid']);
+    $this->assertStringField($data['entity_test']['type']);
+    $this->assertLanguageField($data['entity_test']['langcode']);
+    $this->assertStringField($data['entity_test']['name']);
+
+    $this->assertEntityReferenceField($data['entity_test']['user_id']);
+    $relationship = $data['entity_test']['user_id']['relationship'];
+    $this->assertEquals('users', $relationship['base']);
+    $this->assertEquals('uid', $relationship['base field']);
+  }
+
+  /**
+   * Tests fields on the data table.
+   */
+  public function testDataTableFields() {
+    $this->entityManager->expects($this->any())
+      ->method('getFieldDefinitions')
+      ->with('entity_test_mul')
+      ->will($this->returnCallback(function() {
+        return EntityTestMul::baseFieldDefinitions('entity_test_mul');
+      }));
+    $this->viewsController->setSchemaFields(array(
+      'entity_test_mul' => array('id', 'uuid', 'type', 'langcode'),
+      'entity_test_mul_property_data' => array('id', 'langcode', 'default_langcode', 'name', 'user_id'),
+    ));
+
+    $entity_info = $this->baseEntityInfo += array(
+      'data_table' => 'entity_test_mul_property_data',
+    );
+    $entity_info['base_table'] = 'entity_test_mul';
+    $this->viewsController->setEntityInfo($entity_info);
+    $this->viewsController->setEntityType('entity_test_mul');
+
+    $data = $this->viewsController->viewsData();
+
+    // Check the base fields.
+    $this->assertNumericField($data['entity_test_mul']['id']);
+    $this->assertUuidField($data['entity_test_mul']['uuid']);
+    $this->assertStringField($data['entity_test_mul']['type']);
+    $this->assertLanguageField($data['entity_test_mul']['langcode']);
+    // Also ensure that field_data only fields don't appear on the base table.
+    $this->assertFalse(isset($data['entity_test_mul']['name']));
+    $this->assertFalse(isset($data['entity_test_mul']['user_id']));
+
+    // Check the data fields.
+    $this->assertNumericField($data['entity_test_mul_property_data']['id']);
+    $this->assertLanguageField($data['entity_test_mul_property_data']['langcode']);
+    // @todo Plach said the default language should not be exposed.
+    $this->assertStringField($data['entity_test_mul_property_data']['name']);
+
+    $this->assertEntityReferenceField($data['entity_test_mul_property_data']['user_id']);
+    $relationship = $data['entity_test_mul_property_data']['user_id']['relationship'];
+    $this->assertEquals('users', $relationship['base']);
+    $this->assertEquals('uid', $relationship['base field']);
+  }
+
+  /**
+   * Tests fields on the revision table.
+   */
+  public function testRevisionTableFields() {
+    $this->entityManager->expects($this->any())
+      ->method('getFieldDefinitions')
+      ->with('entity_test_mulrev')
+      ->will($this->returnCallback(function() {
+        return EntityTestMulRev::baseFieldDefinitions('entity_test_mul');
+      }));
+    $this->viewsController->setSchemaFields(array(
+      'entity_test_mulrev' => array('id', 'revision_id', 'uuid', 'type'),
+      'entity_test_mulrev_revision' => array('id', 'revision_id', 'langcode'),
+      'entity_test_mulrev_property_data' => array('id', 'revision_id', 'langcode', 'default_langcode', 'name', 'user_id'),
+      'entity_test_mulrev_property_revision' => array('id', 'revision_id', 'langcode', 'default_langcode', 'name', 'user_id'),
+    ));
+
+    $entity_info = $this->baseEntityInfo += array(
+      'data_table' => 'entity_test_mul_property_data',
+      'revision_table' => 'entity_test_mulrev_revision',
+      'revision_data_table' => 'entity_test_mulrev_property_revision',
+    );
+    $entity_info['base_table'] = 'entity_test_mulrev';
+    $this->viewsController->setEntityInfo($entity_info);
+    $this->viewsController->setEntityType('entity_test_mulrev');
+
+    $data = $this->viewsController->viewsData();
+
+    // Check the base fields.
+    $this->assertNumericField($data['entity_test_mulrev']['id']);
+    $this->assertNumericField($data['entity_test_mulrev']['revision_id']);
+    $this->assertUuidField($data['entity_test_mulrev']['uuid']);
+    $this->assertStringField($data['entity_test_mulrev']['type']);
+
+    // Also ensure that field_data only fields don't appear on the base table.
+    $this->assertFalse(isset($data['entity_test_mulrev']['name']));
+    $this->assertFalse(isset($data['entity_test_mulrev']['langcode']));
+    $this->assertFalse(isset($data['entity_test_mulrev']['user_id']));
+
+    // Check the revision fields.
+    $this->assertNumericField($data['entity_test_mulrev_revision']['id']);
+    $this->assertNumericField($data['entity_test_mulrev_revision']['revision_id']);
+    $this->assertLanguageField($data['entity_test_mulrev_revision']['langcode']);
+    // Also ensure that field_data only fields don't appear on the revision table.
+    $this->assertFalse(isset($data['entity_test_mulrev_revision']['name']));
+    $this->assertFalse(isset($data['entity_test_mulrev_revision']['langcode']));
+    $this->assertFalse(isset($data['entity_test_mulrev_revision']['user_id']));
+
+    // @todo check property and revision property data.
+  }
+
+  /**
+   * Tests views data for a string field.
+   *
+   * @param array $data
+   *   The views data to check.
+   */
+  public function assertStringField($data) {
+    $this->assertEquals('standard', $data['field']['id']);
+    $this->assertEquals('string', $data['filter']['id']);
+    $this->assertEquals('string', $data['argument']['id']);
+    $this->assertEquals('standard', $data['sort']['id']);
+  }
+
+  /**
+   * Tests views data for a UUID field.
+   *
+   * @param array $data
+   *   The views data to check.
+   */
+  public function assertUuidField($data) {
+    // @todo Can we provide additional support for UUIDs in views?
+    $this->assertEquals('standard', $data['field']['id']);
+    $this->assertEquals('string', $data['filter']['id']);
+    $this->assertEquals('string', $data['argument']['id']);
+    $this->assertEquals('standard', $data['sort']['id']);
+  }
+
+  /**
+   * Tests views data for a numeric field.
+   *
+   * @param array $data
+   *   The views data to check.
+   */
+  public function assertNumericField($data) {
+    $this->assertEquals('numeric', $data['field']['id']);
+    $this->assertEquals('numeric', $data['filter']['id']);
+    $this->assertEquals('numeric', $data['argument']['id']);
+    $this->assertEquals('standard', $data['sort']['id']);
+  }
+
+  /**
+   * Tests views data for a language field.
+   *
+   * @param array $data
+   *   The views data to check.
+   */
+  public function assertLanguageField($data) {
+    $this->assertEquals('language', $data['field']['id']);
+    $this->assertEquals('language', $data['filter']['id']);
+    $this->assertEquals('language', $data['argument']['id']);
+    $this->assertEquals('standard', $data['sort']['id']);
+  }
+
+  public function assertEntityReferenceField($data) {
+    $this->assertEquals('numeric', $data['field']['id']);
+    $this->assertEquals('numeric', $data['filter']['id']);
+    $this->assertEquals('numeric', $data['argument']['id']);
+    $this->assertEquals('standard', $data['sort']['id']);
+  }
+
+  /**
+   * Returns entity info for the user entity.
+   *
+   * @return array
+   */
+  public static function userEntityInfo() {
+    return array(
+      'id' => 'user',
+      'label' => 'User',
+      'base_table' => 'users',
+      'entity_keys' => array(
+        'id' => 'uid',
+        'uuid' => 'uuid',
+      ),
+    );
+  }
+
+}
+
+class TestEntityViewsController extends EntityViewsController {
+
+  protected $schemaFields = array();
+
+  public function setSchemaFields($fields) {
+    $this->schemaFields = $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function drupalSchemaFieldsSql($table) {
+    return isset($this->schemaFields[$table]) ? $this->schemaFields[$table] : array();
+  }
+
+  public function setEntityInfo(array $entity_info) {
+    $this->entityInfo = $entity_info;
+  }
+
+  public function setEntityType($entity_type) {
+    $this->entityType = $entity_type;
+  }
+
+}
+
+}
+
+namespace {
+  use Drupal\Component\Utility\String;
+
+  if (!function_exists('t')) {
+    function t($string, array $args = array()) {
+      return String::format($string, $args);
+    }
+  }
+}
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index edf844a..92fdc1a 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -112,8 +112,9 @@ function views_views_data() {
     ),
   );
 
+
   // Registers an entity area handler per entity type.
-  foreach (entity_get_info() as $entity_type => $entity_info) {
+  foreach (\Drupal::entityManager()->getDefinitions() as $entity_type => $entity_info) {
     // Exclude entity types, which cannot be rendered.
     if (!empty($entity_info['controllers']['view_builder'])) {
       $label = $entity_info['label'];
@@ -126,6 +127,13 @@ function views_views_data() {
         ),
       );
     }
+
+    // Execute the views controller for each entity type.
+    if (isset($entity_info['views_controller_class'])) {
+      /** @var $views_controller \Drupal\views\EntityViewsControllerInterface */
+      $views_controller = \Drupal::entityManager()->getController($entity_type, 'views');
+      $data += $views_controller->viewsData();
+    }
   }
 
   return $data;
