diff --git a/core/core.services.yml b/core/core.services.yml
index 640440b..d00b9d2 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -208,7 +208,27 @@ services:
     class: Drupal\Core\TypedData\TypedDataManager
     arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
     calls:
+      - [setContainer, ['@service_container']]
       - [setValidationConstraintManager, ['@validation.constraint']]
+  typed_data_subscriber:
+    class: Drupal\Core\EventSubscriber\TypedDataSubscriber
+    arguments: ['@typed_data_resolver_manager']
+    tags:
+      - { name: event_subscriber }
+  typed_data_resolver_manager:
+    class: Drupal\Core\TypedData\Resolver\ResolverManager
+  typed_data_resolver.reflection:
+    class: Drupal\Core\TypedData\Resolver\ReflectionResolver
+    arguments: ['@typed_data']
+    calls:
+      - [setContainer, ['@service_container']]
+    tags:
+      - { name: typed_data_resolver }
+  typed_data_resolver.entity:
+    class: Drupal\Core\TypedData\Resolver\EntityResolver
+    arguments: ['@typed_data']
+    tags:
+      - { name: typed_data_resolver }
   validation.constraint:
     class: Drupal\Core\Validation\ConstraintManager
     arguments: ['@container.namespaces', '@cache.cache', '@language_manager', '@module_handler']
@@ -292,13 +312,20 @@ services:
       - { name: route_filter }
   paramconverter_manager:
     class: Drupal\Core\ParamConverter\ParamConverterManager
+    calls:
+      - [setContainer, ['@service_container']]
     tags:
       - { name: route_enhancer }
-  paramconverter.entity:
-    class: Drupal\Core\ParamConverter\EntityConverter
+  paramconverter_subscriber:
+    class: Drupal\Core\EventSubscriber\ParamConverterSubscriber
     tags:
-      - { name: paramconverter }
-    arguments: ['@plugin.manager.entity']
+      - { name: event_subscriber }
+    arguments: ['@paramconverter_manager']
+  paramconverter.typeddata:
+    class: Drupal\Core\TypedData\TypedDataConverter
+    tags:
+      - { name: paramconverter, priority: -10 }
+    arguments: ['@typed_data']
   reverse_proxy_subscriber:
     class: Drupal\Core\EventSubscriber\ReverseProxySubscriber
     tags:
diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php
index 385f198..06a449a 100644
--- a/core/lib/Drupal/Core/Controller/ControllerResolver.php
+++ b/core/lib/Drupal/Core/Controller/ControllerResolver.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Controller;
 
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver;
 use Symfony\Component\HttpKernel\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index c3b6994..ae27686 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Cache\ListCacheBinsPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterTypedDataResolversPass;
 use Drupal\Core\DependencyInjection\ServiceProviderInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
