diff --git a/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php
new file mode 100644
index 0000000..38e07a2
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator.
+*/
+
+namespace Drupal\Component\Plugin\Discovery;
+
+/**
+ * A decorator that allows manual registration of undiscoverable definitions.
+ */
+class StaticDiscoveryDecorator extends StaticDiscovery {
+
+  /**
+   * The Discovery object being decorated.
+   *
+   * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
+   */
+  protected $decorated;
+
+  /**
+   * Constructs a \Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator object.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
+   *   The discovery object that is being decorated.
+   */
+  public function __construct(DiscoveryInterface $decorated) {
+    $this->decorated = $decorated;
+  }
+
+  /**
+   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
+   */
+  public function getDefinition($base_plugin_id) {
+    $this->definitions = $this->definitions += $this->decorated->getDefinitions();
+    return parent::getDefinition($base_plugin_id);
+  }
+
+  /**
+   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
+   */
+  public function getDefinitions() {
+    $this->definitions = $this->definitions += $this->decorated->getDefinitions();
+    return parent::getDefinitions();
+  }
+
+  /**
+   * Passes through all unknown calls onto the decorated object
+   */
+  public function __call($method, $args) {
+    return call_user_func_array(array($this->decorated, $method), $args);
+  }
+}
diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 624c716..4884ae1 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -145,7 +145,10 @@ public function build(ContainerBuilder $container) {
       ->setFactoryClass('Drupal\Core\Database\Database')
       ->setFactoryMethod('getConnection')
       ->addArgument('slave');
-    $container->register('typed_data', 'Drupal\Core\TypedData\TypedDataManager');
+    $container->register('typed_data', 'Drupal\Core\TypedData\TypedDataManager')
+      ->addMethodCall('setValidationConstraintManager', array(new Reference('validation.constraint')));
+    $container->register('validation.constraint', 'Drupal\Core\Validation\ConstraintManager');
+
     // Add the user's storage for temporary, non-cache data.
     $container->register('lock', 'Drupal\Core\Lock\DatabaseLockBackend');
     $container->register('user.tempstore', 'Drupal\user\TempStoreFactory')
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 732119c..9092a13 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -698,7 +698,7 @@ public function getFieldDefinitions(array $constraints) {
       }
     }
 
