diff --git a/core/core.services.yml b/core/core.services.yml
index 9af8ff1..d552261 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -252,8 +252,15 @@ services:
       - { name: route_filter }
   paramconverter_manager:
     class: Drupal\Core\ParamConverter\ParamConverterManager
+    calls:
+      - [setContainer, ['@service_container']]
     tags:
       - { name: route_enhancer }
+  paramconverter_subscriber:
+    class: Drupal\Core\EventSubscriber\ParamConverterSubscriber
+    tags:
+      - { name: event_subscriber }
+    arguments: ['@paramconverter_manager']
   paramconverter.entity:
     class: Drupal\Core\ParamConverter\EntityConverter
     tags:
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
index 6fe1447..d18103e 100644
--- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
@@ -34,14 +34,14 @@ public function process(ContainerBuilder $container) {
     foreach ($container->findTaggedServiceIds('paramconverter') as $id => $attributes) {
       $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
 
-      $services[$priority][] = new Reference($id);
+      $services[$priority][] = $id;
     }
 
     krsort($services);
 
     foreach ($services as $priority) {
       foreach ($priority as $service) {
-        $manager->addMethodCall('addConverter', array($service));
+        $manager->addMethodCall('addConverterService', array($service));
       }
     }
   }
diff --git a/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
new file mode 100644
index 0000000..8963fb7
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\EventSubscriber\ParamConverterSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\ParamConverter\ParamConverterManager;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\Core\Routing\RouteBuildEvent;
+
+/**
+ * Event subscriber for registering parameter converters with routes.
+ */
+class ParamConverterSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The parameter converter manager.
+   *
+   * @var \Drupal\Core\ParamConverter\ParamConverterManager
+   */
+  protected $paramConverterManager;
+
+  /**
+   * Constructs a new ParamConverterSubscriber.
+   *
+   * @param \Drupal\Core\ParamConverter\ParamConverterManager $param_converter_manager
+   *   The access check manager that will be responsible for applying
+   *   AccessCheckers against routes.
+   */
+  public function __construct(ParamConverterManager $param_converter_manager) {
+    $this->paramConverterManager = $param_converter_manager;
+  }
+
+  /**
+   * Apply access checks to routes.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The event to process.
+   */
+  public function onRoutingRouteAlterSetParameterConverters(RouteBuildEvent $event) {
+    $this->paramConverterManager->setConverters($event->getRouteCollection());
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetParameterConverters', 10);
+
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
index f28906f..fc5fb8f 100644
--- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -35,72 +35,20 @@ public function __construct(EntityManager $entityManager) {
   }
 
   /**
-   * Tries to upcast every variable to an entity type.
-   *
-   * If there is a type denoted in the route options it will try to upcast to
-   * it, if there is no definition in the options it will try to upcast to an
-   * entity type of that name. If the chosen enity type does not exists it will
-   * leave the variable untouched.
-   * If the entity type exist, but there is no entity with the given id it will
-   * convert the variable to NULL.
-   *
-   * Example:
-   *
-   * pattern: '/a/{user}/some/{foo}/and/{bar}/'
-   * options:
-   *   converters:
-   *     foo: 'node'
-   *
-   * The value for {user} will be converted to a user entity and the value
-   * for {foo} to a node entity, but it will not touch the value for {bar}.
-   *
-   * It will not process variables which are marked as converted. It will mark
-   * any variable it processes as converted.
-   *
-   * @param array &$variables
-   *   Array of values to convert to their corresponding objects, if applicable.
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param array &$converted
-   *   Array collecting the names of all variables which have been
-   *   altered by a converter.
+   * {@inheritdoc}
    */
-  public function process(array &$variables, Route $route, array &$converted) {
-    $variable_names = $route->compile()->getVariables();
-
-    $options = $route->getOptions();
-    $configuredTypes = isset($options['converters']) ? $options['converters'] : array();
-
-    $entityTypes = array_keys($this->entityManager->getDefinitions());
-
-    foreach ($variable_names as $name) {
-      // Do not process this variable if it's already marked as converted.
-      if (in_array($name, $converted)) {
-        continue;
-      }
-
-      // Obtain entity type to convert to from the route configuration or just
-      // use the variable name as default.
-      if (array_key_exists($name, $configuredTypes)) {
-        $type = $configuredTypes[$name];
-      }
-      else {
-        $type = $name;
-      }
-
-      if (in_array($type, $entityTypes)) {
-        $value = $variables[$name];
-
-        $storageController = $this->entityManager->getStorageController($type);
-        $entities = $storageController->load(array($value));
-
-        // Make sure $entities is null, if upcasting fails.
-        $entity = $entities ? reset($entities) : NULL;
-        $variables[$name] = $entity;
+  public function applies($options, $name, Route $route) {
+    return !empty($options['entity']);
+  }
 
-        // Mark this variable as converted.
-        $converted[] = $name;
-      }
-    }
+  /**
+   * {@inheritdoc}
+   */
+  public function convert($value, $options, $name, Route $route, array $defaults) {
+    $storageController = $this->entityManager->getStorageController($options['entity']);
+    $entities = $storageController->load(array($value));
+    $entity = $entities ? reset($entities) : NULL;
+    return $entity;
   }
+
 }
diff --git a/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
index 74307c2..fe5a6fe 100644
--- a/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
@@ -17,13 +17,38 @@
   /**
    * Allows to convert variables to their corresponding objects.
    *
-   * @param array &$variables
-   *   Array of values to convert to their corresponding objects, if applicable.
+   * @param mixed $value
+   *   The current value of the variable.
+   * @param mixed $options
+   *   The variable options provided in the route definition.
+   * @param string $name
+   *   The name of the variable.
    * @param \Symfony\Component\Routing\Route $route
    *   The route object.
-   * @param array &$converted
-   *   Array collecting the names of all variables which have been
-   *   altered by a converter.
+   * @param array $defaults
+   *   The complete array of parameters returned by
+   *   \Symfony\Component\Routing\Matcher\RequestMatcherInterface::matchRequest()
+   *   and processed by prior route enhancers and parameter converters.
+   *
+   * @return mixed
+   *   The converted variable value.
+   */
+  public function convert($value, $options, $name, Route $route, array $defaults);
+
+  /**
+   * Determines if the converter applies to a specific route and variable.
+   *
+   * @param mixed $options
+   *   The variable options provided in the route definition.
+   * @param string $name
+   *   The name of the variable.
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route to consider attaching to.
+   *
+   * @return bool
+   *   TRUE if the converter applies to the passed route and variable, FALSE
+   *   otherwise.
    */
-  public function process(array &$variables, Route $route, array &$converted);
+  public function applies($options, $name, Route $route);
+
 }
