diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index b651e32..e07b4aa 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -47,6 +47,7 @@ function entity_get_info($entity_type = NULL) {
           'fieldable' => FALSE,
           'entity class' => 'Drupal\Core\Entity\Entity',
           'controller class' => 'Drupal\Core\Entity\DatabaseStorageController',
+          'list controller class' => 'Drupal\Core\Entity\EntityListController',
           'form controller class' => array(
             'default' => 'Drupal\Core\Entity\EntityFormController',
           ),
@@ -530,3 +531,33 @@ function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_st
     field_attach_submit($entity_type, $entity, $form, $form_state);
   }
 }
+
+/**
+ * Returns an entity list controller for a given entity type.
+ *
+ * Since there might be different scenarios in which an entity is edited,
+ * multiple form controllers suitable to the different operations may be defined.
+ * If no controller is found for the default operation, the base class will be
+ * used. If a non-existing non-default operation is specified an exception will
+ * be thrown.
+ *
+ * @see hook_entity_info()
+ *
+ * @param string $entity_type
+ *   The type of the entity.
+ *
+ * @return Drupal\Core\Entity\EntityListControllerInterface
+ *   An entity list controller instance.
+ */
+function entity_list_controller($entity_type) {
+  $instances = &drupal_static(__FUNCTION__, array());
+
+  if (isset($instances[$entity_type])) {
+    return $instances[$entity_type];
+  }
+
+  $info = entity_get_info($entity_type);
+  $class = $info['list controller class'];
+  $instances[$entity_type] = new $class($entity_type);
+  return $instances[$entity_type];
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityListController.php b/core/lib/Drupal/Core/Entity/EntityListController.php
new file mode 100644
index 0000000..8c8d165
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityListController.php
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityListController.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Provides a generic implementation of an entity list controller.
+ */
+class EntityListController implements EntityListControllerInterface {
+
+  /**
+   * The entity storage controller class.
+   *
+   * @var Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  protected $storage;
+
+  /**
+   * The entity type name.
+   *
+   * @var string
+   */
+  protected $entityType;
+
+  /**
+   * The entity info.
+   *
+   * @var array
+   */
+  protected $entityInfo;
+
+  public function __construct($entity_type) {
+    $this->entityType = $entity_type;
+    $this->storage = entity_get_controller($this->entityType);
+    $this->entityInfo = entity_get_info($this->entityType);
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::getStorageController();
+   */
+  public function getStorageController() {
+    return $this->storage;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::load();
+   */
+  public function load() {
+    return $this->storage->load();
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::getOperations().
+   */
+  public function getOperations(EntityInterface $entity) {
+    $uri = $entity->uri();
+    $operations['edit'] = array(
+      'title' => t('Edit'),
+      'href' => $uri['path'] . '/edit',
+      'options' => $uri['options'],
+      'weight' => 10,
+    );
+    $operations['delete'] = array(
+      'title' => t('Delete'),
+      'href' => $uri['path'] . '/delete',
+      'options' => $uri['options'],
+      'weight' => 100,
+    );
+    return $operations;
+  }
+
+  public function getPath() {
+    return $this->entityInfo['list path'];
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::buildHeader();
+   */
+  public function buildHeader() {
+    $row['label'] = t('Label');
+    $row['id'] = t('Machine name');
+    $row['operations'] = t('Operations');
+    return $row;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::buildRow();
+   */
+  public function buildRow(EntityInterface $entity) {
+    $row['label'] = $entity->label();
+    $row['id'] = $entity->id();
+    $operations = $this->buildOperations($entity);
+    $row['operations'] = drupal_render($operations);
+    return $row;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::buildOperations();
+   */
+  public function buildOperations(EntityInterface $entity) {
+    // Retrieve and sort operations.
+    $operations = $this->getOperations($entity);
+    uasort($operations, 'drupal_sort_weight');
+    $build = array(
+      '#theme' => 'links',
+      '#links' => $operations,
+    );
+    return $build;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityListControllerInterface::render();
+   */
+  public function render() {
+    $build = array(
+      '#theme' => 'table',
+      '#header' => $this->buildHeader(),
+      '#rows' => array(),
+      '#empty' => t('There is no @label yet. <a href="@add-url">Add one</a>.', array(
+        '@label' => $this->entityInfo['label'],
+        '@add-url' => $this->getPath() . '/add',
+      )),
+    );
+    foreach ($this->load() as $entity) {
+      $build['#rows'][$entity->id()] = $this->buildRow($entity);
+    }
+    return $build;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php
new file mode 100644
index 0000000..e564243
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityListControllerInterface.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityListControllerInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Defines an interface for entity list controllers.
+ */
+interface EntityListControllerInterface {
+
+  /**
+   * Gets the entity storage controller.
+   *
+   * @var Drupal\Core\Entity\EntityStorageControllerInterface
+   */
+  public function getStorageController();
+
+  /**
+   * Loads entities of this type.
+   *
+   * @return array
+   *   An array of entities implementing Drupal\Core\Entity\EntityInterface.
+   */
+  public function load();
+
+  /**
+   * Provides an array of information to render operation links.
+   *
+   * @param Drupal\Core\Entity\EntityInterface $entity
+   *   The entity the operations are for.
+   *
+   * @return array
+   *   An array of operation link data to use in buildOperations.
+   */
+  public function getOperations(EntityInterface $entity);
+
+  /**
+   * Builds the header row.
+   *
+   * @return array
+   *   An array of header strings.
+   */
+  public function buildHeader();
+
+  /**
+   * Builds an array of data for each row.
+   *
+   * @param Drupal\Core\Entity\EntityInterface $entity
+   *
+   * @return array
+   *   An array of fields to use for this entity.
+   */
+  public function buildRow(EntityInterface $entity);
+
+  /**
+   * Renders a list of operation links.
+   *
+   * @return array
+   *   A renderable array of operation links.
+   */
+  public function buildOperations(EntityInterface $entity);
+
+  /**
+   * Renders the list page markup to be output.
+   *
+   * @return string
+   *   The output markup for the listing page.
+   */
+  public function render();
+
+}
diff --git a/core/modules/config/lib/Drupal/config/ConfigEntityListController.php b/core/modules/config/lib/Drupal/config/ConfigEntityListController.php
new file mode 100644
index 0000000..7320291
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/ConfigEntityListController.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\ConfigEntityListController.
+ */
+
+namespace Drupal\config;
+
+use Drupal\Core\Entity\EntityListController;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Default list controller for ConfigEntity objects.
+ */
+class ConfigEntityListController extends EntityListController {
+
+  /**
+   * Overrides Drupal\Core\Entity\EntityListController::load().
+   */
+  public function load() {
+    $entities = parent::load();
+    uasort($entities, 'Drupal\config\ConfigEntityBase::sort');
+    return $entities;
+  }
+
+}
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
new file mode 100644
index 0000000..cf38bf7
--- /dev/null
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigEntityListTest.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\config\Tests\ConfigEntityListTest.
+ */
+
+namespace Drupal\config\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\config_test\ConfigTest;
+
+/**
+ * Tests listing of configuration entities.
+ */
+class ConfigEntityListTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('config_test');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Configuration entity list',
+      'description' => 'Tests listing of configuration entities.',
+      'group' => 'Configuration',
+    );
+  }
+
+  /**
+   * Tests entity list controller functionality.
+   */
+  function testList() {
+    $controller = entity_list_controller('config_test');
+
+    // Get a list of ConfigTest entities.
+    $list = $controller->load();
+    $this->assertEqual(count($list), 1, '1 ConfigTest entity found.');
+    $this->assertTrue(!empty($list['default']), '"Default" ConfigTest entity ID found.');
+    $this->assertTrue($list['default'] instanceof ConfigTest, '"Default" ConfigTest entity is an instance of ConfigTest.');
+  }
+
+  /**
+   * Tests the listing UI.
+   */
+  function testListUI() {
+    $page = $this->drupalGet('admin/structure/config_test');
+    $this->assertText('Test configuration');
+
+    // Verify that the default ConfigTest configuration appears.
+    $this->assertText('default');
+    $this->assertText('Default');
+
+    // Verify that operation links appear.
+    foreach (array('Edit', 'Delete') as $link) {
+      $this->drupalSetContent($page);
+      $this->assertLink($link);
+      $this->clickLink($link);
+      $this->assertResponse(200);
+    }
+  }
+
+}
diff --git a/core/modules/config/tests/config_test/config_test.module b/core/modules/config/tests/config_test/config_test.module
index 44d4087..a6ceca7 100644
--- a/core/modules/config/tests/config_test/config_test.module
+++ b/core/modules/config/tests/config_test/config_test.module
@@ -82,6 +82,8 @@ function config_test_entity_info() {
     'label' => 'Test configuration',
     'controller class' => 'Drupal\config\ConfigStorageController',
     'entity class' => 'Drupal\config_test\ConfigTest',
+    'list controller class' => 'Drupal\config\ConfigEntityListController',
+    'list path' => 'admin/structure/config_test',
     'uri callback' => 'config_test_uri',
     'config prefix' => 'config_test.dynamic',
     'entity keys' => array(
@@ -176,36 +178,8 @@ function config_test_delete($id) {
  * Page callback; Lists available ConfigTest objects.
  */
 function config_test_list_page() {
-  $entities = entity_load_multiple('config_test');
-  uasort($entities, 'Drupal\config\ConfigEntityBase::sort');
-
-  $rows = array();
-  foreach ($entities as $config_test) {
-    $uri = $config_test->uri();
-    $row = array();
-    $row['name']['data'] = array(
-      '#type' => 'link',
-      '#title' => $config_test->label(),
-      '#href' => $uri['path'],
-      '#options' => $uri['options'],
-    );
-    $row['delete']['data'] = array(
-      '#type' => 'link',
-      '#title' => t('Delete'),
-      '#href' => $uri['path'] . '/delete',
-      '#options' => $uri['options'],
-    );
-    $rows[] = $row;
-  }
-  $build = array(
-    '#theme' => 'table',
-    '#header' => array('Name', 'Operations'),
-    '#rows' => $rows,
-    '#empty' => format_string('No test configuration defined. <a href="@add-url">Add some</a>', array(
-      '@add-url' => url('admin/structure/config_test/add'),
-    )),
-  );
-  return $build;
+  $controller = entity_list_controller('config_test');
+  return $controller->render();
 }
 
 /**