-    $bundle = !empty($constraints['bundle']) ? $constraints['bundle'] : FALSE;
+    $bundle = !empty($constraints['Bundle']) ? $constraints['Bundle'] : FALSE;
 
     // Add in per-bundle fields.
     if (!isset($this->fieldDefinitions[$bundle])) {
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index e16c6fb..f8d4300 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -218,8 +218,8 @@ public function getPropertyDefinition($name) {
   public function getPropertyDefinitions() {
     if (!isset($this->fieldDefinitions)) {
       $this->fieldDefinitions = drupal_container()->get('plugin.manager.entity')->getStorageController($this->entityType)->getFieldDefinitions(array(
-        'entity type' => $this->entityType,
-        'bundle' => $this->bundle,
+        'EntityType' => $this->entityType,
+        'Bundle' => $this->bundle,
       ));
     }
     return $this->fieldDefinitions;
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index 2811f1c..ef69cb6 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -125,8 +125,8 @@ public function save(EntityInterface $entity);
    *   'bundle' key. For example:
    *   @code
    *   array(
-   *     'entity type' => 'node',
-   *     'bundle' => 'article',
+   *     'EntityType' => 'node',
+   *     'Bundle' => 'article',
    *   )
    *   @endcode
    *
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
index f1b07e8..6508a40 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
@@ -39,11 +39,14 @@ public function getPropertyDefinitions() {
         // @todo: Lookup the entity type's ID data type and use it here.
         'type' => 'integer',
         'label' => t('Entity ID'),
+        'constraints' => array(
+          'Range' => array('min' => 0),
+        ),
       );
       static::$propertyDefinitions[$entity_type]['entity'] = array(
         'type' => 'entity',
         'constraints' => array(
-          'entity type' => $entity_type,
+          'EntityType' => $entity_type,
         ),
         'label' => t('Entity'),
         'description' => t('The referenced entity'),
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
index 89179d1..691868d 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity\Field\Type;
 
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityNG;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\ContextAwareTypedData;
@@ -29,8 +30,8 @@
  * an 'entity type' constraint is specified.
  *
  * Supported constraints (below the definition's 'constraints' key) are:
- *  - entity type: The entity type.
- *  - bundle: The bundle or an array of possible bundles.
+ *  - EntityType: The entity type.
+ *  - Bundle: The bundle or an array of possible bundles.
  *
  * Supported settings (below the definition's 'settings' key) are:
  *  - id source: If used as computed property, the ID property used to load
@@ -57,7 +58,7 @@ class EntityWrapper extends ContextAwareTypedData implements IteratorAggregate,
    */
   public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) {
     parent::__construct($definition, $name, $parent);
-    $this->entityType = isset($this->definition['constraints']['entity type']) ? $this->definition['constraints']['entity type'] : NULL;
+    $this->entityType = isset($this->definition['constraints']['EntityType']) ? $this->definition['constraints']['EntityType'] : NULL;
   }
 
   /**
@@ -89,7 +90,7 @@ public function setValue($value) {
       $this->entityType = $value->entityType();
       $value = $value->id();
     }
-    elseif (isset($value) && !(is_scalar($value) && !empty($this->definition['constraints']['entity type']))) {
+    elseif (isset($value) && !(is_scalar($value) && !empty($this->definition['constraints']['EntityType']))) {
       throw new InvalidArgumentException('Value is not a valid entity.');
     }
     // Now update the value in the source or the local id property.
@@ -116,7 +117,9 @@ public function getString() {
    * Implements \IteratorAggregate::getIterator().
    */
   public function getIterator() {
-    if ($entity = $this->getValue()) {
+    // @todo: Remove check for EntityNG once all entity types are converted.
+    $entity = $this->getValue();
+    if ($entity && $entity instanceof EntityNG) {
       return $entity->getIterator();
     }
     return new ArrayIterator(array());
@@ -193,6 +196,6 @@ public function setPropertyValues($values) {
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
    */
   public function isEmpty() {
-    return (bool) $this->getValue();
+    return !$this->getValue();
   }
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index 34baa61..49418a0 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -117,6 +117,14 @@ public function getString() {
   }
 
   /**
+   * Overrides \Drupal\Core\TypedData\TypedData::getConstraints().
+   */
+  public function getConstraints() {
+    // Apply the constraints to the list items only.
+    return array();
+  }
+
+  /**
    * Implements \ArrayAccess::offsetExists().
    */
   public function offsetExists($offset) {
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraint.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraint.php
new file mode 100644
index 0000000..a9618ae
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraint.php
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Constraint\BundleConstraint.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+
+/**
+ * Checks if a value is a valid entity type.
+ *
+ * @todo: Move this below the entity core component.
+ *
+ * @Plugin(
+ *   id = "Bundle",
+ *   label = @Translation("Bundle", context = "Validation"),
+ *   type = "entity"
+ * )
+ */
+class BundleConstraint extends Constraint {
+
+  /**
+   * The default violation message.
+   *
+   * @var string
+   */
+  public $message = 'The entity must be of bundle %bundle.';
+
+  /**
+   * The bundle option.
+   *
+   * @var string|array
+   */
+  public $bundle;
+
+  /**
+   * Gets the bundle option as array.
+   *
+   * @return array
+   */
+  public function getBundleOption() {
+    // Support passing the bundle as string, but force it to be an array.
+    if (!is_array($this->bundle)) {
+      $this->bundle = array($this->bundle);
+    }
+    return $this->bundle;
+  }
+
+  /**
+   * Overrides Constraint::getDefaultOption().
+   */
+  public function getDefaultOption() {
+    return 'bundle';
+  }
+
+  /**
+   * Overrides Constraint::getRequiredOptions().
+   */
+  public function getRequiredOptions() {
+    return array('bundle');
+  }
+}
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php
new file mode 100644
index 0000000..d649ccf
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/BundleConstraintValidator.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Validation\Constraint\BundleConstraintValidator.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the Bundle constraint.
+ */
+class BundleConstraintValidator extends ConstraintValidator {
+
+  /**
+   * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
+   */
+  public function validate($typed_data, Constraint $constraint) {
+    $entity = isset($typed_data) ? $typed_data->getValue() : FALSE;
+
+    if (!empty($entity) && !in_array($entity->bundle(), $constraint->getBundleOption())) {
+      $this->context->addViolation($constraint->message, array('%bundle', implode(', ', $constraint->getBundleOption())));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraint.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraint.php
new file mode 100644
index 0000000..3914190
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraint.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Constraint\EntityTypeConstraint.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+
+/**
+ * Checks if a value is a valid entity type.
+ *
+ * @todo: Move this below the entity core component.
+ *
+ * @Plugin(
+ *   id = "EntityType",
+ *   label = @Translation("Entity type", context = "Validation"),
+ *   type = "entity"
+ * )
+ */
+class EntityTypeConstraint extends Constraint {
+
+  /**
+   * The default violation message.
+   *
+   * @var string
+   */
+  public $message = 'The entity must be of type %type.';
+
+  /**
+   * The entity type option.
+   *
+   * @var string
+   */
+  public $type;
+
+  /**
+   * Overrides Constraint::getDefaultOption().
+   */
+  public function getDefaultOption() {
+    return 'type';
+  }
+
+  /**
+   * Overrides Constraint::getRequiredOptions().
+   */
+  public function getRequiredOptions() {
+    return array('type');
+  }
+}
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
new file mode 100644
index 0000000..21b2ac2
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Validation\Constraint\EntityTypeConstraintValidator.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the EntityType constraint.
+ */
+class EntityTypeConstraintValidator extends ConstraintValidator {
+
+  /**
+   * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
+   */
+  public function validate($typed_data, Constraint $constraint) {
+    $entity = isset($typed_data) ? $typed_data->getValue() : FALSE;
+
+    if (!empty($entity) && $entity->entityType() != $constraint->type) {
+      $this->context->addViolation($constraint->message, array('%type', $constraint->type));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
new file mode 100644
index 0000000..8207365
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraint.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Validation\Constraint\PrimitiveTypeConstraint.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Symfony\Component\Validator\Constraints\Type as SymfonyConstraint;
+
+/**
+ * Supports validating all primitive types.
+ *
+ * @todo: Move this below the TypedData core component.
+ *
+ * @Plugin(
+ *   id = "PrimitiveType",
+ *   label = @Translation("Primitive type", context = "Validation")
+ * )
+ */
+class PrimitiveTypeConstraint extends SymfonyConstraint {
+
+  public $message = 'This value should be of type %type.';
+}
diff --git a/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php b/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
new file mode 100644
index 0000000..01821f2
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Validation/Constraint/PrimitiveTypeConstraintValidator.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Validation\Constraint\PrimitiveTypeConstraintValidator.
+ */
+
+namespace Drupal\Core\Plugin\Validation\Constraint;
+
+use DateInterval;
+use Drupal\Core\TypedData\Primitive;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the PrimitiveType constraint.
+ */
+class PrimitiveTypeConstraintValidator extends ConstraintValidator {
+
+  /**
+   * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
+   */
+  public function validate($value, Constraint $constraint) {
+
+    if (!isset($value)) {
+      return;
+    }
+
+    switch ($constraint->type) {
+      case Primitive::BINARY:
+        $valid = is_resource($value);
+        break;
+      case Primitive::BOOLEAN:
+        $valid = is_bool($value) || $value === 0 || $value === '0' || $value === 1 || $value == '1';
+        break;
+      case Primitive::DATE:
+        $valid = $value instanceOf DrupalDateTime && !$value->hasErrors();
+        break;
+      case Primitive::DURATION:
+        $valid = $value instanceof DateInterval;
+        break;
+      case Primitive::FLOAT:
+        $valid = filter_var($value, FILTER_VALIDATE_FLOAT);
+        break;
+      case Primitive::INTEGER:
+        $valid = filter_var($value, FILTER_VALIDATE_INT) !== FALSE;
+        break;
+      case Primitive::STRING:
+        $valid = is_scalar($value);
+        break;
+      case Primitive::URI:
+        $valid = filter_var($value, FILTER_VALIDATE_URL) ;
+        break;
+      default:
+        $valid = FALSE;
+        break;
+    }
+
+    if (!$valid) {
+      $this->context->addViolation($constraint->message, array(
+        '%value' => is_object($value) ? get_class($value) : (is_array($value) ? 'Array' : (string) $value),
+        '%type'  => $constraint->type,
+      ));
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php
index 479ddf4..cf25c73 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Binary.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Binary.php
@@ -40,7 +40,7 @@ public function getValue() {
     // If the value has been set by (absolute) stream resource URI, access the
     // resource now.
     if (!isset($this->handle) && isset($this->uri)) {
-      $this->handle = fopen($this->uri, 'rb');
+      $this->handle = is_readable($this->uri) ? fopen($this->uri, 'rb') : FALSE;
     }
     return $this->handle;
   }
@@ -55,16 +55,14 @@ public function setValue($value) {
       $this->handle = NULL;
       $this->uri = NULL;
     }
-    elseif (is_resource($value)) {
-      $this->handle = $value;
-    }
     elseif (is_string($value)) {
       // Note: For performance reasons we store the given URI and access the
       // resource upon request. See Binary::getValue()
       $this->uri = $value;
+      $this->handle = NULL;
     }
     else {
-      throw new InvalidArgumentException("Invalid value for binary data given.");
+      $this->handle = $value;
     }
   }
 
diff --git a/core/lib/Drupal/Core/TypedData/Type/Boolean.php b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
index 62a6791..6c357c9 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Boolean.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
@@ -23,11 +23,4 @@ class Boolean extends TypedData {
    * @var boolean
    */
   protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value) {
-    $this->value = isset($value) ? (bool) $value : $value;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php
index f6c19e0..37a55ff 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Date.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Date.php
@@ -40,9 +40,13 @@ public function setValue($value) {
     }
     else {
       $this->value = $value instanceOf DrupalDateTime ? $value : new DrupalDateTime($value);
-      if ($this->value->hasErrors()) {
-        throw new InvalidArgumentException("Invalid date format given.");
-      }
     }
   }
+
+  /**
+   * Overrides TypedDataInterface::getString().
+   */
+  public function getString() {
+    return (string) $this->getValue();
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php
index 527fbdd..985d38d 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Duration.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Duration.php
@@ -32,21 +32,29 @@ class Duration extends TypedData {
    * Overrides TypedData::setValue().
    */
   public function setValue($value) {
-    if ($value instanceof DateInterval || !isset($value)) {
-      $this->value = $value;
+    try {
+      if ($value instanceof DateInterval || !isset($value)) {
+        $this->value = $value;
+      }
+      // Treat integer values as time spans in seconds, even if supplied as PHP
+      // string.
+      elseif ((string) (int) $value === (string) $value) {
+        $this->value = new DateInterval('PT' . $value . 'S');
+      }
+      elseif (is_string($value)) {
+        // @todo: Add support for negative intervals on top of the DateInterval
+        // constructor.
+        $this->value = new DateInterval($value);
+      }
+      else {
+        // Unknown value given.
+        $this->value = $value;
+      }
     }
-    // Treat integer values as time spans in seconds, even if supplied as PHP
-    // string.
-    elseif ((string) (int) $value === (string) $value) {
-      $this->value = new DateInterval('PT' . $value . 'S');
-    }
-    elseif (is_string($value)) {
-      // @todo: Add support for negative intervals on top of the DateInterval
-      // constructor.
-      $this->value = new DateInterval($value);
-    }
-    else {
-      throw new InvalidArgumentException("Invalid duration format given.");
+    catch (\Exception $e) {
+      // An invalid value has been given. Setting any invalid value will let
+      // validation fail.
+      $this->value = $e;
     }
   }
 
diff --git a/core/lib/Drupal/Core/TypedData/Type/Float.php b/core/lib/Drupal/Core/TypedData/Type/Float.php
index fe7c8b3..482cad1 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Float.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Float.php
@@ -23,11 +23,4 @@ class Float extends TypedData {
    * @var float
    */
   protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value) {
-    $this->value = isset($value) ? (float) $value : $value;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Integer.php b/core/lib/Drupal/Core/TypedData/Type/Integer.php
index 487e1eb..eb6336e 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Integer.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Integer.php
@@ -23,11 +23,4 @@ class Integer extends TypedData {
    * @var integer
    */
   protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value) {
-    $this->value = isset($value) ? (int) $value : $value;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/String.php b/core/lib/Drupal/Core/TypedData/Type/String.php
index f5402ad..a9d096f 100644
--- a/core/lib/Drupal/Core/TypedData/Type/String.php
+++ b/core/lib/Drupal/Core/TypedData/Type/String.php
@@ -23,11 +23,4 @@ class String extends TypedData {
    * @var string
    */
   protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value) {
-    $this->value = isset($value) ? (string) $value : $value;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Uri.php b/core/lib/Drupal/Core/TypedData/Type/Uri.php
index 3dd893d..791dec9 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Uri.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Uri.php
@@ -22,11 +22,4 @@ class Uri extends TypedData {
    * @var string
    */
   protected $value;
-
-  /**
-   * Overrides TypedData::setValue().
-   */
-  public function setValue($value) {
-    $this->value = isset($value) ? (string) $value : $value;
-  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 35e168c..d6ad4be 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -70,9 +70,18 @@ public function getString() {
   }
 
   /**
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::getConstraints().
+   */
+  public function getConstraints() {
+    // @todo: Add the typed data manager as proper dependency.
+    return typed_data()->getConstraints($this->definition);
+  }
+
+  /**
    * Implements \Drupal\Core\TypedData\TypedDataInterface::validate().
    */
   public function validate() {
-    // TODO: Implement validate() method.
+    // @todo: Add the typed data manager as proper dependency.
+    return typed_data()->getValidator()->validate($this);
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
index 27914cc..0fcf36f 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataInterface.php
@@ -57,7 +57,20 @@ public function setValue($value);
   public function getString();
 
   /**
+   * Gets a list of validation constraints.
+   *
+   * @return array
+   *   Array of constraints, each being an instance of
+   *   \Symfony\Component\Validator\Constraint.
+   */
+  public function getConstraints();
+
+  /**
    * Validates the currently set data value.
+   *
+   * @return \Symfony\Component\Validator\ConstraintViolationListInterface
+   *   A list of constraint violations. If the list is empty, validation
+   *   succeeded.
    */
   public function validate();
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index e978619..bbc132f 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -11,6 +11,10 @@
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\HookDiscovery;
+use Drupal\Core\TypedData\Validation\MetadataFactory;
+use Drupal\Core\Validation\ConstraintManager;
+use Symfony\Component\Validator\ValidatorInterface;
+use Symfony\Component\Validator\Validation;
 
 /**
  * Manages data type plugins.
@@ -18,6 +22,20 @@
 class TypedDataManager extends PluginManagerBase {
 
   /**
+   * The validator used for validating typed data.
+   *
+   * @var \Symfony\Component\Validator\ValidatorInterface
+   */
+  protected $validator;
+
+  /**
+   * The validation constraint manager to use for instantiating constraints.
+   *
+   * @var \Drupal\Core\Validation\ConstraintManager
+   */
+  protected $constraintManager;
+
+  /**
    * An array of typed data property prototypes.
    *
    * @var array
@@ -76,9 +94,8 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
    *   - list settings: An array of settings as required by the used
    *     'list class'. See the documentation of the list class for support or
    *     required settings.
-   *   - constraints: An array of type specific value constraints, e.g. for data
-   *     of type 'entity' the 'entity type' and 'bundle' may be specified. See
-   *     the documentation of the data type 'class' for supported constraints.
+   *   - constraints: An array of validation constraints. See
+   *     \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
    *   - required: A boolean specifying whether a non-NULL value is mandatory.
    *   Further keys may be supported in certain usages, e.g. for further keys
    *   supported for entity field definitions see
@@ -212,4 +229,141 @@ public function getPropertyInstance(ContextAwareInterface $object, $property_nam
     }
     return $property;
   }
+
+  /**
+   * Sets the validator for validating typed data.
+   *
+   * @param \Symfony\Component\Validator\ValidatorInterface $validator
+   *   The validator object to set.
+   */
+  public function setValidator(ValidatorInterface $validator) {
+    $this->validator = $validator;
+  }
+
+  /**
+   * Gets the validator for validating typed data.
+   *
+   * @return \Symfony\Component\Validator\ValidatorInterface
+   *   The validator object.
+   */
+  public function getValidator() {
+    if (!isset($this->validator)) {
+      $this->validator = Validation::createValidatorBuilder()
+        ->setMetadataFactory(new MetadataFactory())
+        ->getValidator();
+    }
+    return $this->validator;
+  }
+
+  /**
+   * Sets the validation constraint manager.
+   *
+   * The validation constraint manager is used to instantiate validation
+   * constraint plugins.
+   *
+   * @param \Drupal\Core\Validation\ConstraintManager
+   *   The constraint manager to set.
+   */
+  public function setValidationConstraintManager(ConstraintManager $constraintManager) {
+    $this->constraintManager = $constraintManager;
+  }
+
+  /**
+   * Gets the validation constraint manager.
+   *
+   * @return \Drupal\Core\Validation\ConstraintManager
+   *   The constraint manager.
+   */
+  public function getValidationConstraintManager() {
+    return $this->constraintManager;
+  }
+
+  /**
+   * Creates a validation constraint plugin.
+   *
+   * @param string $name
+   *   The name or plugin id of the constraint.
+   * @param mixed $options
+   *   The options to pass to the constraint class. Required and supported
+   *   options depend on the constraint class.
+   *
+   * @return \Symfony\Component\Validator\Constraint
+   *   A validation constraint plugin.
+   */
+  public function createValidationConstraint($name, $options) {
+    if (!is_array($options)) {
+      // Plugins need an array as configuration, so make sure we have one.
+      // The constraint classes support passing the options as part of the
+      // 'value' key also.
+      $options = array('value' => $options);
+    }
+    return $this->getValidationConstraintManager()->createInstance($name, $options);
+  }
+
+  /**
+   * Gets configured constraints from a data definition.
+   *
+   * Any constraints defined for the data type, i.e. below the 'constraint' key
+   * of the type's plugin definition, or constraints defined below the data
+   * definition's constraint' key are taken into account.
+   *
+   * Constraints are defined via an array, having constraint plugin IDs as key
+   * and constraint options as values, e.g.
+   * @code
+   * $constraints = array(
+   *   'Range' => array('min' => 5, 'max' => 10),
+   *   'NotBlank' => array(),
+   * );
+   * @endcode
+   * Options have to be specified using another array if the constraint has more
+   * than one or zero options. If it has exactly one option, the value should be
+   * specified without nesting it into another array:
+   * @code
+   * $constraints = array(
+   *   'EntityType' => 'node',
+   *   'Bundle' => 'article',
+   * );
+   * @endcode
+   *
+   * Note that the specified constraints must be compatible with the data type,
+   * e.g. for data of type 'entity' the 'EntityType' and 'Bundle' constraints
+   * may be specified.
+   *
+   * @see \Drupal\Core\Validation\ConstraintManager
+   *
+   * @param array $definition
+   *   A data definition array.
+   *
+   * @return array
+   *   Array of constraints, each being an instance of
+   *   \Symfony\Component\Validator\Constraint.
+   */
+  public function getConstraints($definition) {
+    $constraints = array();
+    // @todo: Figure out how to handle nested constraint structures as
+    // collections.
+    $type_definition = $this->getDefinition($definition['type']);
+    // Auto-generate a constraint for the primitive type if we have a mapping.
+    if (isset($type_definition['primitive type'])) {
+      $constraints[] = $this->getValidationConstraintManager()->
+        createInstance('PrimitiveType', array('type' => $type_definition['primitive type']));
+    }
+    // Add in constraints specified by the data type.
+    if (isset($type_definition['constraints'])) {
+      foreach ($type_definition['constraints'] as $name => $options) {
+        $constraints[] = $this->createValidationConstraint($name, $options);
+      }
+    }
+    // Add any constraints specified as part of the data definition.
+    if (isset($definition['constraints'])) {
+      foreach ($definition['constraints'] as $name => $options) {
+        $constraints[] = $this->createValidationConstraint($name, $options);
+      }
+    }
+    // Add the NotNull constraint for required data.
+    if (!empty($definition['required']) && empty($definition['constraints']['NotNull'])) {
+      $constraints[] = $this->createValidationConstraint('NotNull', array());
+    }
+    return $constraints;
+  }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Validation/Metadata.php b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
new file mode 100644
index 0000000..5cfc5df
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/Metadata.php
@@ -0,0 +1,94 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\Metadata.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\TypedDataInterface;
+use Symfony\Component\Validator\ValidationVisitorInterface;
+use Symfony\Component\Validator\PropertyMetadataInterface;
+
+/**
+ * Typed data implementation of the validator MetadataInterface.
+ */
+class Metadata implements PropertyMetadataInterface {
+
+  /**
+   * The name of the property, or empty if this is the root.
+   *
+   * @var string
+   */
+  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;
+  }
+
+  /**
+   * Implements MetadataInterface::accept().
+   */
+  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);
+  }
+
+  /**
+   * Implements MetadataInterface::findConstraints().
+   */
+  public function findConstraints($group) {
+    return $this->typedData->getConstraints();
+  }
+
+  /**
+   * Returns the name of the property.
+   *
+   * @return string The property name.
+   */
+  public function getPropertyName() {
+    return $this->name;
+  }
+
+  /**
+   * 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();
+  }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
new file mode 100644
index 0000000..2858daf
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/MetadataFactory.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\MetadataFactory.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
+use Symfony\Component\Validator\MetadataFactoryInterface;
+
+/**
+ * Typed data implementation of the validator MetadataFactoryInterface.
+ */
+class MetadataFactory implements MetadataFactoryInterface {
+
+  /**
+   * Implements MetadataFactoryInterface::getMetadataFor().
+   *
+   * @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 = '') {
+    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);
+  }
+
+  /**
+   * Implements MetadataFactoryInterface::hasMetadataFor().
+   */
+  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
new file mode 100644
index 0000000..57b6074
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/Validation/PropertyContainerMetadata.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\Validation\PropertyContainerMetadata.
+ */
+
+namespace Drupal\Core\TypedData\Validation;
+
+use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\ListInterface;
+use Symfony\Component\Validator\PropertyMetadataContainerInterface;
+use Symfony\Component\Validator\ValidationVisitorInterface;
+
+/**
+ * Typed data implementation of the validator MetadataInterface.
+ */
+class PropertyContainerMetadata extends Metadata implements PropertyMetadataContainerInterface {
+
+  /**
+   * Overrides Metadata::accept().
+   */
+  public function accept(ValidationVisitorInterface $visitor, $typed_data, $group, $propertyPath) {
+    // 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.
+    if ($typed_data->isEmpty()) {
+      $typed_data = NULL;
+    }
+    $visitor->visit($this, $typed_data, $group, $propertyPath);
+    $pathPrefix = empty($propertyPath) ? '' : $propertyPath . '.';
+
+    if ($typed_data) {
+      foreach ($typed_data as $name => $data) {
+        $metadata = $this->factory->getMetadataFor($data, $name);
+        $metadata->accept($visitor, $data, $group, $pathPrefix . $name);
+      }
+    }
+  }
+
+  /**
+   * Implements PropertyMetadataContainerInterface::getPropertyMetadata().
+   */
+  public function getPropertyMetadata($property_name) {
+    if ($this->typedData instanceof ListInterface) {
+      return array(new PropertyMetadata($this->typedData[$property_name], $property_name));
+    }
+    elseif ($this->typedData instanceof ComplexDataInterface) {
+      return array(new PropertyMetadata($this->typedData->get($property_name), $property_name));
+    }
+    else {
+      throw new \LogicException("There are no known properties.");
+    }
+  }
+}
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Validation/ConstraintManager.php b/core/lib/Drupal/Core/Validation/ConstraintManager.php
new file mode 100644
index 0000000..30eca01
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/ConstraintManager.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\ConstraintManager.
+ */
+
+namespace Drupal\Core\Validation;
+
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\StaticDiscoveryDecorator;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
+use Drupal\Component\Plugin\Discovery\ProcessDecorator;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+/**
+ * Constraint plugin manager.
+ *
+ * Manages validation constraints based upon
+ * \Symfony\Component\Validator\Constraint, whereas Symfony constraints are
+ * added in via the SymfonyDiscoveryDecorator class. Constraint options are
+ * passed on as plugin configuration during plugin instantiation.
+ *
+ * While core does not prefix constraint plugins, modules have to prefix them
+ * with the module name in order to avoid any naming conflicts. E.g. a "profile"
+ * module would have to prefix any constraints with "Profile".
+ *
+ * Constraint plugins may specify data types to which support is limited via the
+ * 'type' key of plugin definitions. Valid values are any types registered via
+ * the typed data API, or an array of multiple type names. For supporting all
+ * types FALSE may be specified. The key defaults to an empty array, i.e. no
+ * types are supported.
+ */
+class ConstraintManager extends PluginManagerBase {
+
+  /**
+   * Overrides \Drupal\Component\Plugin\PluginManagerBase::__construct().
+   */
+  public function __construct() {
+    $this->discovery = new AnnotatedClassDiscovery('Validation', 'Constraint');
+    $this->discovery = new StaticDiscoveryDecorator($this->discovery);
+    $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
+    $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
+    $this->discovery = new AlterDecorator($this->discovery, 'validation_constraint');
+    $this->discovery = new CacheDecorator($this->discovery, 'validation_constraints:' . language(LANGUAGE_TYPE_INTERFACE)->langcode);
+
+    $this->factory = new DefaultFactory($this->discovery);
+
+    $this->discovery->setDefinition('Range', array(
+      'label' => t('Range'),
+      'class' => '\Symfony\Component\Validator\Constraints\Range',
+      'type' => array('integer', 'float'),
+    ));
+    $this->discovery->setDefinition('Length', array(
+      'label' => t('Length'),
+      'class' => '\Symfony\Component\Validator\Constraints\Length',
+      'type' => 'string',
+    ));
+    $this->discovery->setDefinition('Null', array(
+      'label' => t('Null'),
+      'class' => '\Symfony\Component\Validator\Constraints\Null',
+      'type' => FALSE,
+    ));
+    $this->discovery->setDefinition('NotNull', array(
+      'label' => t('Not null'),
+      'class' => '\Symfony\Component\Validator\Constraints\NotNull',
+      'type' => FALSE,
+    ));
+    $this->discovery->setDefinition('Blank', array(
+      'label' => t('Blank'),
+      'class' => '\Symfony\Component\Validator\Constraints\Blank',
+      'type' => FALSE,
+    ));
+    $this->discovery->setDefinition('NotBlank', array(
+      'label' => t('Not blank'),
+      'class' => '\Symfony\Component\Validator\Constraints\NotBlank',
+      'type' => FALSE,
+    ));
+  }
+
+  /**
+   * Process definition callback for the ProcessDecorator.
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    // Make sure 'type' is set and either an array or FALSE.
+    if (!isset($definition['type'])) {
+      $definition['type'] = array();
+    }
+    elseif ($definition['type'] !== FALSE && !is_array($definition['type'])) {
+      $definition['type'] = array($definition['type']);
+    }
+  }
+
+  /**
+   * Returns a list of constraints that support the given type.
+   *
+   * @param string $type
+   *   The type to filter on.
+   *
+   * @return array
+   *   An array of constraint plugin definitions supporting the given type,
+   *   keyed by constraint name (plugin ID).
+   */
+  public function getDefinitionsByType($type) {
+    $definitions = array();
+    foreach ($this->getDefinitions() as $plugin_id => $definition) {
+      if ($definition['type'] === FALSE || in_array($type, $definition['type'])) {
+        $definitions[$plugin_id] = $definition;
+      }
+    }
+    return $definitions;
+  }
+}
diff --git a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
index 68a5701..ae53920 100644
--- a/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
+++ b/core/modules/field_sql_storage/lib/Drupal/field_sql_storage/Entity/Tables.php
@@ -182,9 +182,9 @@ function addField($field, $type, $langcode) {
           $next_index_prefix = $relationship_specifier;
         }
         // Check for a valid relationship.
-        if (isset($propertyDefinitions[$relationship_specifier]['constraints']['entity type']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) {
+        if (isset($propertyDefinitions[$relationship_specifier]['constraints']['EntityType']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) {
           // If it is, use the entity type.
-          $entity_type = $propertyDefinitions[$relationship_specifier]['constraints']['entity type'];
+          $entity_type = $propertyDefinitions[$relationship_specifier]['constraints']['EntityType'];
           $entity_info = entity_get_info($entity_type);
           // Add the new entity base table using the table and sql column.
           $join_condition= '%alias.' . $entity_info['entity_keys']['id'] . " = $table.$sql_column";
@@ -194,7 +194,7 @@ function addField($field, $type, $langcode) {
           $index_prefix .= "$next_index_prefix.";
         }
         else {
-          throw new QueryException(format_string('Invalid specifier @next.', array('@next' => $next)));
+          throw new QueryException(format_string('Invalid specifier @next.', array('@next' => $relationship_specifier)));
         }
       }
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
index f01d440..5f93835 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -333,7 +333,7 @@ protected function assertIntrospection($entity_type) {
     $definition = array(
       'type' => 'entity',
       'constraints' => array(
-        'entity type' => $entity_type,
+        'EntityType' => $entity_type,
       ),
       'label' => t('Test entity'),
     );
@@ -432,8 +432,7 @@ protected function assertIterator($entity_type) {
   }
 
   /**
-   * Tests working with entity properties based upon data structure and data
-   * list interfaces.
+   * Tests working with the entity based upon the TypedData API.
    */
   public function testDataStructureInterfaces() {
     // All entity variations have to have the same results.
@@ -454,14 +453,15 @@ protected function assertDataStructureInterfaces($entity_type) {
     $entity_definition = array(
       'type' => 'entity',
       'constraints' => array(
-        'entity type' => $entity_type,
+        'EntityType' => $entity_type,
       ),
       'label' => t('Test entity'),
     );
     $wrapped_entity = typed_data()->create($entity_definition, $entity);
 
-    // For the test we navigate through the tree of contained properties and get
-    // all contained strings, limited by a certain depth.
+    // Test using the whole tree of typed data by navigating through the tree of
+    // contained properties and getting all contained strings, limited by a
+    // certain depth.
     $strings = array();
     $this->getContainedStrings($wrapped_entity, 0, $strings);
 
@@ -504,6 +504,52 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &
   }
 
   /**
+   * Tests validation constraints provided by the Entity API.
+   */
+  public function testEntityConstraintValidation() {
+    $entity = $this->createTestEntity('entity_test');
+    $entity->save();
+    $entity_definition = array(
+      'type' => 'entity',
+      'constraints' => array(
+        'EntityType' => 'entity_test',
+      ),
+      'label' => t('Test entity'),
+    );
+    $wrapped_entity = typed_data()->create($entity_definition, $entity);
+
+    // Test validation the typed data object.
+    $violations = $wrapped_entity->validate();
+    $this->assertEqual($violations->count(), 0);
+
+    // Test validating an entity of the wrong type.
+    $node = $this->drupalCreateNode(array('type' => 'page'));
+    $wrapped_entity->setValue($node);
+    $violations = $wrapped_entity->validate();
+    $this->assertEqual($violations->count(), 1);
+
+    // Test bundle validation.
+    $entity_definition = array(
+      'type' => 'entity',
+      'constraints' => array(
+        'EntityType' => 'node',
+        'Bundle' => 'article',
+      ),
+      'label' => t('Test entity'),
+    );
+    $wrapped_entity = typed_data()->create($entity_definition, $node);
+
+    $violations = $wrapped_entity->validate();
+    $this->assertEqual($violations->count(), 1);
+
+    $node->type = 'article';
+    node_save($node);
+    $wrapped_entity->setValue($node);
+    $violations = $wrapped_entity->validate();
+    $this->assertEqual($violations->count(), 0);
+  }
+
+  /**
    * Tests getting processed property values via a computed property.
    */
   public function testComputedProperties() {
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 d88a254..3d2c256 100644
--- a/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/TypedData/TypedDataTest.php
@@ -16,6 +16,13 @@
  */
 class TypedDataTest extends WebTestBase {
 
+  /**
+   * The typed data manager to use.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager
+   */
+  protected $typedData;
+
   public static function getInfo() {
     return array(
       'name' => 'Test typed data objects',
@@ -24,94 +31,137 @@ public static function getInfo() {
     );
   }
 
+  public function setUp() {
+    parent::setup();
+    $this->typedData = typed_data();
+  }
+
   /**
-   * Tests the basics around constructing and working with data wrappers.
+   * Tests the basics around constructing and working with typed data objects.
    */
   public function testGetAndSet() {
     // Boolean type.
-    $wrapper = $this->createTypedData(array('type' => 'boolean'), TRUE);
-    $this->assertTrue($wrapper->getValue() === TRUE, 'Boolean value was fetched.');
-    $wrapper->setValue(FALSE);
-    $this->assertTrue($wrapper->getValue() === FALSE, 'Boolean value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Boolean value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Boolean wrapper is null-able.');
+    $typed_data = $this->createTypedData(array('type' => 'boolean'), TRUE);
+    $this->assertTrue($typed_data->getValue() === TRUE, 'Boolean value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(FALSE);
+    $this->assertTrue($typed_data->getValue() === FALSE, 'Boolean value was changed.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $this->assertTrue(is_string($typed_data->getString()), 'Boolean value was converted to string');
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Boolean wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // String type.
     $value = $this->randomString();
-    $wrapper = $this->createTypedData(array('type' => 'string'), $value);
-    $this->assertTrue($wrapper->getValue() === $value, 'String value was fetched.');
+    $typed_data = $this->createTypedData(array('type' => 'string'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'String value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     $new_value = $this->randomString();
-    $wrapper->setValue($new_value);
-    $this->assertTrue($wrapper->getValue() === $new_value, 'String value was changed.');
+    $typed_data->setValue($new_value);
+    $this->assertTrue($typed_data->getValue() === $new_value, 'String value was changed.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     // Funky test.
-    $this->assertTrue(is_string($wrapper->getString()), 'String value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'String wrapper is null-able.');
+    $this->assertTrue(is_string($typed_data->getString()), 'String value was converted to string');
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'String wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(array('no string'));
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // Integer type.
     $value = rand();
-    $wrapper = $this->createTypedData(array('type' => 'integer'), $value);
-    $this->assertTrue($wrapper->getValue() === $value, 'Integer value was fetched.');
+    $typed_data = $this->createTypedData(array('type' => 'integer'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'Integer value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     $new_value = rand();
-    $wrapper->setValue($new_value);
-    $this->assertTrue($wrapper->getValue() === $new_value, 'Integer value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Integer value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Integer wrapper is null-able.');
+    $typed_data->setValue($new_value);
+    $this->assertTrue($typed_data->getValue() === $new_value, 'Integer value was changed.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Integer value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Integer wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // Float type.
     $value = 123.45;
-    $wrapper = $this->createTypedData(array('type' => 'float'), $value);
-    $this->assertTrue($wrapper->getValue() === $value, 'Float value was fetched.');
+    $typed_data = $this->createTypedData(array('type' => 'float'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'Float value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     $new_value = 678.90;
-    $wrapper->setValue($new_value);
-    $this->assertTrue($wrapper->getValue() === $new_value, 'Float value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Float value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Float wrapper is null-able.');
+    $typed_data->setValue($new_value);
+    $this->assertTrue($typed_data->getValue() === $new_value, 'Float value was changed.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Float value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Float wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // Date type.
     $value = new DrupalDateTime(REQUEST_TIME);
-    $wrapper = $this->createTypedData(array('type' => 'date'), $value);
-    $this->assertTrue($wrapper->getValue() === $value, 'Date value was fetched.');
+    $typed_data = $this->createTypedData(array('type' => 'date'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'Date value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     $new_value = REQUEST_TIME + 1;
-    $wrapper->setValue($new_value);
-    $this->assertTrue($wrapper->getValue()->getTimestamp() === $new_value, 'Date value was changed and set by timestamp.');
-    $wrapper->setValue('2000-01-01');
-    $this->assertTrue($wrapper->getValue()->format('Y-m-d') == '2000-01-01', 'Date value was changed and set by date string.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Date value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Date wrapper is null-able.');
+    $typed_data->setValue($new_value);
+    $this->assertTrue($typed_data->getValue()->getTimestamp() === $new_value, 'Date value was changed and set by timestamp.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('2000-01-01');
+    $this->assertTrue($typed_data->getValue()->format('Y-m-d') == '2000-01-01', 'Date value was changed and set by date string.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Date value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Date wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // Duration type.
     $value = new DateInterval('PT20S');
-    $wrapper = $this->createTypedData(array('type' => 'duration'), $value);
-    $this->assertTrue($wrapper->getValue() === $value, 'Duration value was fetched.');
-    $wrapper->setValue(10);
-    $this->assertTrue($wrapper->getValue()->s == 10, 'Duration value was changed and set by time span in seconds.');
-    $wrapper->setValue('P40D');
-    $this->assertTrue($wrapper->getValue()->d == 40, 'Duration value was changed and set by duration string.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Duration value was converted to string');
+    $typed_data = $this->createTypedData(array('type' => 'duration'), $value);
+    $this->assertTrue($typed_data->getValue() === $value, 'Duration value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(10);
+    $this->assertTrue($typed_data->getValue()->s == 10, 'Duration value was changed and set by time span in seconds.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('P40D');
+    $this->assertTrue($typed_data->getValue()->d == 40, 'Duration value was changed and set by duration string.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Duration value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     // Test getting the string and passing it back as value.
-    $duration = $wrapper->getString();
-    $wrapper->setValue($duration);
-    $this->assertEqual($wrapper->getString(), $duration, 'Duration formatted as string can be used to set the duration value.');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Duration wrapper is null-able.');
-
-    // Generate some files that will be used to test the URI and the binary
-    // data types.
-    $files = $this->drupalGetTestFiles('image');
+    $duration = $typed_data->getString();
+    $typed_data->setValue($duration);
+    $this->assertEqual($typed_data->getString(), $duration, 'Duration formatted as string can be used to set the duration value.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Duration wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
 
     // URI type.
-    $wrapper = $this->createTypedData(array('type' => 'uri'), $files[0]->uri);
-    $this->assertTrue($wrapper->getValue() === $files[0]->uri, 'URI value was fetched.');
-    $wrapper->setValue($files[1]->uri);
-    $this->assertTrue($wrapper->getValue() === $files[1]->uri, 'URI value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'URI value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'URI wrapper is null-able.');
+    $uri = 'http://example.com/foo/';
+    $typed_data = $this->createTypedData(array('type' => 'uri'), $uri);
+    $this->assertTrue($typed_data->getValue() === $uri, 'URI value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue($uri . 'bar.txt');
+    $this->assertTrue($typed_data->getValue() === $uri . 'bar.txt', 'URI value was changed.');
+    $this->assertTrue(is_string($typed_data->getString()), 'URI value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'URI wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+
+    // Generate some files that will be used to test the binary data type.
+    $files = $this->drupalGetTestFiles('image');
 
     // Email type.
     $value = $this->randomString();
@@ -127,17 +177,121 @@ public function testGetAndSet() {
     $this->assertNull($wrapper->getValue(), 'E-mail wrapper is null-able.');
 
     // Binary type.
-    $wrapper = $this->createTypedData(array('type' => 'binary'), $files[0]->uri);
-    $this->assertTrue(is_resource($wrapper->getValue()), 'Binary value was fetched.');
+    $typed_data = $this->createTypedData(array('type' => 'binary'), $files[0]->uri);
+    $this->assertTrue(is_resource($typed_data->getValue()), 'Binary value was fetched.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     // Try setting by URI.
-    $wrapper->setValue($files[1]->uri);
-    $this->assertEqual(is_resource($wrapper->getValue()), fopen($files[1]->uri, 'r'), 'Binary value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Binary value was converted to string');
+    $typed_data->setValue($files[1]->uri);
+    $this->assertEqual(is_resource($typed_data->getValue()), fopen($files[1]->uri, 'r'), 'Binary value was changed.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Binary value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
     // Try setting by resource.
-    $wrapper->setValue(fopen($files[2]->uri, 'r'));
-    $this->assertEqual(is_resource($wrapper->getValue()), fopen($files[2]->uri, 'r'), 'Binary value was changed.');
-    $this->assertTrue(is_string($wrapper->getString()), 'Binary value was converted to string');
-    $wrapper->setValue(NULL);
-    $this->assertNull($wrapper->getValue(), 'Binary wrapper is null-able.');
+    $typed_data->setValue(fopen($files[2]->uri, 'r'));
+    $this->assertEqual(is_resource($typed_data->getValue()), fopen($files[2]->uri, 'r'), 'Binary value was changed.');
+    $this->assertTrue(is_string($typed_data->getString()), 'Binary value was converted to string');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue(NULL);
+    $this->assertNull($typed_data->getValue(), 'Binary wrapper is null-able.');
+    $this->assertEqual($typed_data->validate()->count(), 0);
+    $typed_data->setValue('invalid');
+    $this->assertEqual($typed_data->validate()->count(), 1, 'Validation detected invalid value.');
+  }
+
+  /**
+   * Tests typed data validation.
+   */
+  public function testTypedDataValidation() {
+    $definition = array(
+      'type' => 'integer',
+      'constraints' => array(
+        'Range' => array('min' => 5),
+      ),
+    );
+    $violations = $this->typedData->create($definition, 10)->validate();
+    $this->assertEqual($violations->count(), 0);
+
+    $integer = $this->typedData->create($definition, 1);
+    $violations = $integer->validate();
+    $this->assertEqual($violations->count(), 1);
+
+    $message = t('This value should be {{ limit }} or more.', array('{{ limit }}' => 5));
+    // Right now there is no support for getting translated messages, so test
+    // doing it manually for now.
+    // @todo: Replace this to use the API for getting translations.
+    $translation = t($violations[0]->getMessageTemplate(), $violations[0]->getMessageParameters());
+    $this->assertEqual($translation, $message, 'Translated violation message retrieved.');
+    $this->assertEqual($violations[0]->getPropertyPath(), '');
+    $this->assertEqual($violations[0]->getPropertyPath(), '');
+    $this->assertIdentical($violations[0]->getRoot(), $integer, 'Root object returned.');
+
+    // Test having multiple violations.
+    $definition = array(
+      'type' => 'integer',
+      'constraints' => array(
+        'Range' => array('min' => 5),
+        'Null' => array(),
+      ),
+    );
+    $violations = $this->typedData->create($definition, 10)->validate();
+    $this->assertEqual($violations->count(), 1);
+    $violations = $this->typedData->create($definition, 1)->validate();
+    $this->assertEqual($violations->count(), 2);
+
+    // Test validating property containers and make sure the NotNull and Null
+    // constraints work with typed data containers.
+    $definition = array(
+      'type' => 'integer_field',
+      'constraints' => array(
+        'NotNull' => array(),
+      ),
+    );
+    $field_item = $this->typedData->create($definition, array('value' => 10));
+    $violations = $field_item->validate();
+    $this->assertEqual($violations->count(), 0);
+
+    $field_item = $this->typedData->create($definition, array('value' => 'no integer'));
+    $violations = $field_item->validate();
+    $this->assertEqual($violations->count(), 1);
+    $this->assertEqual($violations[0]->getPropertyPath(), 'value');
+
+    // Test that the field item may not be empty.
+    $field_item = $this->typedData->create($definition);
+    $violations = $field_item->validate();
+    $this->assertEqual($violations->count(), 1);
+
+    // Test the Null constraint with typed data containers.
+    $definition = array(
+      'type' => 'integer_field',
+      'constraints' => array(
+        'Null' => array(),
+      ),
+    );
+    $field_item = $this->typedData->create($definition, array('value' => 10));
+    $violations = $field_item->validate();
+    $this->assertEqual($violations->count(), 1);
+    $field_item = $this->typedData->create($definition);
+    $violations = $field_item->validate();
+    $this->assertEqual($violations->count(), 0);
+
+    // Test getting constraint definitions by type.
+    $definitions = $this->typedData->getValidationConstraintManager()->getDefinitionsByType('entity');
+    $this->assertTrue(isset($definitions['EntityType']), 'Constraint plugin found for type entity.');
+    $this->assertTrue(isset($definitions['Null']), 'Constraint plugin found for type entity.');
+    $this->assertTrue(isset($definitions['NotNull']), 'Constraint plugin found for type entity.');
+
+    $definitions = $this->typedData->getValidationConstraintManager()->getDefinitionsByType('string');
+    $this->assertFalse(isset($definitions['EntityType']), 'Constraint plugin not found for type string.');
+    $this->assertTrue(isset($definitions['Null']), 'Constraint plugin found for type string.');
+    $this->assertTrue(isset($definitions['NotNull']), 'Constraint plugin found for type string.');
+
+    // Test automatic 'required' validation.
+    $definition = array(
+      'type' => 'integer',
+      'required' => TRUE,
+    );
+    $violations = $this->typedData->create($definition)->validate();
+    $this->assertEqual($violations->count(), 1);
+    $violations = $this->typedData->create($definition, 0)->validate();
+    $this->assertEqual($violations->count(), 0);
   }
 }
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 7e95a03..f4130db 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -178,6 +178,8 @@ function hook_cron() {
  *     primitive types in \Drupal\Core\TypedData\Primitive. If set, it must be
  *     a constant defined by \Drupal\Core\TypedData\Primitive such as
  *     \Drupal\Core\TypedData\Primitive::String.
+ *   - constraints: An array of validation constraints for this type. See
+ *     \Drupal\Core\TypedData\TypedDataManager::getConstraints() for details.
  *
  * @see typed_data()
  * @see Drupal\Core\TypedData\TypedDataManager::create()
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
index e7a3f40..7f7ab02 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
@@ -35,7 +35,7 @@ public function getPropertyDefinitions() {
       static::$propertyDefinitions['entity'] = array(
         'type' => 'entity',
         'constraints' => array(
-          'entity type' => 'taxonomy_term',
+          'EntityType' => 'taxonomy_term',
         ),
         'label' => t('Term'),
         'description' => t('The referenced taxonomy term'),
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
index 8c2dcef..73e0f6f 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/Tests/EntityTranslationUITest.php
@@ -156,7 +156,7 @@ function testTranslationUI() {
       $stored_value = $this->getValue($translation, $property, $default_langcode);
       $value = is_array($value) ? $value[0]['value'] : $value;
       $message = format_string('@property correctly stored in the default language.', array('@property' => $property));
-      $this->assertIdentical($stored_value, $value, $message);
+      $this->assertEqual($stored_value, $value, $message);
     }
 
     // Add an entity translation.
