diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 3216d21..c81f7c3 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -138,7 +138,14 @@ public function getConstraints() {
    */
   public function validate() {
     // @todo: Add the typed data manager as proper dependency.
-    return \Drupal::typedDataManager()->getValidator()->validate($this);
+    if ($this instanceof PrimitiveInterface) {
+      // Primitive data is always validated unwrapped. For that to work we have
+      // to manually pass on the constraints.
+      return \Drupal::typedDataManager()->getValidator()->validate($this->getValue(), $this->getConstraints());
+    }
+    else {
+      return \Drupal::typedDataManager()->getValidator()->validate($this);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
old mode 100644
new mode 100755
index 7fe362e..39142e1
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -335,7 +335,7 @@ public function getValidator() {
         ->setMetadataFactory(new MetadataFactory())
         ->setTranslator(new DrupalTranslator())
         ->setConstraintValidatorFactory(new ConstraintValidatorFactory($this->classResolver))
-        ->setApiVersion(Validation::API_VERSION_2_4)
+        ->setApiVersion(Validation::API_VERSION_2_5)
         ->getValidator();
     }
     return $this->validator;
diff --git a/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php
new file mode 100644
index 0000000..d3c2aa7
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/ComplexPropertyConstraintMetadata.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\ComplexPropertyConstraintMetadata.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+use Symfony\Component\Validator\PropertyMetadataInterface;
+
+/**
+ * Defines a class for providing metadata for a property.
+ */
+class ComplexPropertyConstraintMetadata extends Metadata {
+
+  /**
+   * Constraints for this property.
+   *
+   * @var array|\Symfony\Component\Validator\Constraint[]
+   */
+  protected $constraints;
+
+  /**
+   * If this is for a property.
+   *
+   * @var bool
+   */
+  protected $property = FALSE;
+
+  /**
+   * Constructs a new ComplexPropertyConstraintMetadata object.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
+   *   Typed data for this property.
+   * @param \Symfony\Component\Validator\Constraint[] $constraints
+   *   Array of constraints.
+   */
+  public function __construct(TypedDataInterface $typed_data, array $constraints) {
+    $this->typedData = $typed_data;
+    $this->constraints = $constraints;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function findConstraints($group) {
+    return $this->property ? $this->constraints : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyName() {
+    return 'value';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyValue($container) {
+    return $container->value;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstrainedProperties() {
+    return $this->property ? [] : ['value'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasPropertyMetadata($property_name) {
+    return $this->property ? FALSE : ($property_name == 'value');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyMetadata($property_name) {
+    $metadata = clone $this;
+    $metadata->property = TRUE;
+    return [$metadata];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
old mode 100644
new mode 100755
index 73bfc8b..6eba413
--- a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
@@ -7,98 +7,40 @@
 
 namespace Drupal\Core\TypedData\Validation;
 
-use Drupal\Core\TypedData\TypedDataInterface;
-use Symfony\Component\Validator\ValidationVisitorInterface;
-use Symfony\Component\Validator\PropertyMetadataInterface;
+use Symfony\Component\Validator\Exception\BadMethodCallException;
+use Symfony\Component\Validator\Mapping\CascadingStrategy;
 
 /**
- * Typed data implementation of the validator MetadataInterface.
+ * Validator metadata for typed data objects containing no properties.
  */
-class Metadata implements PropertyMetadataInterface {
+class Metadata extends MetadataBase {
 
   /**
-   * The name of the property, or empty if this is the root.
-   *
-   * @var string
+   * {@inheritdoc}
    */
-  protected $name;
-
-  /**
-   * The typed data object the metadata is about.
-   *
-   * @var \Drupal\Core\TypedData\TypedDataInterface
-   */
-  protected $typedData;
-
-  /**
-   * The metadata factory used.
-   *
-   * @var \Drupal\Core\TypedData\Validation\MetadataFactory
-   */
-  protected $factory;
-
-  /**
-   * Constructs the object.
-   *
-   * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
-   *   The typed data object the metadata is about.
-   * @param $name
-   *   The name of the property to get metadata for. Leave empty, if
-   *   the data is the root of the typed data tree.
-   * @param \Drupal\Core\TypedData\Validation\MetadataFactory $factory
-   *   The factory to use for instantiating property metadata.
-   */
-  public function __construct(TypedDataInterface $typed_data, $name = '', MetadataFactory $factory) {
-    $this->typedData = $typed_data;
-    $this->name = $name;
-    $this->factory = $factory;
+  public function getConstrainedProperties() {
+    return [];
   }
 
   /**
-   * Implements MetadataInterface::accept().
+   * {@inheritdoc}
    */
-  public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
-
-    // @todo: Do we have to care about groups? Symfony class metadata has
-    // $propagatedGroup.
-
-    $visitor->visit($this, $typed_data->getValue(), $group, $propertyPath);
+  public function hasPropertyMetadata($property_name) {
+    return [];
   }
 
   /**
-   * Implements MetadataInterface::findConstraints().
+   * {@inheritdoc}
    */
-  public function findConstraints($group) {
-    return $this->typedData->getConstraints();
+  public function getPropertyMetadata($property_name) {
+    throw new BadMethodCallException("The data does not contain any properties.");
   }
 
   /**
-   * Returns the name of the property.
-   *
-   * @return string The property name.
+   * {@inheritdoc}
    */
-  public function getPropertyName() {
-    return $this->name;
+  public function getCascadingStrategy() {
+    return CascadingStrategy::NONE;
   }
 
-  /**
-   * Extracts the value of the property from the given container.
-   *
-   * @param mixed $container The container to extract the property value from.
-   *
-   * @return mixed The value of the property.
-   */
-  public function getPropertyValue($container) {
-    return $this->typedData->getValue();
-  }
-
-  /**
-   * Returns the typed data object.
-   *
-   * @return \Drupal\Core\TypedData\TypedDataInterface
-   *   The typed data object.
-   */
-  public function getTypedData() {
-    return $this->typedData;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php
new file mode 100755
index 0000000..8380fa0
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataBase.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\MetadataBase.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
+use Symfony\Component\Validator\Mapping\PropertyMetadataInterface;
+use Symfony\Component\Validator\Mapping\TraversalStrategy;
+use Symfony\Component\Validator\ValidationVisitorInterface;
+
+/**
+ * Common base class for implementing validator metadata for typed data objects.
+ */
+abstract class MetadataBase implements ClassMetadataInterface, PropertyMetadataInterface {
+
+  /**
+   * The typed data object the metadata is about.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected $typedData;
+
+  /**
+   * The metadata factory used.
+   *
+   * @var \Drupal\Core\TypedData\Validation\MetadataFactory
+   */
+  protected $factory;
+
+  /**
+   * Constructs the object.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
+   *   The typed data object the metadata is about.
+   * @param \Drupal\Core\TypedData\Validation\MetadataFactory $factory
+   *   The factory to use for instantiating property metadata.
+   */
+  public function __construct(TypedDataInterface $typed_data, MetadataFactory $factory) {
+    $this->typedData = $typed_data;
+    $this->factory = $factory;
+  }
+
+  /**
+   * Returns the typed data object.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The typed data object.
+   */
+  public function getTypedData() {
+    return $this->typedData;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
+
+    // @todo: Do we have to care about groups? Symfony class metadata has
+    // $propagatedGroup.
+    throw new BadMethodCallException('Not supported.');
+
+    $visitor->visit($this, $typed_data->getValue(), $group, $propertyPath);
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function findConstraints($group) {
+    return $this->typedData->getConstraints();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    return $this->typedData->getConstraints();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTraversalStrategy() {
+    return TraversalStrategy::NONE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getClassName() {
+    return get_class($this->typedData);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getGroupSequence() {
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasGroupSequence() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isGroupSequenceProvider() {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyName() {
+    return $this->typedData->getName();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyValue($container) {
+    $property = $container->get($this->getPropertyName());
+    if ($property instanceof ListInterface || $property instanceof ComplexDataInterface) {
+      // To let all constraints properly handle empty structures, pass on NULL
+      // if the data structure is empty. That way existing NotNull or NotBlank
+      // constraints work as expected.
+      return !$property->isEmpty() ? $property : NULL;
+    }
+    else {
+      return $property->getValue();
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
old mode 100644
new mode 100755
index 2858daf..7d271f8
--- a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
@@ -10,7 +10,7 @@
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ListInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
-use Symfony\Component\Validator\MetadataFactoryInterface;
+use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface;
 
 /**
  * Typed data implementation of the validator MetadataFactoryInterface.
@@ -18,27 +18,30 @@
 class MetadataFactory implements MetadataFactoryInterface {
 
   /**
-   * Implements MetadataFactoryInterface::getMetadataFor().
+   * {@inheritdoc}
    *
    * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
    *   Some typed data object containing the value to validate.
-   * @param $name
-   *   (optional) The name of the property to get metadata for. Leave empty, if
-   *   the data is the root of the typed data tree.
    */
-  public function getMetadataFor($typed_data, $name = '') {
+  public function getMetadataFor($typed_data) {
+    if ($typed_data instanceof TypedDataPropertyValidationEnvelope) {
+      $constraints = $typed_data->getConstraints();
+      $typed_data = $typed_data->getTypedData();
+      return new ComplexPropertyConstraintMetadata($typed_data, $constraints);
+    }
     if (!$typed_data instanceof TypedDataInterface) {
       throw new \InvalidArgumentException('The passed value must be a typed data object.');
     }
     $is_container = $typed_data instanceof ComplexDataInterface || $typed_data instanceof ListInterface;
     $class = '\Drupal\Core\TypedData\Validation\\' . ($is_container ? 'PropertyContainerMetadata' : 'Metadata');
-    return new $class($typed_data, $name, $this);
+    return new $class($typed_data, $this);
   }
 
   /**
-   * Implements MetadataFactoryInterface::hasMetadataFor().
+   * {@inheritdoc}
    */
   public function hasMetadataFor($value) {
     return $value instanceof TypedDataInterface;
   }
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
index f5850eb..92dfcfd 100644
--- a/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
+++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
@@ -9,18 +9,21 @@
 
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ListInterface;
-use Symfony\Component\Validator\PropertyMetadataContainerInterface;
+use Symfony\Component\Validator\Mapping\CascadingStrategy;
+use Symfony\Component\Validator\Mapping\TraversalStrategy;
 use Symfony\Component\Validator\ValidationVisitorInterface;
 
 /**
- * Typed data implementation of the validator MetadataInterface.
+ * Validator metadata for typed data property containers.
  */
-class PropertyContainerMetadata extends Metadata implements PropertyMetadataContainerInterface {
+class PropertyContainerMetadata extends MetadataBase {
 
   /**
-   * Overrides Metadata::accept().
+   * {@inheritdoc}
    */
   public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
+    throw new BadMethodCallException('Not supported.');
+
     // To let all constraints properly handle empty structures, pass on NULL
     // if the data structure is empty. That way existing NotNull or NotBlank
     // constraints work as expected.
@@ -39,7 +42,7 @@ public function accept(ValidationVisitorInterface $visitor, $typed_data, $group,
   }
 
   /**
-   * Implements PropertyMetadataContainerInterface::hasPropertyMetadata().
+   * {@inheritdoc}
    */
   public function hasPropertyMetadata($property_name) {
     try {
@@ -52,17 +55,39 @@ public function hasPropertyMetadata($property_name) {
   }
 
   /**
-   * Implements PropertyMetadataContainerInterface::getPropertyMetadata().
+   * {@inheritdoc}
    */
   public function getPropertyMetadata($property_name) {
+    if ($this->typedData instanceof ListInterface || $this->typedData instanceof ComplexDataInterface) {
+      return array($this->factory->getMetadataFor($this->typedData->get($property_name)));
+    }
+    else {
+      throw new \LogicException("There are no known properties.");
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstrainedProperties() {
     if ($this->typedData instanceof ListInterface) {
-      return array(new Metadata($this->typedData[$property_name], $property_name));
+      // @todo: When https://www.drupal.org/node/2164601 gets committed,
+      // simplify this to return the list indexes based on the list count.
+      return array_keys(iterator_to_array($this->typedData));
     }
     elseif ($this->typedData instanceof ComplexDataInterface) {
-      return array(new Metadata($this->typedData->get($property_name), $property_name));
+      return array_keys($this->typedData->getProperties());
     }
     else {
       throw new \LogicException("There are no known properties.");
     }
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCascadingStrategy() {
+    return CascadingStrategy::CASCADE;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php b/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php
new file mode 100644
index 0000000..7ddc074
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/TypedDataPropertyValidationEnvelope.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\TypedDataPropertyValidationEnvelope.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+
+/**
+ * Defines a class for wrapping a typed-data property for validation sake.
+ */
+class TypedDataPropertyValidationEnvelope {
+
+  /**
+   * Typed data in this envelope.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected $typedData;
+
+  /**
+   * Constraints for this envelope.
+   *
+   * @var array|\Symfony\Component\Validator\Constraint[]
+   */
+  protected $constraints;
+
+  /**
+   * The value to validate.
+   *
+   * @var mixed
+   */
+  public $value;
+
+  /**
+   * Constructs a new ComplexPropertyConstraintMetadata object.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
+   *   Typed data for this property.
+   * @param \Symfony\Component\Validator\Constraint[] $constraints
+   *   Array of constraints.
+   * @param mixed $value
+   *   The value to validate.
+   */
+  public function __construct(TypedDataInterface $typed_data, array $constraints, $value) {
+    $this->value = $value;
+    $this->typedData = $typed_data;
+    $this->constraints = $constraints;
+  }
+
+  /**
+   * Factory to create a new TypedDataPropertyValidationEnvelope.
+   *
+   * @param \Drupal\Core\TypedData\TypedDataInterface $typed_data
+   *   Typed data for this property.
+   * @param \Symfony\Component\Validator\Constraint[] $constraints
+   *   Array of constraints.
+   * @param mixed $value
+   *   The value to validate.
+   *
+   * @return static
+   */
+  public static function create($typed_data, array $constraints, $value) {
+    return new static($typed_data, $constraints, $value);
+  }
+
+  /**
+   * Gets the typed data for this envelope.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The wrapped typed data.
+   */
+  public function getTypedData() {
+    return $this->typedData;
+  }
+
+  /**
+   * Constraints for this envelope.
+   *
+   * @return \Symfony\Component\Validator\Constraint[]
+   *   The constraints for this property.
+   */
+  public function getConstraints() {
+    return $this->constraints;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
index efc6906..16782fb 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/AllowedValuesConstraintValidator.php
@@ -7,30 +7,48 @@
 
 namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
 
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\OptionsProviderInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\Constraints\ChoiceValidator;
 
 /**
  * Validates the AllowedValues constraint.
  */
-class AllowedValuesConstraintValidator extends ChoiceValidator {
+class AllowedValuesConstraintValidator extends ChoiceValidator implements ContainerInjectionInterface {
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new AllowedValuesConstraintValidator.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   */
+  public function __construct(AccountInterface $current_user) {
+    $this->currentUser = $current_user;
+  }
 
   /**
    * {@inheritdoc}
    */
   public function validate($value, Constraint $constraint) {
     $typed_data = $this->context->getMetadata()->getTypedData();
-
     if ($typed_data instanceof OptionsProviderInterface) {
-      $account = \Drupal::currentUser();
-      $allowed_values = $typed_data->getSettableValues($account);
+      $allowed_values = $typed_data->getSettableValues($this->currentUser);
       $constraint->choices = $allowed_values;
 
       // If the data is complex, we have to validate its main property.
       if ($typed_data instanceof ComplexDataInterface) {
-        $name = $typed_data->getDataDefinition()->getMainPropertyName();
+        $name = $value->getDataDefinition()->getMainPropertyName();
         if (!isset($name)) {
           throw new \LogicException('Cannot validate allowed values for complex data without a main property.');
         }
@@ -49,4 +67,11 @@ public function validate($value, Constraint $constraint) {
     parent::validate($value, $constraint);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('current_user'));
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
old mode 100644
new mode 100755
index 31ffc5b..9e8c7cc
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ComplexDataConstraintValidator.php
@@ -8,6 +8,8 @@
 namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
 
 use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\Validation\TypedDataPropertyValidationEnvelope;
 use Symfony\Component\Validator\Constraint;
 use Symfony\Component\Validator\ConstraintValidator;
 use Symfony\Component\Validator\Exception\UnexpectedTypeException;
@@ -38,10 +40,18 @@ public function validate($value, Constraint $constraint) {
         $property = $property->getValue();
       }
       elseif ($property->isEmpty()) {
-        // @see \Drupal\Core\TypedData\Validation\PropertyContainerMetadata::accept();
+        // @see \Drupal\Core\TypedData\Validation\ClassMetadata::accept();
         $property = NULL;
       }
-      $this->context->validateValue($property, $constraints, $name, $group);
+
+      // Wrap the property in a validation envelope containing the constraints,
+      // the typed-data and the property value.
+      $envelope = TypedDataPropertyValidationEnvelope::create($value, $constraints, $property);
+
+      $this->context
+        ->getValidator()
+        ->inContext($this->context)
+        ->validate($envelope, NULL, $group);
     }
   }
 }