@@ -57,7 +58,8 @@ public function register(ContainerBuilder $container) {
     // Add a compiler pass for registering event subscribers.
     $container->addCompilerPass(new RegisterKernelListenersPass(), PassConfig::TYPE_AFTER_REMOVING);
     $container->addCompilerPass(new RegisterAccessChecksPass());
-    // Add a compiler pass for upcasting of entity route parameters.
+    // Add a compiler pass for upcasting route parameters.
+    $container->addCompilerPass(new RegisterTypedDataResolversPass());
     $container->addCompilerPass(new RegisterParamConvertersPass());
     $container->addCompilerPass(new RegisterRouteEnhancersPass());
     // Add a compiler pass for registering services needing destruction.
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
index 6fe1447..1948727 100644
--- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterParamConvertersPass.php
@@ -23,26 +23,14 @@ class RegisterParamConvertersPass implements CompilerPassInterface {
    *   The container to process.
    */
   public function process(ContainerBuilder $container) {
-
     if (!$container->hasDefinition('paramconverter_manager')) {
       return;
     }
 
     $manager = $container->getDefinition('paramconverter_manager');
-
-    $services = array();
     foreach ($container->findTaggedServiceIds('paramconverter') as $id => $attributes) {
       $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
-
-      $services[$priority][] = new Reference($id);
-    }
-
-    krsort($services);
-
-    foreach ($services as $priority) {
-      foreach ($priority as $service) {
-        $manager->addMethodCall('addConverter', array($service));
-      }
+      $manager->addMethodCall('addConverter', array($id, $priority));
     }
   }
 }
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTypedDataResolversPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTypedDataResolversPass.php
new file mode 100644
index 0000000..8939648
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterTypedDataResolversPass.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\DependencyInjection\Compiler\RegisterTypedDataResolversPass.
+ */
+
+namespace Drupal\Core\DependencyInjection\Compiler;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Registers typed data resolver services with the typed data resolver manager.
+ */
+class RegisterTypedDataResolversPass implements CompilerPassInterface {
+
+  /**
+   * Register typed data resolver services with the typed data resolver manager.
+   *
+   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
+   *   The container to process.
+   */
+  public function process(ContainerBuilder $container) {
+    if (!$container->hasDefinition('typed_data_resolver_manager')) {
+      return;
+    }
+
+    $manager = $container->getDefinition('typed_data_resolver_manager');
+    foreach ($container->findTaggedServiceIds('typed_data_resolver') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addTypedDataResolver', array(new Reference($id), $priority));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 33a62ce..77f3975 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -13,6 +13,7 @@
 use Drupal\Core\TypedData\TypedDataInterface;
 use IteratorAggregate;
 use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Defines a base entity class.
@@ -77,6 +78,26 @@ public function __construct(array $values, $entity_type) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public static function load($id, array $definition, ContainerInterface $container) {
+    $entity_type = substr($definition['type'], strlen('entity:'));
+    $bundle = NULL;
+    if (strpos(':', $entity_type) !== FALSE) {
+      list($entity_type, $bundle) = explode($entity_type, 2);
+    }
+
+    if (!$storage = $container->get('plugin.manager.entity')->getStorageController($entity_type)) {
+      throw new \InvalidArgumentException(sprintf('Could not retrieve storage controller for the %s entity type', $entity_type));
+    }
+
+    $entity = $storage->load($id);
+    if ($entity && (!isset($bundle) || $entity->bundle() == $bundle)) {
+      return $entity;
+    }
+  }
+
+  /**
    * Implements \Drupal\Core\Entity\EntityInterface::id().
    */
   public function id() {
diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
index a3ffcb9..f0b0c92 100644
--- a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
+++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides backwards compatible (BC) access to entity fields.
@@ -68,6 +69,13 @@ function __construct(EntityNG $decorated, array &$definitions) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public static function load($id, array $definition, ContainerInterface $container) {
+    return Entity::load($id, $definition, $container);
+  }
+
+  /**
    * Overrides Entity::getNGEntity().
    */
   public function getNGEntity() {
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index e965b8e..c4a4488 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -10,6 +10,7 @@
 use Drupal\Core\TypedData\AccessibleInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\IdentifiableInterface;
+use Drupal\Core\TypedData\LoadableInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 
 /**
@@ -28,7 +29,7 @@
  * @see \Drupal\Core\TypedData\TypedDataManager
  * @see \Drupal\Core\Field\FieldInterface
  */
-interface EntityInterface extends IdentifiableInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface {
+interface EntityInterface extends IdentifiableInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface, LoadableInterface {
 
   /**
    * Returns the entity UUID (Universally Unique Identifier).
diff --git a/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
new file mode 100644
index 0000000..f9a0a69
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/ParamConverterSubscriber.php
@@ -0,0 +1,55 @@
+<?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 parameter converter manager that will be responsible for upcasting
+   *   request attributes.
+   */
+  public function __construct(ParamConverterManager $param_converter_manager) {
+    $this->paramConverterManager = $param_converter_manager;
+  }
+
+  /**
+   * Applies parameter converters to route parameters.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The event to process.
+   */
+  public function onRoutingRouteAlterSetParameterConverters(RouteBuildEvent $event) {
+    $this->paramConverterManager->setRouteParameterConverters($event->getRouteCollection());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  static function getSubscribedEvents() {
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetParameterConverters', 10);
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/EventSubscriber/TypedDataSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/TypedDataSubscriber.php
new file mode 100644
index 0000000..9b05d6b
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/TypedDataSubscriber.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\EventSubscriber\TypedDataSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\TypedData\Resolver\ResolverManager;
+use Drupal\Core\TypedData\TypedDataDetector;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\Core\Routing\RouteBuildEvent;
+
+/**
+ * Typed data subscriber for route build events.
+ */
+class TypedDataSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The typed data resolver.
+   *
+   * @var \Drupal\Core\TypedData\Resolver\ResolverManager
+   */
+  protected $resolverManager;
+
+  /**
+   * Constructs a TypedDataSubscriber object.
+   */
+  public function __construct(ResolverManager $resolver_manager) {
+    $this->resolverManager = $resolver_manager;
+  }
+
+  /**
+   * Generates typed data definition for route arguments.
+   *
+   * @param \Drupal\Core\Routing\RouteBuildEvent $event
+   *   The event to process.
+   */
+  public function onRoutingRouteAlterSetAccessCheck(RouteBuildEvent $event) {
+    $this->resolverManager->resolveParameterTypes($event->getRouteCollection());
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    // Setting very low priority to ensure typed data discovery runs after
+    $events[RoutingEvents::ALTER][] = array('onRoutingRouteAlterSetAccessCheck', 20);
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
deleted file mode 100644
index 50b7d7c..0000000
--- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ /dev/null
@@ -1,103 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\Core\ParamConverter\EntityConverter.
- */
-
-namespace Drupal\Core\ParamConverter;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Routing\Route;
-use Drupal\Core\Entity\EntityManager;
-
-/**
- * This class allows the upcasting of entity ids to the respective entity
- * object.
- */
-class EntityConverter implements ParamConverterInterface {
-
-  /**
-   * Entity manager which performs the upcasting in the end.
-   *
-   * @var \Drupal\Core\Entity\EntityManager
-   */
-  protected $entityManager;
-
-  /**
-   * Constructs a new EntityConverter.
-   *
-   * @param \Drupal\Core\Entity\EntityManager $entityManager
-   *   The entity manager.
-   */
-  public function __construct(EntityManager $entityManager) {
-    $this->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.
-   */
-  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);
-        $entity = $storageController->load($value);
-        $variables[$name] = $entity;
-
-        // Mark this variable as converted.
-        $converted[] = $name;
-      }
-    }
-  }
-}
diff --git a/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
index 74307c2..c1265fc 100644
--- a/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\ParamConverter;
 
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Route;
 
 /**
@@ -17,13 +18,34 @@
   /**
    * Allows to convert variables to their corresponding objects.
    *
-   * @param array &$variables
-   *   Array of values to convert to their corresponding objects, if applicable.
+   * @param mixed $definition
+   *   The parameter definition provided in the route options.
+   * @param string $name
+   *   The name of the parameter.
+   * @param array $defaults
+   *   The route defaults array.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   *
+   * @return mixed|null
+   *   The converted parameter value.
+   */
+  public function convert($definition, $name, array $defaults, Request $request);
+
+  /**
+   * Determines if the converter applies to a specific route and variable.
+   *
+   * @param mixed $definition
+   *   The parameter definition provided in the route options.
+   * @param string $name
+   *   The name of the parameter.
    * @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.
+   *   The route to consider attaching to.
+   *
+   * @return bool
+   *   TRUE if the converter applies to the passed route and parameter, FALSE
+   *   otherwise.
    */
-  public function process(array &$variables, Route $route, array &$converted);
+  public function applies($definition, $name, Route $route);
+
 }
diff --git a/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
index be5676e..5d5b0b4 100644
--- a/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
+++ b/core/lib/Drupal/Core/ParamConverter/ParamConverterManager.php
@@ -7,14 +7,13 @@
 
 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\DependencyInjection\ContainerAware;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+use Symfony\Component\Routing\RouteCollection;
 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,167 @@
  * 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 {
+
+  /**
+   * An array of registered converter service ids.
+   *
+   * @var array
+   */
+  protected $converterIds = array();
+
+  /**
+   * Array of registered converter service ids sorted by their priority.
+   *
+   * @var array
+   */
+  protected $sortedConverterIds;
 
   /**
-   * Converters managed by the ParamConverterManager.
+   * Array of loaded converter services keyed by their ids.
    *
    * @var array
    */
-  protected $converters;
+  protected $converters = array();
 
   /**
-   * Adds a converter to the paramconverter service.
+   * Registers a parameter converter with the manager.
    *
-   * @see \Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass
+   * @param string $converter
+   *   The parameter converter service id to register.
+   * @param int $priority
+   *   (optional) The priority of the converter. Defaults to 0.
    *
-   * @param \Drupal\Core\ParamConverter\ParamConverterInterface $converter
-   *   The converter to add.
+   * @return \Drupal\Core\ParamConverter\ParamConverterManager
+   *   The called object for chaining.
    */
-  public function addConverter(ParamConverterInterface $converter) {
-    $this->converters[] = $converter;
+  public function addConverter($converter, $priority = 0) {
+    $converter = substr($converter, strlen('paramconverter.'));
+    if (empty($this->converterIds[$priority])) {
+      $this->converterIds[$priority] = array();
+    }
+    $this->converterIds[$priority][] = $converter;
+    unset($this->sortedConverterIds);
     return $this;
   }
 
   /**
-   * Implements \Symfony\Cmf\Component\Routing\Enhancer\ŖouteEnhancerIterface.
+   * Sorts the converter service ids and flattens them.
    *
-   * Iterates over all registered converters and allows them to alter the
-   * defaults.
+   * @return array
+   *   The sorted parameter converter service ids.
+   */
+  public function getConverterIds() {
+    if (!isset($this->sortedConverterIds)) {
+      krsort($this->converterIds);
+      $this->sortedConverterIds = array();
+      foreach ($this->converterIds as $resolvers) {
+        $this->sortedConverterIds = array_merge($this->sortedConverterIds, $resolvers);
+      }
+    }
+    return $this->sortedConverterIds;
+  }
+
+  /**
+   * For each route, saves a list of applicable converters to the route.
+   *
+   * @param \Symfony\Component\Routing\RouteCollection $routes
+   *   A collection of routes to apply converters to.
+   */
+  public function setRouteParameterConverters(RouteCollection $routes) {
+    foreach ($routes->all() as $route) {
+      if (!$parameters = $route->getOption('parameters')) {
+        // Continue with the next route if no parameters have been defined.
+        continue;
+      }
+
+      // Loop over all defined parameters and look up the right converter.
+      foreach ($parameters as $name => &$definition) {
+        if (isset($definition['converter'])) {
+          // Skip parameters that already have a manually set converter.
+          continue;
+        }
+
+        foreach ($this->getConverterIds() as $converter) {
+          if ($this->getConverter($converter)->applies($definition, $name, $route)) {
+            $definition['converter'] = $converter;
+            break;
+          }
+        }
+      }
+
+      // Override the parameters array.
+      $route->setOption('parameters', $parameters);
+    }
+  }
+
+  /**
+   * Invokes the registered converter for each defined parameter on a route.
    *
    * @param array $defaults
-   *   The getRouteDefaults array.
+   *   The route defaults array.
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   The current request.
    *
+   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+   *   If one of the assigned converters returned NULL because the given
+   *   variable could not be converted.
+   *
    * @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];
 
-    foreach ($this->converters as $converter) {
-      $converter->process($defaults, $route, $converters);
+    // Skip this enhancer if there are no parameter definitions.
+    if (!$parameters = $route->getOption('parameters') ?: array()) {
+      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) {
+    // Invoke the registered converter for each parameter.
+    foreach ($parameters as $parameter => $definition) {
+      if (array_key_exists($parameter, $defaults) && $defaults[$parameter] === NULL) {
+        // Do not try to convert anything that is already set to NULL.
+        continue;
+      }
+
+      if (!isset($definition['converter'])) {
+        // Continue if no converter has been specified.
+        continue;
+      }
+
+      // If a converter returns NULL it means that the parameter could not be
+      // converted in which case we throw a 404.
+      $defaults[$parameter] = $this->getConverter($definition['converter'])->convert($definition, $parameter, $defaults, $request);
+      if (!isset($defaults[$parameter])) {
         throw new NotFoundHttpException();
       }
     }
 
     return $defaults;
   }
+
+  /**
+   * Lazy-loads converter services.
+   *
+   * @param string $converter
+   *   The service id of converter service to load.
+   *
+   * @return \Drupal\Core\ParamConverter\ParamConverterInterface
+   *   The loaded converter service identified by the given service id.
+   *
+   * @throws \InvalidArgumentException
+   *   If the given service id is not a registered converter.
+   */
+  protected function getConverter($converter) {
+    if (isset($this->converters[$converter])) {
+      return $this->converters[$converter];
+    }
+    if (!in_array($converter, $this->getConverterIds())) {
+      throw new \InvalidArgumentException(sprintf('No converter has been registered for %s', $converter));
+    }
+    return $this->converters[$converter] = $this->container->get("paramconverter.$converter");
+  }
+
 }
+
diff --git a/core/lib/Drupal/Core/TypedData/LoadableInterface.php b/core/lib/Drupal/Core/TypedData/LoadableInterface.php
new file mode 100644
index 0000000..12eeaea
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/LoadableInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\LoadableInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Interface for loadable typed data objects.
+ */
+interface LoadableInterface {
+
+  /**
+   * Loads a typed data object.
+   *
+   * @param mixed $id
+   *   The unique identifier of the typed data object to be loaded.
+   * @param array $definition
+   *   The typed data definition.
+   * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
+   *   The service container this object should use.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface|null
+   *   The loaded typed data object or NULL if it could not be loaded.
+   */
+  public static function load($id, array $definition, ContainerInterface $container);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Resolver/EntityResolver.php b/core/lib/Drupal/Core/TypedData/Resolver/EntityResolver.php
new file mode 100644
index 0000000..eb72e37
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Resolver/EntityResolver.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\TypedData\Resolver\EntityResolver.
+ */
+
+namespace Drupal\Core\TypedData\Resolver;
+
+use Drupal\Core\TypedData\TypedDataManager;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Generates typed data definitions for specific entity routes.
+ */
+class EntityResolver implements ResolverInterface {
+
+  /**
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager;
+   */
+  protected $typedDataManager;
+
+  /**
+   * Constructs a EntityResolver object.
+   *
+   * @param TypedDataManager $typed_data_manager
+   *   The typed data manager to use.
+   */
+  public function __construct(TypedDataManager $typed_data_manager) {
+    $this->typedDataManager = $typed_data_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resolveParameterTypes(Route $route) {
+    $defaults = $route->getDefaults();
+    if (!empty($defaults['_controller'])) {
+      return;
+    }
+
+    // Either the '_entity_form' or '_entity_list' have to be set.
+    if (empty($defaults['_entity_form']) && empty($defaults['_entity_list'])) {
+      return;
+    }
+
+    // This typed data resolver is only capable of generating typed data
+    // definitions for routes that have either '_entity_form' or '_entity_list'
+    // in the route defaults array.
+    $entity_type = NULL;
+    if (!empty($defaults['_entity_form'])) {
+      list ($entity_type) = explode('.', $defaults['_entity_form']);
+    }
+    elseif (!empty($defaults['_entity_list'])) {
+      $entity_type = $defaults['_entity_list'];
+    }
+
+    // Check that the given entity type is not either a variable or has a
+    // default value set for it.
+    if (!in_array($entity_type, $route->compile()->getVariables()) && !isset($defaults[$entity_type])) {
+      return;
+    }
+
+    // Return the typed data definition for the given entity type if it exists
+    // and the parameter has not been defined manually already.
+    $predefined = $route->getOption('parameters') ?: array();
+    if (!isset($predefined[$entity_type]) && $this->typedDataManager->getDefinition("entity:$entity_type")) {
+      return array($entity_type => array(
+        'type' => "entity:$entity_type",
+      ));
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Resolver/ReflectionResolver.php b/core/lib/Drupal/Core/TypedData/Resolver/ReflectionResolver.php
new file mode 100644
index 0000000..801b0df
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Resolver/ReflectionResolver.php
@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\TypedData\Resolver\ReflectionResolver.
+ */
+
+namespace Drupal\Core\TypedData\Resolver;
+
+use Drupal\Core\TypedData\TypedDataManager;
+use Symfony\Component\DependencyInjection\ContainerAware;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Generates typed data definitions based on a controller's type hints.
+ */
+class ReflectionResolver extends ContainerAware implements ResolverInterface {
+
+  /**
+   * A static cache of type hinted typed data types.
+   *
+   * Stores a keyed list of typed data types with the key being the class or
+   * interface type hint and the value being the typed data type that belongs to
+   * that class or interface.
+   *
+   * @var array
+   */
+  protected $cache = array();
+
+  /**
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager;
+   */
+  protected $typedDataManager;
+
+  /**
+   * Constructs a ReflectionResolver object.
+   *
+   * @param TypedDataManager $typed_data_manager
+   *   The typed data manager to use.
+   */
+  public function __construct(TypedDataManager $typed_data_manager) {
+    $this->typedDataManager = $typed_data_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resolveParameterTypes(Route $route) {
+    $defaults = $route->getDefaults();
+    $predefined = $route->getOption('parameters') ?: array();
+    $variables = $route->compile()->getVariables();
+
+    // Resolve type hints from the '_form' controller, if set.
+    $controllers = array();
+    if (isset($defaults['_form'])) {
+      $controllers['_form'] = $this->getFormController($defaults['_form']);
+    }
+
+    // Resolve type hints from '_content' or '_controller' controllers.
+    foreach (array('_content', '_controller') as $controller) {
+      if (isset($defaults[$controller]) && $callable = $this->getController($defaults[$controller])) {
+        $controllers[$controller] = $callable;
+      }
+    }
+
+    // Retrieve the typed data types of as many parameters as possible by
+    // reflecting on the type hints on a controller. However, for performance
+    // reasons, make sure we do not try to reflect the same parameter twice.
+    $definitions = $predefined;
+    foreach ($controllers as $callable) {
+      if ($parameters = $this->getParameterTypes($callable, $variables, $definitions)) {
+        $definitions = array_merge($parameters, $definitions);
+      }
+    }
+
+    return array_diff_key($definitions, $predefined);
+  }
+
+  /**
+   * Returns a callable for the given form controller.
+   *
+   * @param string $controller
+   *   The controller string from the route defaults.
+   *
+   * @return array
+   *   A callable, composed of the controller class and method.
+   */
+  protected function getFormController($controller) {
+    // If the class does not exist, the form controller is a service.
+    if (!class_exists($controller)) {
+      $controller = $this->container->get($controller);
+    }
+    return array($controller, 'buildForm');
+  }
+
+  /**
+   * Returns a callable for the given controller.
+   *
+   * @param string $controller
+   *   The controller string from the route defaults.
+   *
+   * @throws \InvalidArgumentException
+   *   If the resolved class name is not a valid class.
+   *
+   * @return array|null
+   *   A callable, composed of the controller class and method or NULL if the
+   *   given string could not be turned to a valid callable.
+   */
+  protected function getController($controller) {
+    // Controller in the service:method notation.
+    $count = substr_count($controller, ':');
+    if ($count == 1) {
+      list($service, $method) = explode(':', $controller, 2);
+      return array($this->container->get($service), $method);
+    }
+
+    // Controller in the class::method notation.
+    if (strpos($controller, '::') !== FALSE) {
+      list($class, $method) = explode('::', $controller, 2);
+      if (!class_exists($class)) {
+        throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class));
+      }
+      return array($class, $method);
+    }
+  }
+
+  /**
+   * Tries to determine the typed data types via the type hints on a method.
+   *
+   * @param array $callable
+   *   The name of a method on the given class.
+   * @param array $variables
+   *   The route variables array for the current route.
+   * @param array $predefined
+   *   A keyed array of pre-defined parameter definitions.
+   *
+   * @return array
+   *   An array of typed data definitions keyed by parameter name.
+   */
+  protected function getParameterTypes($callable, array $variables, array $predefined) {
+    $reflection = new \ReflectionMethod($callable[0], $callable[1]);
+    $definitions = array();
+    foreach ($reflection->getParameters() as $parameter) {
+      $name = $parameter->getName();
+      // Do not try to resolve parameters that have been manually set.
+      if (isset($predefined[$name]) || isset($definitions[$name])) {
+        continue;
+      }
+
+      // Do not resolve parameters that are not part of the route variables and
+      // instead provide a default value.
+      if (!in_array($name, $variables) && $parameter->isDefaultValueAvailable()) {
+        continue;
+      }
+
+      if ($type = $this->getParameterType($parameter)) {
+        $definitions[$name] = array(
+          'type' => $type,
+        );
+      }
+    }
+    return $definitions;
+  }
+
+  /**
+   * Tries to determine the typed data type of a given parameter.
+   *
+   * @param \ReflectionParameter $parameter
+   *   The parameter to generate a typed data definition for.
+   *
+   * @return array
+   *   The typed data definition for the given parameter.
+   */
+  protected function getParameterType(\ReflectionParameter $parameter) {
+    if (!$class = $parameter->getClass()) {
+      // The parameter does not have a proper type hint.
+      return;
+    }
+
+    $name = $class->getName();
+    if (isset($this->cache[$name])) {
+      return $this->cache[$name];
+    }
+
+    // Ignore request object type hints.
+    $request = 'Symfony\Component\HttpFoundation\Request';
+    if ($name == $request || $class->isSubclassOf($request)) {
+      return;
+    }
+
+    // Try to find a typed data type that matches the type hint.
+    $this->cache[$name] = $class->isInterface() ? $this->getParameterTypeFromInterface($name) : $this->getParameterTypeFromClass($name);
+
+    return $this->cache[$name];
+  }
+
+  /**
+   * Tries to determine the typed data type that matches a given interface.
+   *
+   * @param string $name
+   *   An interface name for which to find the matching typed data type.
+   *
+   * @return array|null
+   *   The typed data definition that matches the given interface or NULL if it
+   *   it is ambiguous or does not match any.
+   */
+  protected function getParameterTypeFromInterface($name) {
+    $match = NULL;
+    foreach ($this->typedDataManager->getDefinitions() as $type => $definition) {
+      // Check if the defined typed data class implements the given interface.
+      if (is_subclass_of($definition['class'], $name)) {
+        // Make sure that the type hint is not ambiguous.
+        if (isset($match)) {
+          return;
+        }
+
+        $match = $type;
+      }
+    }
+
+    // Return the matched typed data type if any.
+    return $match ?: NULL;
+  }
+
+  /**
+   * Tries to determine the typed data type that matches a given class.
+   *
+   * @param string $name
+   *   A class name for which to find the matching typed data type.
+   *
+   * @return array|null
+   *   The typed data definition that matches the given interface or NULL if it
+   *   did not match any.
+   */
+  protected function getParameterTypeFromClass($name) {
+    foreach ($this->typedDataManager->getDefinitions() as $type => $definition) {
+      if ($definition['class'] === $name) {
+        // Return the first match.
+        return $type;
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Resolver/ResolverInterface.php b/core/lib/Drupal/Core/TypedData/Resolver/ResolverInterface.php
new file mode 100644
index 0000000..9b63eb6
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Resolver/ResolverInterface.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Resolver\ResolverInterface.
+ */
+
+namespace Drupal\Core\TypedData\Resolver;
+
+use Symfony\Component\Routing\Route;
+
+/**
+ * Interface for typed data definition resolvers.
+ */
+interface ResolverInterface {
+
+  /**
+   * Tries to generate typed data definitions for the parameters of a route.
+   *
+   * @param Route $route
+   *   The route object for which to generate typed data definitions.
+   *
+   * @return array
+   *   An array of typed data definitions keyed by parameter name.
+   */
+  public function resolveParameterTypes(Route $route);
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Resolver/ResolverManager.php b/core/lib/Drupal/Core/TypedData/Resolver/ResolverManager.php
new file mode 100644
index 0000000..7860b39
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Resolver/ResolverManager.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\TypedData\Resolver\ResolverManager.
+ */
+
+namespace Drupal\Core\TypedData\Resolver;
+
+use Drupal\Core\TypedData\TypedDataManager;
+use ReflectionMethod;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Generates typed data definitions for route parameters.
+ *
+ * By defining the typed data type of a route argument, other systems (like
+ * page manager) get the chance to manipulate the behavior of a given route
+ * while being fully aware of the available contexts.
+ *
+ * Typed data resolver services can employ different strategies to automatically
+ * discover and return the typed data type of certain route parameters e.g.
+ * by reflecting the type hints on the assigned controller or by parsing other
+ * metadata in a route definition that clearly identifies the type of a given
+ * parameter.
+ *
+ * This greatly increases the developer experience when defining new routes as
+ * it removes the need to repeat the type definition in the route options even
+ * though it may already be obvious e.g. due to a type hint or a special,
+ * generic controller like '_entity_form' or '_entity_list'.
+ */
+class ResolverManager {
+
+  /**
+   * An array of registered typed data resolvers.
+   *
+   * @var array
+   */
+  protected $resolvers = array();
+
+  /**
+   * Array of registered typed resolvers sorted by their priority.
+   *
+   * @var array
+   */
+  protected $sortedResolvers;
+
+  /**
+   * Registers a typed data resolver with the manager.
+   *
+   * @param \Drupal\Core\TypedData\Resolver\ResolverInterface $resolver
+   *   The typed data resolver to register.
+   * @param int $priority
+   *   (optional) The priority of the typed data resolver. Defaults to 0.
+   *
+   * @return \Drupal\Core\TypedData\Resolver\ResolverManager
+   *   The called object for chaining.
+   */
+  public function addTypedDataResolver(ResolverInterface $resolver, $priority = 0) {
+    if (empty($this->resolvers[$priority])) {
+      $this->resolvers[$priority] = array();
+    }
+    $this->resolvers[$priority][] = $resolver;
+    unset($this->sortedResolvers);
+    return $this;
+  }
+
+  /**
+   * Sorts the resolvers and flattens them.
+   *
+   * @return \Drupal\Core\TypedData\Resolver\ResolverInterface[]
+   *   The sorted typed data resolvers.
+   */
+  public function getTypedDataResolvers() {
+    if (!isset($this->sortedResolvers)) {
+      krsort($this->resolvers);
+      $this->sortedResolvers = array();
+      foreach ($this->resolvers as $resolvers) {
+        $this->sortedResolvers = array_merge($this->sortedResolvers, $resolvers);
+      }
+    }
+    return $this->sortedResolvers;
+  }
+
+  /**
+   * Tries to generate typed data definitions for each route in a collection.
+   *
+   * @param RouteCollection $routes
+   *   A route collection.
+   */
+  public function resolveParameterTypes(RouteCollection $routes) {
+    $resolvers = $this->getTypedDataResolvers();
+    foreach ($routes->all() as $route) {
+      $definitions = array();
+      $before = $route->getOption('parameters') ?: array();
+      foreach ($resolvers as $resolver) {
+        if ($parameters = $resolver->resolveParameterTypes($route)) {
+          $definitions = array_merge($parameters, $definitions);
+        }
+      }
+
+      // Merge the resolved typed data definitions into the route options.
+      if (!empty($definitions)) {
+        // Do not override manually defined typed data definitions.
+        $definitions = array_merge($definitions, $before);
+        $route->setOption('parameters', $definitions);
+      }
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataConverter.php b/core/lib/Drupal/Core/TypedData/TypedDataConverter.php
new file mode 100644
index 0000000..83a5b5b
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/TypedDataConverter.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\ParamConverter\TypedDataConverter.
+ */
+
+namespace Drupal\Core\TypedData;
+
+use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Component\Utility\String;
+use Drupal\Core\ParamConverter\ParamConverterInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Upcasts request attributes to typed data objects.
+ */
+class TypedDataConverter implements ParamConverterInterface {
+
+  /**
+   * The typed data manager.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager
+   */
+  protected $typedDataManager;
+
+  /**
+   * Constructs a new TypedDataConverter object.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
+   *   The typed data manager.
+   */
+  public function __construct(TypedDataManager $typed_data_manager) {
+    $this->typedDataManager = $typed_data_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function convert($definition, $name, array $defaults, Request $request) {
+    // Only continue if there is a value for the given parameter.
+    if (!isset($defaults[$name])) {
+      return;
+    }
+
+    // Remove keys that do not belong to the typed data definition.
+    unset($definition['converter']);
+
+    // Allow per-data definition overrides of the used classes, i.e. take over
+    // classes specified in the data definition.
+    $original = $this->typedDataManager->getDefinition($definition['type']);
+    $key = empty($definition['list']) ? 'class' : 'list class';
+    if (isset($definition[$key])) {
+      $class = $definition[$key];
+    }
+    elseif (isset($original[$key])) {
+      $class = $original[$key];
+    }
+
+    if (!isset($class)) {
+      throw new PluginException(String::format('The plugin %plugin did not specify an instance class.', array('%plugin' => $definition['type'])));
+    }
+
+    // If the given typed data type is not loadable, just create an instance.
+    if (!is_subclass_of($class, 'Drupal\Core\TypedData\LoadableInterface')) {
+      return $this->typedDataManager->create($definition, $defaults[$name]);
+    }
+
+    // Try to load the typed data object via the typed data manager.
+    return $this->typedDataManager->load($defaults[$name], $definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies($definition, $name, Route $route) {
+    return !empty($definition['type']) && $this->typedDataManager->getDefinition($definition['type']);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 5c48fb0..e2425aa 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\TypedData;
 
 use Drupal\Component\Plugin\Exception\PluginException;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManager;
@@ -16,13 +17,15 @@
 use Drupal\Core\TypedData\Validation\MetadataFactory;
 use Drupal\Core\Validation\ConstraintManager;
 use Drupal\Core\Validation\DrupalTranslator;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\ValidatorInterface;
 use Symfony\Component\Validator\Validation;
 
 /**
  * Manages data type plugins.
  */
-class TypedDataManager extends DefaultPluginManager {
+class TypedDataManager extends DefaultPluginManager implements ContainerAwareInterface {
 
   /**
    * The validator used for validating typed data.
@@ -45,6 +48,13 @@ class TypedDataManager extends DefaultPluginManager {
    */
   protected $prototypes = array();
 
+  /**
+   * The container.
+   *
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface
+   */
+  protected $container;
+
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler) {
     $this->alterInfo($module_handler, 'data_type_info');
     $this->setCacheBackend($cache_backend, $language_manager, 'typed_data:types');
@@ -56,6 +66,13 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function setContainer(ContainerInterface $container = NULL) {
+    $this->container = $container;
+  }
+
+  /**
    * Implements \Drupal\Component\Plugin\PluginManagerInterface::createInstance().
    *
    * @param string $plugin_id
@@ -382,4 +399,48 @@ public function getConstraints($definition) {
     }
     return $constraints;
   }
+
+  /**
+   * Loads a typed data object by its id.
+   *
+   * @param $id
+   *   The unique identifier of the typed data object to be loaded.
+   * @param array $definition
+   *   The data definition array.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   *   If no instance class was given.
+   * @throws \InvalidArgumentException
+   *   If the given instance class does not implement the LoadableInterface.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface|null
+   *   The loaded typed data object or NULL if it could not be loaded.
+   */
+  public function load($id, array $definition) {
+    $original_definition = $this->discovery->getDefinition($definition['type']);
+    if (!isset($original_definition)) {
+      throw new \InvalidArgumentException(String::format('Invalid data type (%plugin) has been given.', array('%plugin' => $definition['type'])));
+    }
+
+    // Allow per-data definition overrides of the used classes, i.e. take over
+    // classes specified in the data definition.
+    $key = empty($definition['list']) ? 'class' : 'list class';
+    if (isset($definition[$key])) {
+      $class = $definition[$key];
+    }
+    elseif (isset($original_definition[$key])) {
+      $class = $original_definition[$key];
+    }
+
+    if (!isset($class)) {
+      throw new PluginException(String::format('The plugin %plugin did not specify an instance class.', array('%plugin' => $definition['type'])));
+    }
+
+    // The instance class has to implement the LoadableInterface.
+    if (!in_array('Drupal\Core\TypedData\LoadableInterface', class_implements($class))) {
+      throw new \InvalidArgumentException('The given class has to implement the LoadableInterface.');
+    }
+
+    return $class::load($id, $definition, $this->container);
+  }
 }
diff --git a/core/modules/image/lib/Drupal/image/Form/ImageEffectDeleteForm.php b/core/modules/image/lib/Drupal/image/Form/ImageEffectDeleteForm.php
index 50bf2bb..ba6367d 100644
--- a/core/modules/image/lib/Drupal/image/Form/ImageEffectDeleteForm.php
+++ b/core/modules/image/lib/Drupal/image/Form/ImageEffectDeleteForm.php
@@ -8,6 +8,7 @@
 namespace Drupal\image\Form;
 
 use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\image\ImageStyleInterface;
 use Drupal\image\Plugin\Core\Entity\ImageStyle;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -61,7 +62,7 @@ public function getFormID() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, array &$form_state, $image_style = NULL, $image_effect = NULL, Request $request = NULL) {
+  public function buildForm(array $form, array &$form_state, ImageStyleInterface $image_style = NULL, $image_effect = NULL, Request $request = NULL) {
     $this->imageStyle = $image_style;
     $this->imageEffect = image_effect_load($image_effect, $this->imageStyle->id());
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php b/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
deleted file mode 100644
index d56ee5f..0000000
--- a/core/modules/system/lib/Drupal/system/Tests/ParamConverter/UpcastingTest.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\system\Tests\ParamConverter\UpcastingTest.
- */
-
-namespace Drupal\system\Tests\ParamConverter;
-
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Web tests for the upcasting.
- */
-class UpcastingTest extends WebTestBase {
-
-  /**
-   * Implement getInfo().
-   */
-  public static function getInfo() {
-    return array(
-      'name' => 'Upcasting tests',
-      'description' => 'Tests upcasting of url arguments to entities.',
-      'group' => 'ParamConverter',
-    );
-  }
-
-  public static $modules = array('paramconverter_test');
-
-  /**
-   * Confirms that all parameters are converted as expected.
-   *
-   * All of these requests end up being proccessed by a controller with this
-   * the signature: f($user, $node, $foo) returning either values or labels
-   * like "user: Dries, node: First post, foo: bar"
-   *
-   * The tests shuffle the parameters around an checks if the right thing is
-   * happening.
-   */
-  public function testUpcasting() {
-    $node = $this->drupalCreateNode(array('title' => $this->randomName(8)));
-    $user = $this->drupalCreateUser(array('access content'));
-    $foo = 'bar';
-
-    // paramconverter_test/test_user_node_foo/{user}/{node}/{foo}
-    $this->drupalGet("paramconverter_test/test_user_node_foo/"  . $user->id() . "/{$node->nid}/$foo");
-    $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'
-    $this->drupalGet("paramconverter_test/test_node_user_user/{$node->nid}/" . $user->id() . "/" . $user->id());
-    $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'
-    $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)');
-  }
-
-  /**
-   * Confirms we can upcast to controller arguments of the same type.
-   */
-  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'
-    $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/lib/Drupal/system/Tests/TypedData/TypedDataTest.php b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
index 3558d20..4b76fdc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -614,4 +614,21 @@ public function testTypedDataValidation() {
     $this->assertEqual($violations[0]->getInvalidValue(), 'string');
     $this->assertIdentical($violations[0]->getPropertyPath(), '0.value');
   }
+
+  /**
+   * Tests loading of typed data objects that implement LoadableInterface.
+   */
+  public function testTypedDataLoading() {
+    // Generate a file for testing typed data loading.
+    file_unmanaged_copy(DRUPAL_ROOT . '/core/misc/druplicon.png', 'public://example.png');
+    $image = entity_create('file', array('uri' => 'public://example.png'));
+    $image->save();
+
+    $loaded = \Drupal::typedData()->load($image->id(), array(
+      'type' => 'entity:file',
+    ));
+
+    $this->assertNotIdentical(FALSE, $loaded, 'Successfully loaded the image entity.');
+    $this->assertIdentical($image->uuid(), $loaded->uuid(), 'Successfully loaded the right image entity..');
+  }
 }
diff --git a/core/modules/system/tests/modules/paramconverter_test/lib/Drupal/paramconverter_test/TestControllers.php b/core/modules/system/tests/modules/paramconverter_test/lib/Drupal/paramconverter_test/TestControllers.php
deleted file mode 100644
index ca95200..0000000
--- a/core/modules/system/tests/modules/paramconverter_test/lib/Drupal/paramconverter_test/TestControllers.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\paramconverter_test\TestControllers.
- */
-
-namespace Drupal\paramconverter_test;
-
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * Controller routine for testing the paramconverter.
- */
-class TestControllers {
-
-  public function testUserNodeFoo($user, $node, $foo) {
-    $retval = "user: " . (is_object($user) ? $user->label() : $user);
-    $retval .= ", node: " . (is_object($node) ? $node->label() : $node);
-    $retval .= ", foo: " . (is_object($foo) ? $foo->label() : $foo);
-    return $retval;
-  }
-
-  public function testNodeSetParent(EntityInterface $node, EntityInterface $parent) {
-    return "Setting '{$parent->title}' as parent of '{$node->title}'.";
-  }
-}
diff --git a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml
deleted file mode 100644
index 4aa2455..0000000
--- a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.info.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-name: "ParamConverter test"
-type: module
-description: "Support module for paramconverter testing."
-package: Testing
-version: VERSION
-core: 8.x
-hidden: TRUE
diff --git a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.module b/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.module
deleted file mode 100644
index c37a9e2..0000000
--- a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.module
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-/**
- * @file
- * Intentionally blank file.
- */
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
deleted file mode 100644
index 9d226e4..0000000
--- a/core/modules/system/tests/modules/paramconverter_test/paramconverter_test.routing.yml
+++ /dev/null
@@ -1,36 +0,0 @@
-paramconverter_test_user_node_foo:
-  pattern: '/paramconverter_test/test_user_node_foo/{user}/{node}/{foo}'
-  defaults:
-    _content: '\Drupal\paramconverter_test\TestControllers::testUserNodeFoo'
-  requirements:
-    _access: 'TRUE'
-
-paramconverter_test_node_user_user:
-  pattern: '/paramconverter_test/test_node_user_user/{node}/{foo}/{user}'
-  defaults:
-    _content: '\Drupal\paramconverter_test\TestControllers::testUserNodeFoo'
-  requirements:
-    _access: 'TRUE'
-  options:
-    converters:
-      foo: 'user'
-
-paramconverter_test_node_node_foo:
-  pattern: '/paramconverter_test/test_node_node_foo/{user}/{node}/{foo}'
-  defaults:
-    _content: '\Drupal\paramconverter_test\TestControllers::testUserNodeFoo'
-  requirements:
-    _access: 'TRUE'
-  options:
-    converters:
-      user: 'node'
-
-paramconverter_test_node_set_parent:
-  pattern: '/paramconverter_test/node/{node}/set/parent/{parent}'
-  requirements:
-    _access: 'TRUE'
-  defaults:
-    _content: '\Drupal\paramconverter_test\TestControllers::testNodeSetParent'
-  options:
-    converters:
-      parent: '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
deleted file mode 100644
index f828de1..0000000
--- a/core/modules/views_ui/lib/Drupal/views_ui/ParamConverter/ViewUIConverter.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\views_ui\ParamConverter\ViewUIConverter.
- */
-
-namespace Drupal\views_ui\ParamConverter;
-
-use Symfony\Component\Routing\Route;
-use Drupal\Core\ParamConverter\ParamConverterInterface;
-use Drupal\user\TempStoreFactory;
-use Drupal\views\ViewStorageInterface;
-use Drupal\views_ui\ViewUI;
-
-/**
- * Provides upcasting for a view entity to be used in the Views UI.
- */
-class ViewUIConverter implements ParamConverterInterface {
-
-  /**
-   * Stores the tempstore factory.
-   *
-   * @var \Drupal\user\TempStoreFactory
-   */
-  protected $tempStoreFactory;
-
-  /**
-   * Constructs a new ViewUIConverter.
-   *
-   * @param \Drupal\user\TempStoreFactory $temp_store_factory
-   *   The factory for the temp store object.
-   */
-  public function __construct(TempStoreFactory $temp_store_factory) {
-    $this->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.
-   */
-  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]);
-        }
-
-        // Store the new view and mark this variable as converted.
-        $variables[$name] = $view;
-        $converted[] = $name;
-      }
-    }
-  }
-
-}
diff --git a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
index e3538e6..63cd912 100644
--- a/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\views_ui;
 
+use Drupal\Core\Entity\Entity;
 use Drupal\views\Views;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
 use Drupal\views\ViewExecutable;
@@ -16,6 +17,7 @@
 use Drupal\views\Plugin\views\query\Sql;
 use Drupal\views\Plugin\Core\Entity\View;
 use Drupal\views\ViewStorageInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Stores UI related temporary settings.
@@ -1243,4 +1245,34 @@ public function mergeDefaultDisplaysOptions() {
   public function uriRelationships() {
     return $this->storage->uriRelationships();
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function load($id, array $definition, ContainerInterface $container) {
+    $storage = $container->get('plugin.manager.entity')->getStorageController('view');
+    if (!$original = $storage->load($id)) {
+      return;
+    }
+
+    // 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.
+    $store = $container->get('user.tempstore')->get('views');
+    if ($view = $store->get($id)) {
+      if ($original->status()) {
+        $view->enable();
+      }
+      else {
+        $view->disable();
+      }
+      $view->lock = $store->getMetadata($id);
+    }
+    // Otherwise, decorate the existing view for use in the UI.
+    else {
+      $view = new static($original);
+    }
+
+    return $view;
+  }
 }
diff --git a/core/modules/views_ui/views_ui.routing.yml b/core/modules/views_ui/views_ui.routing.yml
index 0d44630..64ac85b 100644
--- a/core/modules/views_ui/views_ui.routing.yml
+++ b/core/modules/views_ui/views_ui.routing.yml
@@ -71,35 +71,41 @@ views_ui.autocomplete:
 
 views_ui.edit:
   pattern: '/admin/structure/views/view/{view}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Controller\ViewsUIController::edit'
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.edit.display:
   pattern: '/admin/structure/views/view/{view}/edit/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Controller\ViewsUIController::edit'
     display_id: NULL
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.preview:
   pattern: '/admin/structure/views/view/{view}/preview/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _entity_form: 'view.preview'
     display_id: NULL
   requirements:
     _permission: 'administer views'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.breakLock:
   pattern: '/admin/structure/views/view/{view}/break-lock'
@@ -110,111 +116,131 @@ views_ui.breakLock:
 
 views_ui.form.addItem:
   pattern: '/admin/structure/views/{js}/add-item/{view}/{display_id}/{type}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\AddItem::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.editDetails:
   pattern: '/admin/structure/views/{js}/edit-details/{view}/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\EditDetails::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.reorderDisplays:
   pattern: '/admin/structure/views/{js}/reorder-displays/{view}/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ReorderDisplays::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.analyze:
   pattern: '/admin/structure/views/{js}/analyze/{view}/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Analyze::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.rearrange:
   pattern: '/admin/structure/views/{js}/rearrange/{view}/{display_id}/{type}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Rearrange::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.rearrangeFilter:
   pattern: '/admin/structure/views/{js}/rearrange-filter/{view}/{display_id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\RearrangeFilter::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.display:
   pattern: '/admin/structure/views/{js}/display/{view}/{display_id}/{type}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\Display::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.configItem:
   pattern: '/admin/structure/views/{js}/config-item/{view}/{display_id}/{type}/{id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItem::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.configItemExtra:
   pattern: '/admin/structure/views/{js}/config-item-extra/{view}/{display_id}/{type}/{id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItemExtra::getForm'
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
 
 views_ui.form.configItemGroup:
   pattern: '/admin/structure/views/{js}/config-item-group/{view}/{display_id}/{type}/{id}'
-  options:
-    tempstore:
-      view: 'views'
   defaults:
     _controller: '\Drupal\views_ui\Form\Ajax\ConfigItemGroup::getForm'
     form_state: NULL
   requirements:
     _permission: 'administer views'
     js: 'nojs|ajax'
+  options:
+    parameters:
+      view:
+        type: 'entity:view'
+        class: '\Drupal\views_ui\ViewUI'
diff --git a/core/modules/views_ui/views_ui.services.yml b/core/modules/views_ui/views_ui.services.yml
deleted file mode 100644
index 34b2740..0000000
--- a/core/modules/views_ui/views_ui.services.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-services:
-  paramconverter.views_ui:
-    class: Drupal\views_ui\ParamConverter\ViewUIConverter
-    arguments: ['@user.tempstore']
-    tags:
-      - { name: paramconverter }
diff --git a/core/tests/Drupal/Tests/Core/TypedData/EntityResolverTest.php b/core/tests/Drupal/Tests/Core/TypedData/EntityResolverTest.php
new file mode 100644
index 0000000..91e76e7
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/TypedData/EntityResolverTest.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Tests\Core\TypedData\EntityResolverTest.
+ */
+
+namespace Drupal\Tests\Core\TypedData;
+
+use Drupal\Core\TypedData\Resolver\EntityResolver;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests the typed data entity type resolver.
+ */
+class EntityResolverTest extends UnitTestCase {
+
+  /**
+   * The entity type resolver.
+   *
+   * @var \Drupal\Core\TypedData\Resolver\EntityResolver
+   */
+  protected $entityResolver;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity type resolver',
+      'description' => 'Tests the typed data entity type resolver.',
+      'group' => 'Typed Data API',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+    $this->setupEntityResolver();
+  }
+
+  /**
+   * Tests \Drupal\Core\TypedData\Resolver\ResolverManager::resolveParamaterTypes().
+   *
+   * @dataProvider providerTestParameters
+   *
+   * @see \Drupal\Tests\Core\TypedData\EntityResolverTest::providerTestParameters()
+   */
+  public function testResolveParameterTypes(Route $route, $expected) {
+    $this->assertEquals($expected, $this->entityResolver->resolveParameterTypes($route));
+  }
+
+  /**
+   * Provide data for reflection resolver tests.
+   *
+   * @return array
+   *   An array of arrays, each containing the input parameters for
+   *   ResolverManagerTest::testResolveParameterTypes().
+   *
+   * @see \Drupal\Tests\Core\TypedData\EntityResolverTest::testResolveParameterTypes().
+   */
+  public function providerTestParameters() {
+    return array(
+      // Test that the entity resolver automatically finds the entity:node type
+      // if _entity_form is set to 'node.foo'.
+      array(new Route('/test-1/{node}', array(
+        '_entity_form' => 'node.edit',
+      )), array(
+        'node' => array('type' => 'entity:node'),
+      )),
+      // Test that the entity resolver automatically finds the entity:user type
+      // if _entity_list is set to 'user'.
+      array(new Route('/test-2/{user}', array(
+        '_entity_list' => 'user',
+      )), array(
+        'user' => array('type' => 'entity:user'),
+      )),
+      // Test that the entity resolver doesn't do anything if neither
+      // _entity_form nor _entity_list are defined.
+      array(new Route('/test-3/{user}/{node}'), NULL),
+    );
+  }
+
+  /**
+   * Sets up a mocked reflection resolver for the test.
+   */
+  protected function setupEntityResolver() {
+    $manager = $this
+      ->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+      ->disableOriginalConstructor()
+      ->setMethods(array('getDefinitions'))
+      ->getMock();
+
+    $definitions = array(
+      'entity:node' => array('id' => 'entity:node'),
+      'entity:user' => array('id' => 'entity:user'),
+    );
+
+    $property = new \ReflectionProperty('Drupal\Core\TypedData\TypedDataManager', 'definitions');
+    $property->setAccessible(TRUE);
+    $property->setValue($manager, $definitions);
+
+    $this->entityResolver = new EntityResolver($manager);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/TypedData/ReflectionResolverTest.php b/core/tests/Drupal/Tests/Core/TypedData/ReflectionResolverTest.php
new file mode 100644
index 0000000..d908d15
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/TypedData/ReflectionResolverTest.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Tests\Core\TypedData\ReflectionResolverTest.
+ */
+
+namespace Drupal\Tests\Core\TypedData;
+
+use Drupal\Core\TypedData\Resolver\ReflectionResolver;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests the typed data reflection resolver.
+ */
+class ReflectionResolverTest extends UnitTestCase {
+
+  /**
+   * The typed data reflection resolver.
+   *
+   * @var \Drupal\Core\TypedData\Resolver\ReflectionResolver
+   */
+  protected $reflectionResolver;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Reflection resolver',
+      'description' => 'Tests the typed data reflection resolver.',
+      'group' => 'Typed Data API',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+    $this->setupReflectionResolver();
+  }
+
+  /**
+   * Tests \Drupal\Core\TypedData\Resolver\ResolverManager::resolveParamaterTypes().
+   *
+   * @dataProvider providerTestParameters
+   */
+  public function testResolveParameterTypes(Route $route, $expected) {
+    $actual = $this->reflectionResolver->resolveParameterTypes($route);
+    krsort($expected);
+    krsort($actual);
+    $this->assertEquals($expected, $actual);
+  }
+
+  /**
+   * Provide data for reflection resolver tests.
+   *
+   * @return array
+   *   An array of arrays, each containing the input parameters for
+   *   ResolverManagerTest::testResolveParameterTypes().
+   *
+   * @see ResolverManagerTest::testResolveParameterTypes().
+   */
+  public function providerTestParameters() {
+    return array(
+      // Test class type hint reflection of a single _controller method
+      // argument.
+      array(new Route('/test-1/{node}', array(
+        '_controller' => 'Drupal\Tests\Core\TypedData\ReflectionTestController::nodeController',
+      )), array(
+        'node' => array('type' => 'entity:node'),
+      )),
+      // Test class type hint reflection of multiple _controller method
+      // arguments.
+      array(new Route('/test-2/{user}/{node}', array(
+        '_controller' => 'Drupal\Tests\Core\TypedData\ReflectionTestController::nodeUserController',
+      )), array(
+        'user' => array('type' => 'entity:user'),
+        'node' => array('type' => 'entity:node'),
+      )),
+      // Test interface type hint reflection of a _controller method argument.
+      array(new Route('/test-3/{block}', array(
+        '_controller' => 'Drupal\Tests\Core\TypedData\ReflectionTestController::blockController',
+      )), array(
+        'block' => array('type' => 'entity:block'),
+      )),
+      // Test interface and class type hint reflection together.
+      array(new Route('/test-4/{block}/{node}', array(
+        '_controller' => 'Drupal\Tests\Core\TypedData\ReflectionTestController::blockNodeController',
+      )), array(
+        'block' => array('type' => 'entity:block'),
+        'node' => array('type' => 'entity:node'),
+      )),
+      // Test _form controller reflection (different because _form controllers
+      // do not manually specify the controller method).
+      array(new Route('/test-5/{node}/{block}', array(
+        '_form' => 'Drupal\Tests\Core\TypedData\ReflectionTestController',
+      )), array(
+        'node' => array('type' => 'entity:node'),
+        'block' => array('type' => 'entity:block'),
+      )),
+    );
+  }
+
+  /**
+   * Sets up a mocked reflection resolver for the test.
+   */
+  protected function setupReflectionResolver() {
+    $manager = $this
+      ->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
+      ->disableOriginalConstructor()
+      ->setMethods(NULL)
+      ->getMock();
+
+    $definitions = array(
+      'entity:node' => array(
+        'id' => 'entity:node',
+        'class' => 'Drupal\node\Plugin\Core\Entity\Node',
+      ),
+      'entity:user' => array(
+        'id' => 'entity:user',
+        'class' => 'Drupal\user\Plugin\Core\Entity\User',
+      ),
+      'entity:block' => array(
+        'id' => 'entity:block',
+        'class' => 'Drupal\block\Plugin\Core\Entity\Block',
+      ),
+    );
+
+    $property = new \ReflectionProperty('Drupal\Core\TypedData\TypedDataManager', 'definitions');
+    $property->setAccessible(TRUE);
+    $property->setValue($manager, $definitions);
+
+    $this->reflectionResolver = new ReflectionResolver($manager);
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/TypedData/ReflectionTestController.php b/core/tests/Drupal/Tests/Core/TypedData/ReflectionTestController.php
new file mode 100644
index 0000000..7fd0a07
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/TypedData/ReflectionTestController.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Contains \Drupal\Tests\Core\TypedData\ReflectionTestController.
+ */
+
+namespace Drupal\Tests\Core\TypedData;
+
+use Drupal\block\BlockInterface;
+use Drupal\node\Plugin\Core\Entity\Node;
+use Drupal\user\Plugin\Core\Entity\User;
+
+/**
+ * Hypothetical controller for testing reflection based type resolving.
+ */
+class ReflectionTestController {
+
+  /**
+   * Controller with a single node entity type hint.
+   */
+  public function nodeController(Node $node) {
+  }
+
+  /**
+   * Controller with a node and a user entity type hint.
+   */
+  public function nodeUserController(Node $node, User $user) {
+  }
+
+  /**
+   * Controller with a block interface type hint.
+   */
+  public function blockController(BlockInterface $block) {
+  }
+
+  /**
+   * Controller with a block interface and a node class type hint.
+   */
+  public function blockNodeController(Node $node, BlockInterface $block) {
+  }
+
+  /**
+   * Form controller method with a block interface and a node class type hint.
+   */
+  public function buildForm(array $form, array &$form_state, BlockInterface $block = NULL, Node $node = NULL) {
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/TypedData/ResolverManagerTest.php b/core/tests/Drupal/Tests/Core/TypedData/ResolverManagerTest.php
new file mode 100644
index 0000000..e71d036
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/TypedData/ResolverManagerTest.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Tests\Core\TypedData\ResolverManagerTest.
+ */
+
+namespace Drupal\Tests\Core\TypedData;
+
+use Drupal\Core\TypedData\Resolver\ResolverManager;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Tests the typed data resolver manager.
+ */
+class ResolverManagerTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Typed data resolver manager',
+      'description' => 'Tests the typed data resolver manager.',
+      'group' => 'Typed Data API',
+    );
+  }
+
+  /**
+   * Tests \Drupal\Core\TypedData\Resolver\ResolverManager::resolveParamaterTypes().
+   */
+  public function testResolveParameterTypes() {
+    $collection = new RouteCollection();
+    // Add a route for which we want to have automatically generate parameter
+    // definitions.
+    $collection->add('magic', new Route('magic'));
+
+    // Manually pre-define a parameter set for testing that manually defined
+    // parameters do not get replaced by automatically resolved definitions.
+    $collection->add('manual', new Route('manual', array(), array(), array(
+      'parameters' => array(
+        'magic' => 'no',
+      ),
+    )));
+
+    // Mock a resolver that tries to resolve 'magic' to 'yes'. This should not
+    // override manually defined parameters.
+    $resolver = $this->getMock('Drupal\Core\TypedData\Resolver\ResolverInterface');
+    $resolver->expects($this->exactly($collection->count()))
+      ->method('resolveParameterTypes')
+      ->with($this->isInstanceOf('Symfony\Component\Routing\Route'))
+      ->will($this->returnValue(array('magic' => 'yes')));
+
+    $manager = $this->getMockBuilder('Drupal\Core\TypedData\Resolver\ResolverManager')
+      ->setMethods(array('getTypedDataResolvers'))
+      ->getMock();
+
+    $manager->expects($this->once())
+      ->method('getTypedDataResolvers')
+      ->will($this->returnValue(array($resolver)));
+
+    $manager->resolveParameterTypes($collection);
+
+    // Assert that the route options are correct.
+    $this->assertEquals(array('magic' => 'yes'), $collection->get('magic')->getOption('parameters'));
+    $this->assertEquals(array('magic' => 'no'), $collection->get('manual')->getOption('parameters'));
+  }
+
+  /**
+   * Tests \Drupal\Core\TypedData\Resolver\ResolverManager::addTypedDataResolver().
+   *
+   * @dataProvider providerTestResolvers
+   */
+  public function testAddTypedDataResolver($unsorted, $sorted) {
+    $manager = new ResolverManager();
+    foreach ($unsorted as $data) {
+      $resolver = $this->getMockBuilder('Drupal\Core\TypedData\Resolver\ResolverInterface')
+        ->setMockClassName($data['name'])
+        ->getMock();
+
+      $manager->addTypedDataResolver($resolver, $data['priority']);
+    }
+
+    // Test that ResolverManager::getTypedDataResolvers() returns the resolvers
+    // in the expected order.
+    foreach ($manager->getTypedDataResolvers() as $key => $resolver) {
+      $this->assertInstanceOf($sorted[$key], $resolver);
+    }
+  }
+
+  /**
+   * Provide data for typed data resolver tests.
+   *
+   * @return array
+   *   An array of arrays, each containing the input parameters for
+   *   ResolverManagerTest::testAddTypedDataResolver().
+   *
+   * @see ResolverManagerTest::testAddTypedDataResolver().
+   */
+  public function providerTestResolvers() {
+    $resolvers[0]['unsorted'] = array(
+      array('name' => 'RaspberryResolver', 'priority' => 10),
+      array('name' => 'PearResolver', 'priority' => 5),
+      array('name' => 'StrawberryResolver', 'priority' => 20),
+      array('name' => 'PineappleResolver', 'priority' => 0),
+      array('name' => 'BananaResolver', 'priority' => -10),
+      array('name' => 'AppleResolver', 'priority' => -10),
+      array('name' => 'PeachFooResolver', 'priority' => 5),
+    );
+
+    $resolvers[0]['sorted'] = array(
+      'StrawberryResolver', 'RaspberryResolver', 'PearResolver', 'PeachFooResolver',
+      'PineappleResolver', 'BananaResolver', 'AppleResolver'
+    );
+
+    $resolvers[1]['unsorted'] = array(
+      array('name' => 'ApeResolver', 'priority' => 0),
+      array('name' => 'CatResolver', 'priority' => -5),
+      array('name' => 'PuppyResolver', 'priority' => -10),
+      array('name' => 'LlamaResolver', 'priority' => -15),
+      array('name' => 'GiraffeResolver', 'priority' => 10),
+      array('name' => 'ZebraResolver', 'priority' => 10),
+      array('name' => 'EagleResolver', 'priority' => 5),
+    );
+
+    $resolvers[1]['sorted'] = array(
+      'GiraffeResolver', 'ZebraResolver', 'EagleResolver', 'ApeResolver',
+      'CatResolver', 'PuppyResolver', 'LlamaResolver'
+    );
+
+    return $resolvers;
+  }
+
+}