diff --git a/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
index be5676e..238b2ae 100644
--- a/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * @file
  * Contains Drupal\Core\ParamConverter\ParamConverterManager.
@@ -7,14 +6,14 @@
 
 namespace Drupal\Core\ParamConverter;
 
-use Symfony\Component\DependencyInjection\ContainerAware;
 use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\RouteCollection;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\DependencyInjection\ContainerAware;
 use Symfony\Component\HttpFoundation\Request;
 
-use Drupal\Core\ParamConverter\ParamConverterInterface;
-
 /**
  * Provides a service which allows to enhance (say alter) the arguments coming
  * from the URL.
@@ -24,66 +23,134 @@
  * This class will not enhance any of the arguments itself, but allow other
  * services to register to do so.
  */
-class ParamConverterManager implements RouteEnhancerInterface {
+class ParamConverterManager extends ContainerAware implements RouteEnhancerInterface {
+
+  /**
+   * Array of registered parameter converter service ids.
+   *
+   * @var array
+   */
+  protected $converterIds;
 
   /**
-   * Converters managed by the ParamConverterManager.
+   * Array of parameter converter objects keyed by service id.
    *
    * @var array
    */
   protected $converters;
 
   /**
-   * Adds a converter to the paramconverter service.
+   * Registers a new ParamConverter by service ID.
    *
-   * @see \Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass
+   * @param string $service_id
+   *   The ID of the service in the Container that provides a converter.
+   */
+  public function addConverterService($service_id) {
+    $this->converterIds[] = $service_id;
+  }
+
+  /**
+   * For each route, saves a list of applicable converters to the route.
    *
-   * @param \Drupal\Core\ParamConverter\ParamConverterInterface $converter
-   *   The converter to add.
+   * @param \Symfony\Component\Routing\RouteCollection $routes
+   *   A collection of routes to apply converters to.
    */
-  public function addConverter(ParamConverterInterface $converter) {
-    $this->converters[] = $converter;
-    return $this;
+  public function setConverters(RouteCollection $routes) {
+    foreach ($routes as $route) {
+      $converters = $this->applies($route);
+      if (!empty($converters)) {
+        $route->setOption('_parameter_converters', $converters);
+      }
+    }
   }
 
   /**
-   * Implements \Symfony\Cmf\Component\Routing\Enhancer\ŖouteEnhancerIterface.
+   * Determine which registered parameter converters apply to a route.
    *
-   * Iterates over all registered converters and allows them to alter the
-   * defaults.
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route to get list of parameter converters for.
+   *
+   * @return array
+   *   An array of parameter converter service ids that apply to the passed
+   *   route.
+   */
+  protected function applies(Route $route) {
+    $converters = array();
+    $parameters = $route->getOption('parameters') ?: array();
+    foreach ($parameters as $parameter => $options) {
+      foreach ($this->converterIds as $service_id) {
+        if (empty($this->converters[$service_id])) {
+          $this->loadConverter($service_id);
+        }
+        if ($this->converters[$service_id]->applies($options, $parameter, $route)) {
+          $converters[$parameter][] = $service_id;
+        }
+      }
+    }
+    return $converters;
+  }
+
+  /**
+   * Invokes each registered converter for a given route.
    *
    * @param array $defaults
    *   The getRouteDefaults array.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The current request.
    *
+   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+   *   If a variable has been converted to NULL.
+   *
    * @return array
    *   The modified defaults.
    */
   public function enhance(array $defaults, Request $request) {
-    // This array will collect the names of all variables which have been
-    // altered by a converter.
-    // This serves two purposes:
-    // 1. It might prevent converters later in the pipeline to process
-    //    a variable again.
-    // 2. To check if upcasting was successfull after each converter had
-    //    a go. See below.
-    $converters = array();
-
     $route = $defaults[RouteObjectInterface::ROUTE_OBJECT];
+    $parameters = $route->getOption('_parameter_converters') ?: array();
 
-    foreach ($this->converters as $converter) {
-      $converter->process($defaults, $route, $converters);
+    // Bail out if no converter has been registered for the route.
+    if (empty($parameters)) {
+      return $defaults;
     }
 
-    // Check if all upcasting yielded a result.
-    // If an upcast value is NULL do a 404.
-    foreach ($converters as $variable) {
-      if ($defaults[$variable] === NULL) {
-        throw new NotFoundHttpException();
+    // Invoke each registered converter.
+    $options = $route->getOption('parameters') ?: array();
+    foreach ($parameters as $parameter => $converters) {
+      foreach ($converters as $service_id) {
+        if (empty($this->converters[$service_id])) {
+          $this->loadConverter($service_id);
+        }
+
+        // Invoke the converter.
+        $defaults[$parameter] = $this->converters[$service_id]->convert($defaults[$parameter], $options[$parameter], $parameter, $route, $defaults);
+
+        // If a converter returns NULL it means that the parameter could not
+        // be converted in which case we throw a 404.
+        if ($defaults[$parameter] === NULL) {
+          throw new NotFoundHttpException();
+        }
       }
     }
 
     return $defaults;
   }
+
+  /**
+   * Lazy-loads access check services.
+   *
+   * @param string $service_id
+   *   The service id of the access check service to load.
+   *
+   * @throws \InvalidArgumentException
+   *   If the given service has not been registered as a converter.
+   */
+  protected function loadConverter($service_id) {
+    if (!in_array($service_id, $this->converterIds)) {
+      throw new \InvalidArgumentException(sprintf('No converter has been registered for %s', $service_id));
+    }
+
+    $this->converters[$service_id] = $this->container->get($service_id);
+  }
+
 }
+
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index c41cf0e..9c68994 100644
--- a/core/modules/aggregator/aggregator.routing.yml
+++ b/core/modules/aggregator/aggregator.routing.yml
@@ -18,6 +18,10 @@ aggregator_feed_items_delete:
     _form: '\Drupal\aggregator\Form\FeedItemsDelete'
   requirements:
     _permission: 'administer news feeds'
+  options:
+    parameters:
+      aggregator_feed:
+        entity: 'aggregator_feed'
 
 aggregator_feed_delete:
   pattern: '/admin/config/services/aggregator/delete/feed/{aggregator_feed}'
@@ -25,6 +29,10 @@ aggregator_feed_delete:
     _form: '\Drupal\aggregator\Form\FeedDelete'
   requirements:
     _permission: 'administer news feeds'
+  options:
+    parameters:
+      aggregator_feed:
+        entity: 'aggregator_feed'
 
 aggregator_feed_add:
   pattern: '/admin/config/services/aggregator/add/feed'
diff --git a/core/modules/block/block.routing.yml b/core/modules/block/block.routing.yml
index af247b0..6e65417 100644
--- a/core/modules/block/block.routing.yml
+++ b/core/modules/block/block.routing.yml
@@ -4,3 +4,7 @@ block_admin_block_delete:
     _form: '\Drupal\block\Form\AdminBlockDeleteForm'
   requirements:
     _permission: 'administer blocks'
+  options:
+    parameters:
+      block:
+        entity: 'block'
diff --git a/core/modules/contact/contact.routing.yml b/core/modules/contact/contact.routing.yml
index 1f34937..7443405 100644
--- a/core/modules/contact/contact.routing.yml
+++ b/core/modules/contact/contact.routing.yml
@@ -4,3 +4,7 @@ contact_category_delete:
     _form: '\Drupal\contact\Form\DeleteForm'
   requirements:
     _permission: 'administer contact forms'
+  options:
+    parameters:
+      contact_category:
+        entity: 'contact_category'
diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
index 94e7b09..69c5bf8 100644
--- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
+++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php
@@ -55,28 +55,48 @@ public function routes(RouteBuildEvent $event) {
         $route = new Route(
           "$path/fields/{field_instance}",
           array('_form' => '\Drupal\field_ui\Form\FieldInstanceEditForm'),
-          array('_permission' => 'administer ' . $entity_type . ' fields')
+          array('_permission' => 'administer ' . $entity_type . ' fields'),
+          array('parameters' => array(
+            'field_instance' => array(
+              'entity' => 'field_instance',
+            ),
+          ))
         );
         $collection->add("field_ui.instance_edit.$entity_type", $route);
 
         $route = new Route(
           "$path/fields/{field_instance}/widget-type",
           array('_form' => '\Drupal\field_ui\Form\FieldWidgetTypeForm'),
-          array('_permission' => 'administer ' . $entity_type . ' fields')
+          array('_permission' => 'administer ' . $entity_type . ' fields'),
+          array('parameters' => array(
+            'field_instance' => array(
+              'entity' => 'field_instance',
+            ),
+          ))
         );
         $collection->add("field_ui.widget_type.$entity_type", $route);
 
         $route = new Route(
           "$path/fields/{field_instance}/field-settings",
           array('_form' => '\Drupal\field_ui\Form\FieldSettingsForm'),
-          array('_permission' => 'administer ' . $entity_type . ' fields')
+          array('_permission' => 'administer ' . $entity_type . ' fields'),
+          array('parameters' => array(
+            'field_instance' => array(
+              'entity' => 'field_instance',
+            ),
+          ))
         );
         $collection->add("field_ui.settings.$entity_type", $route);
 
         $route = new Route(
           "$path/fields/{field_instance}/delete",
           array('_form' => '\Drupal\field_ui\Form\FieldDeleteForm'),
-          array('_permission' => 'administer ' . $entity_type . ' fields')
+          array('_permission' => 'administer ' . $entity_type . ' fields'),
+          array('parameters' => array(
+            'field_instance' => array(
+              'entity' => 'field_instance',
+            ),
+          ))
         );
         $collection->add("field_ui.delete.$entity_type", $route);
 
diff --git a/core/modules/filter/filter.routing.yml b/core/modules/filter/filter.routing.yml
index de9e9cb..ef9d5de 100644
--- a/core/modules/filter/filter.routing.yml
+++ b/core/modules/filter/filter.routing.yml
@@ -4,3 +4,7 @@ filter_admin_disable:
     _form: '\Drupal\filter\Form\DisableForm'
   requirements:
     _filter_disable_format_access: 'TRUE'
+  options:
+    parameters:
+      filter_format:
+        entity: 'filter_format'
diff --git a/core/modules/forum/forum.routing.yml b/core/modules/forum/forum.routing.yml
index f7d90f8..5bb79dc 100644
--- a/core/modules/forum/forum.routing.yml
+++ b/core/modules/forum/forum.routing.yml
@@ -4,6 +4,11 @@ forum_delete:
     _form: 'Drupal\forum\Form\DeleteForm'
   requirements:
     _permission: 'administer forums'
+  options:
+    parameters:
+      taxonomy_term:
+        entity: 'taxonomy_term'
+
 forum_settings:
   pattern: '/admin/structure/forum/settings'
   defaults:
diff --git a/core/modules/image/image.routing.yml b/core/modules/image/image.routing.yml
index 0aa23ba..2374246 100644
--- a/core/modules/image/image.routing.yml
+++ b/core/modules/image/image.routing.yml
@@ -4,6 +4,10 @@ image_style_delete:
     _form: '\Drupal\image\Form\ImageStyleDeleteForm'
   requirements:
     _permission: 'administer image styles'
+  options:
+    parameters:
+      image_style:
+        entity: 'image_style'
 
 image_effect_delete:
   pattern: 'admin/config/media/image-styles/manage/{image_style}/effects/{image_effect}/delete'
@@ -11,3 +15,7 @@ image_effect_delete:
     _form: '\Drupal\image\Form\ImageEffectDeleteForm'
   requirements:
     _permission: 'administer image styles'
+  options:
+    parameters:
+      image_style:
+        entity: 'image_style'
diff --git a/core/modules/menu/menu.routing.yml b/core/modules/menu/menu.routing.yml
index aafe5c3..50c9506 100644
--- a/core/modules/menu/menu.routing.yml
+++ b/core/modules/menu/menu.routing.yml
@@ -11,6 +11,10 @@ menu_link_reset:
     _form: '\Drupal\menu\Form\MenuLinkResetForm'
   requirements:
     _permission: 'administer menu'
+  options:
+    parameters:
+      menu_link:
+        entity: 'menu_link'
 
 menu_link_delete:
   pattern: 'admin/structure/menu/item/{menu_link}/delete'
@@ -18,6 +22,10 @@ menu_link_delete:
     _form: '\Drupal\menu\Form\MenuLinkDeleteForm'
   requirements:
     _access_menu_delete_link: 'TRUE'
+  options:
+    parameters:
+      menu_link:
+        entity: 'menu_link'
 
 menu_delete_menu:
   pattern: 'admin/structure/menu/manage/{menu}/delete'
@@ -25,3 +33,7 @@ menu_delete_menu:
     _form: '\Drupal\menu\Form\MenuDeleteMenuForm'
   requirements:
     _access_menu_delete_menu: 'TRUE'
+  options:
+    parameters:
+      menu:
+        entity: 'menu'
diff --git a/core/modules/picture/picture.routing.yml b/core/modules/picture/picture.routing.yml
index 96da6d3..faecf87 100644
--- a/core/modules/picture/picture.routing.yml
+++ b/core/modules/picture/picture.routing.yml
@@ -4,3 +4,7 @@ picture_mapping_action_confirm:
     _form: '\Drupal\picture\Form\PictureMappingActionConfirmForm'
   requirements:
     _permission: 'administer pictures'
+  options:
+    parameters:
+      picture_mapping:
+        entity: 'picture_mapping'
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index 488d187..778d9a2 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -4,6 +4,10 @@ shortcut_link_delete:
     _form: 'Drupal\shortcut\Form\LinkDelete'
   requirements:
     _access_shortcut_link_delete: 'TRUE'
+  options:
+    parameters:
+      menu_link:
+        entity: 'menu_link'
 
 shortcut_set_delete:
   pattern: '/admin/config/user-interface/shortcut/manage/{shortcut}/delete'
@@ -11,6 +15,10 @@ shortcut_set_delete:
     _form: 'Drupal\shortcut\Form\SetDelete'
   requirements:
     _entity_access: 'shortcut.delete'
+  options:
+    parameters:
+      shortcut:
+        entity: 'shortcut'
 
 shortcut_set_admin:
   pattern: '/admin/config/user-interface/shortcut'
diff --git a/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
index 14a0422..ca01ebc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
@@ -50,14 +50,12 @@ public function testUpcasting() {
     $this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: $foo", 'user and node upcast by entity name');
 
     // paramconverter_test/test_node_user_user/{node}/{foo}/{user}
-    // converters:
-    //   foo: 'user'
+    // options.parameters.foo.type = user
     $this->drupalGet("paramconverter_test/test_node_user_user/{$node->nid}/{$user->uid}/{$user->uid}");
     $this->assertRaw("user: {$user->label()}, node: {$node->label()}, foo: {$user->label()}", 'foo converted to user as well');
 
     // paramconverter_test/test_node_node_foo/{user}/{node}/{foo}
-    // converters:
-    //   user: 'node'
+    // options.parameters.user.type = node
     $this->drupalGet("paramconverter_test/test_node_node_foo/{$node->nid}/{$node->nid}/$foo");
     $this->assertRaw("user: {$node->label()}, node: {$node->label()}, foo: $foo", 'user is upcast to node (rather than to user)');
   }
@@ -69,8 +67,7 @@ public function testSameTypes() {
     $node = $this->drupalCreateNode(array('title' => $this->randomName(8)));
     $parent = $this->drupalCreateNode(array('title' => $this->randomName(8)));
     // paramconverter_test/node/{node}/set/parent/{parent}
-    // converters:
-    //   parent: 'node'
+    // options.parameters.parent.type = node
     $this->drupalGet("paramconverter_test/node/" . $node->nid . "/set/parent/" . $parent->nid);
     $this->assertRaw("Setting '" . $parent->title . "' as parent of '" . $node->title . "'.");
   }
diff --git a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
index 9d226e4..64f4b81 100644
--- a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
+++ b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
@@ -4,6 +4,12 @@ paramconverter_test_user_node_foo:
     _content: '\Drupal\paramconverter_test\TestControllers::testUserNodeFoo'
   requirements:
     _access: 'TRUE'
+  options:
+    parameters:
+      user:
+        entity: 'user'
+      node:
+        entity: 'node'
 
 paramconverter_test_node_user_user:
   pattern: '/paramconverter_test/test_node_user_user/{node}/{foo}/{user}'
@@ -12,8 +18,13 @@ paramconverter_test_node_user_user:
   requirements:
     _access: 'TRUE'
   options:
-    converters:
-      foo: 'user'
+    parameters:
+      node:
+        entity: 'node'
+      foo:
+        entity: 'user'
+      user:
+        entity: 'user'
 
 paramconverter_test_node_node_foo:
   pattern: '/paramconverter_test/test_node_node_foo/{user}/{node}/{foo}'
@@ -22,8 +33,13 @@ paramconverter_test_node_node_foo:
   requirements:
     _access: 'TRUE'
   options:
-    converters:
-      user: 'node'
+    parameters:
+      user:
+        entity: 'node'
+      node:
+        entity: 'node'
+      foo:
+        entity: 'node'
 
 paramconverter_test_node_set_parent:
   pattern: '/paramconverter_test/node/{node}/set/parent/{parent}'
@@ -32,5 +48,8 @@ paramconverter_test_node_set_parent:
   defaults:
     _content: '\Drupal\paramconverter_test\TestControllers::testNodeSetParent'
   options:
-    converters:
-      parent: 'node'
+    parameters:
+      node:
+        entity: 'node'
+      parent:
+        entity: 'node'
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php b/core/modules/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php
index f828de1..f163c2a 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php
@@ -10,7 +10,6 @@
 use Symfony\Component\Routing\Route;
 use Drupal\Core\ParamConverter\ParamConverterInterface;
 use Drupal\user\TempStoreFactory;
-use Drupal\views\ViewStorageInterface;
 use Drupal\views_ui\ViewUI;
 
 /**
@@ -36,72 +35,40 @@ public function __construct(TempStoreFactory $temp_store_factory) {
   }
 
   /**
-   * Tries to upcast every view entity to a decorated ViewUI object.
-   *
-   * The key refers to the portion of the route that is a view entity that
-   * should be prepared for the Views UI. If there is a non-null value, it will
-   * be used as the collection of a temp store object used for loading.
-   *
-   * Example:
-   *
-   * pattern: '/some/{view}/and/{foo}/and/{bar}'
-   * options:
-   *   converters:
-   *     foo: 'view'
-   *   tempstore:
-   *     view: 'views'
-   *     foo: NULL
-   *
-   * The values for {view} and {foo} will be converted to view entities prepared
-   * for the Views UI, with {view} being loaded from the views temp store, but
-   * it will not touch the value for {bar}.
-   *
-   * Note: This requires that the placeholder either be named {view}, or that a
-   * converter is specified as done above for {foo}.
-   *
-   * It will still process variables which are marked as converted. It will mark
-   * any variable it processes as converted.
-   *
-   * @param array &$variables
-   *   Array of values to convert to their corresponding objects, if applicable.
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param array &$converted
-   *   Array collecting the names of all variables which have been
-   *   altered by a converter.
+   * {@inheritdoc}
    */
-  public function process(array &$variables, Route $route, array &$converted) {
-    // If nothing was specified to convert, return.
-    $options = $route->getOptions();
-    if (!isset($options['tempstore'])) {
-      return;
-    }
-
-    foreach ($options['tempstore'] as $name => $collection) {
-      // Only convert if the variable is a view.
-      if ($variables[$name] instanceof ViewStorageInterface) {
-        // Get the temp store for this variable if it needs one.
-        // Attempt to load the view from the temp store, synchronize its
-        // status with the existing view, and store the lock metadata.
-        if ($collection && ($temp_store = $this->tempStoreFactory->get($collection)) && ($view = $temp_store->get($variables[$name]->id()))) {
-          if ($variables[$name]->status()) {
-            $view->enable();
-          }
-          else {
-            $view->disable();
-          }
-          $view->lock = $temp_store->getMetadata($variables[$name]->id());
-        }
-        // Otherwise, decorate the existing view for use in the UI.
-        else {
-          $view = new ViewUI($variables[$name]);
-        }
+  public function applies($options, $name, Route $route) {
+    return !empty($options['tempstore']);
+  }
 
-        // Store the new view and mark this variable as converted.
-        $variables[$name] = $view;
-        $converted[] = $name;
+  /**
+   * {@inheritdoc}
+   */
+  public function convert($value, $options, $parameter, Route $route, array $defaults) {
+    // Many things, not just Views, can use the tempstore. Here, we are only
+    // interested in upcasting Views. We are relying on an earlier converter
+    // (e.g., Drupal\Core\ParamConverter\EntityConverter) to have converted
+    // the desired parameters to View entities, so we must grab it out of
+    // $defaults, rather than $unconverted_parameters.
+    $collection = $options['tempstore'];
+    // Get the temp store for this variable if it needs one.
+    // Attempt to load the view from the temp store, synchronize its
+    // status with the existing view, and store the lock metadata.
+    if (($temp_store = $this->tempStoreFactory->get($collection)) && ($view = $temp_store->get($value->id()))) {
+      if ($value->status()) {
+        $view->enable();
       }
+      else {
+        $view->disable();
+      }
+      $view->lock = $temp_store->getMetadata($value->id());
+    }
+    // Otherwise, decorate the existing view for use in the UI.
+    else {
+      $view = new ViewUI($value);
     }
+
+    return $view;
   }
 
 }
diff --git a/core/modules/views_ui/views_ui.routing.yml b/core/modules/views_ui/views_ui.routing.yml
index f2046f6..0b22588 100644
--- a/core/modules/views_ui/views_ui.routing.yml
+++ b/core/modules/views_ui/views_ui.routing.yml
@@ -47,6 +47,10 @@ views_ui.operation:
   requirements:
     _permission: 'administer views'
     op: 'enable|disable'
+  options:
+    parameters:
+      view:
+        entity: 'view'
 
 views_ui.clone:
   pattern: '/admin/structure/views/view/{view}/clone'
@@ -54,6 +58,10 @@ views_ui.clone:
     _entity_form: 'view.clone'
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        entity: 'view'
 
 views_ui.delete:
   pattern: '/admin/structure/views/view/{view}/delete'
@@ -61,6 +69,10 @@ views_ui.delete:
     _form: 'Drupal\views_ui\Form\DeleteForm'
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        entity: 'view'
 
 views_ui.autocomplete:
   pattern: '/admin/views/ajax/autocomplete/tag'
@@ -68,12 +80,18 @@ views_ui.autocomplete:
     _controller: '\Drupal\views_ui\Routing\ViewsUIController::autocompleteTag'
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        entity: 'view'
 
 views_ui.edit:
   pattern: '/admin/structure/views/view/{view}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Routing\ViewsUIController::edit'
   requirements:
@@ -82,8 +100,10 @@ views_ui.edit:
 views_ui.edit.display:
   pattern: '/admin/structure/views/view/{view}/edit/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Routing\ViewsUIController::edit'
     display_id: NULL
@@ -93,8 +113,10 @@ views_ui.edit.display:
 views_ui.preview:
   pattern: '/admin/structure/views/view/{view}/preview/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _entity_form: 'view.preview'
     display_id: NULL
@@ -103,6 +125,10 @@ views_ui.preview:
 
 views_ui.breakLock:
   pattern: '/admin/structure/views/view/{view}/break-lock'
+  options:
+    parameters:
+      view:
+        entity: 'view'
   defaults:
     _form: '\Drupal\views_ui\Form\BreakLockForm'
     display_id: NULL
@@ -112,8 +138,10 @@ views_ui.breakLock:
 views_ui.form.addItem:
   pattern: '/admin/structure/views/{js}/add-item/{view}/{display_id}/{type}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\AddItem::getForm'
   requirements:
@@ -123,8 +151,10 @@ views_ui.form.addItem:
 views_ui.form.editDetails:
   pattern: '/admin/structure/views/{js}/edit-details/{view}/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\EditDetails::getForm'
   requirements:
@@ -134,8 +164,10 @@ views_ui.form.editDetails:
 views_ui.form.reorderDisplays:
   pattern: '/admin/structure/views/{js}/reorder-displays/{view}/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ReorderDisplays::getForm'
   requirements:
@@ -145,8 +177,10 @@ views_ui.form.reorderDisplays:
 views_ui.form.analyze:
   pattern: '/admin/structure/views/{js}/analyze/{view}/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Analyze::getForm'
   requirements:
@@ -156,8 +190,10 @@ views_ui.form.analyze:
 views_ui.form.rearrange:
   pattern: '/admin/structure/views/{js}/rearrange/{view}/{display_id}/{type}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Rearrange::getForm'
   requirements:
@@ -167,8 +203,10 @@ views_ui.form.rearrange:
 views_ui.form.rearrangeFilter:
   pattern: '/admin/structure/views/{js}/rearrange-filter/{view}/{display_id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\RearrangeFilter::getForm'
   requirements:
@@ -178,8 +216,10 @@ views_ui.form.rearrangeFilter:
 views_ui.form.display:
   pattern: '/admin/structure/views/{js}/display/{view}/{display_id}/{type}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Display::getForm'
   requirements:
@@ -189,8 +229,10 @@ views_ui.form.display:
 views_ui.form.configItem:
   pattern: '/admin/structure/views/{js}/config-item/{view}/{display_id}/{type}/{id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItem::getForm'
   requirements:
@@ -200,8 +242,10 @@ views_ui.form.configItem:
 views_ui.form.configItemExtra:
   pattern: '/admin/structure/views/{js}/config-item-extra/{view}/{display_id}/{type}/{id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItemExtra::getForm'
   requirements:
@@ -211,8 +255,10 @@ views_ui.form.configItemExtra:
 views_ui.form.configItemGroup:
   pattern: '/admin/structure/views/{js}/config-item-group/{view}/{display_id}/{type}/{id}'
   options:
-    tempstore:
-      view: 'views'
+    parameters:
+      view:
+        entity: 'view'
+        tempstore: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItemGroup::getForm'
     form_state: NULL
