diff --git a/core/lib/Drupal/Core/Config/Schema/Mapping.php b/core/lib/Drupal/Core/Config/Schema/Mapping.php
index b7bddb3..752ad46 100644
--- a/core/lib/Drupal/Core/Config/Schema/Mapping.php
+++ b/core/lib/Drupal/Core/Config/Schema/Mapping.php
@@ -53,10 +53,6 @@ public function get($property_name) {
    * Implements Drupal\Core\TypedData\ComplexDataInterface::set().
    */
   public function set($property_name, $value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     // Set the data into the configuration array but behave according to the
     // interface specification when we've got a null value.
     if (isset($value)) {
@@ -71,6 +67,10 @@ public function set($property_name, $value, $notify = TRUE) {
       $property->setValue($value);
       return $property;
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 7ea8b62..4b1a89e 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -409,9 +409,10 @@ public function getNGEntity() {
    * Implements \Drupal\Core\TypedData\TypedDataInterface::getType().
    */
   public function getType() {
-    // @todo: Incorporate the entity type here by making entities proper
-    // typed data. See http://drupal.org/node/1868004.
-    return 'entity';
+    if ($this->bundle() != $this->entityType()) {
+      return 'entity:' . $this->entityType() . ':' . $this->bundle();
+    }
+    return 'entity:' . $this->entityType();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 57fe186..898f2ce 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\TypedData\AccessibleInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
+use Drupal\Core\TypedData\IdentifiableInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 
 /**
@@ -27,16 +28,7 @@
  * @see \Drupal\Core\TypedData\TypedDataManager
  * @see \Drupal\Core\Field\FieldInterface
  */
-interface EntityInterface extends ComplexDataInterface, AccessibleInterface, TranslatableInterface {
-
-  /**
-   * Returns the entity identifier (the entity's machine name or numeric ID).
-   *
-   * @return
-   *   The identifier of the entity, or NULL if the entity does not yet have
-   *   an identifier.
-   */
-  public function id();
+interface EntityInterface extends IdentifiableInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface {
 
   /**
    * Returns the entity UUID (Universally Unique Identifier).
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index 2ccbfa8..aee1c4f 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -91,15 +91,6 @@ public function __construct(array $values, $entity_type, $bundle = FALSE) {
   }
 
   /**
-   * Gets the typed data type of the entity.
-   *
-   * @return string
-   */
-  public function getType() {
-    return $this->entityType;
-  }
-
-  /**
    * Initialize the object. Invoked upon construction and wake up.
    */
   protected function init() {
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
index cb25ab3..f1b345e 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
@@ -49,10 +49,6 @@ public function setValue($values, $notify = TRUE) {
       $keys = array_keys($this->getPropertyDefinitions());
       $values = array($keys[0] => $values);
     }
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     $this->values = $values;
     // Update any existing property objects.
     foreach ($this->properties as $name => $property) {
@@ -63,6 +59,10 @@ public function setValue($values, $notify = TRUE) {
       $property->setValue($value, FALSE);
       unset($this->values[$name]);
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
@@ -83,10 +83,6 @@ public function __get($name) {
    * Overrides \Drupal\Core\TypedData\Type\Map::set().
    */
   public function set($property_name, $value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     // For defined properties there is either a property object or a plain
     // value that needs to be updated.
     if (isset($this->properties[$property_name])) {
@@ -96,6 +92,10 @@ public function set($property_name, $value, $notify = TRUE) {
     else {
       $this->values[$property_name] = $value;
     }
+    // Directly notify ourselves.
+    if ($notify) {
+      $this->onChange($property_name);
+    }
   }
 
   /**
@@ -134,6 +134,8 @@ public function onChange($property_name) {
     }
     // Remove the plain value, such that any further __get() calls go via the
     // updated property object.
-    unset($this->values[$property_name]);
+    if (isset($this->properties[$property_name])) {
+      unset($this->values[$property_name]);
+    }
   }
 }
\ No newline at end of file
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityDeriver.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityDeriver.php
new file mode 100644
index 0000000..2ad896a
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityDeriver.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Field\Type\EntityDeriver.
+ */
+
+namespace Drupal\Core\Entity\Field\Type;
+
+use Drupal\Component\Plugin\Derivative\DerivativeInterface;
+
+/**
+ * Cares about registering data types for each entity type and entity bundle.
+ *
+ * @see \Drupal\Core\Entity\Field\Type\AbstractEntity
+ */
+class EntityDeriver implements DerivativeInterface {
+
+  /**
+   * List of derivative definitions.
+   *
+   * @var array
+   */
+  protected $derivatives = array();
+
+  /**
+   * Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinition().
+   */
+  public function getDerivativeDefinition($derivative_id, array $base_plugin_definition) {
+    if (!empty($this->derivatives) && !empty($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+    $this->getDerivativeDefinitions($base_plugin_definition);
+    if (isset($this->derivatives[$derivative_id])) {
+      return $this->derivatives[$derivative_id];
+    }
+  }
+
+  /**
+   * Implements \Drupal\Component\Plugin\Derivative\DerivativeInterface::getDerivativeDefinitions().
+   */
+  public function getDerivativeDefinitions(array $base_plugin_definition) {
+    // Also keep the 'entity' defined as is.
+    $this->derivatives[''] = $base_plugin_definition;
+    // Add definitions for each entity type and bundle.
+    foreach (entity_get_info() as $entity_type => $info) {
+      $this->derivatives[$entity_type] = array(
+        'label' => $info['label'],
+        'class' => $info['class'],
+        'constraints' => array('EntityType' => $entity_type),
+      ) + $base_plugin_definition;
+
+      // Incorporate the bundles as entity:$entity_type:$bundle, if any.
+      foreach (entity_get_bundles($entity_type) as $bundle => $bundle_info) {
+        $this->derivatives[$entity_type . ':' . $bundle] = array(
+          'label' => $bundle_info['label'],
+          'class' => $info['class'],
+          'constraints' => array(
+            'EntityType' => $entity_type,
+            'Bundle' => $bundle,
+          ),
+        ) + $base_plugin_definition;
+      }
+    }
+    return $this->derivatives;
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityReference.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityReference.php
new file mode 100644
index 0000000..defc6ea
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReference.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Field\Type\EntityReference.
+ */
+
+namespace Drupal\Core\Entity\Field\Type;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\DataReferenceBase;
+use InvalidArgumentException;
+
+/**
+ * Defines an 'entity_reference' data type.
+ *
+ * This serves as 'entity' property of entity reference field items and gets
+ * its value set from the parent, i.e. LanguageItem.
+ *
+ * The plain value of this reference is the entity object, i.e. an instance of
+ * \Drupal\Core\Entity\EntityInterface. For setting the value the entity object
+ * or the entity ID may be passed, whereas passing the ID is only supported if
+ * an 'entity type' constraint is specified.
+ *
+ * Some supported constraints (below the definition's 'constraints' key) are:
+ *  - EntityType: The entity type. Required.
+ *  - Bundle: (optional) The bundle or an array of possible bundles.
+ */
+class EntityReference extends DataReferenceBase {
+
+  /**
+   * The entity ID.
+   *
+   * @var integer|string
+   */
+  protected $id;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetDefinition() {
+    $definition = array(
+      'type' => 'entity',
+    );
+    if (isset($this->definition['constraints']['EntityType'])) {
+      $definition['type'] .= ':' . $this->definition['constraints']['EntityType'];
+    }
+    if (isset($this->definition['constraints']['Bundle']) && is_string($this->definition['constraints']['Bundle'])) {
+      $definition['type'] .= ':' . $this->definition['constraints']['Bundle'];
+    }
+    return $definition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTarget() {
+    if (!isset($this->target) && isset($this->id)) {
+      // If we have a valid reference, return the entity object which is typed
+      // data itself.
+      $this->target = entity_load($this->definition['constraints']['EntityType'], $this->id);
+    }
+    return $this->target;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetIdentifier() {
+    if (isset($this->id)) {
+      return $this->id;
+    }
+    elseif ($entity = $this->getValue()) {
+      return $entity->id();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|null|false
+   */
+  public function getValue() {
+    // Entities are already typed data, so just return that.
+    return $this->getTarget();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * Both the entity ID and the entity object may be passed as value.
+   */
+  public function setValue($value, $notify = TRUE) {
+    unset($this->target);
+    unset($this->id);
+
+    // Support passing in the entity ID.
+    if (!isset($value) || $value instanceof EntityInterface) {
+      $this->target = $value;
+    }
+    elseif (!is_scalar($value) || empty($this->definition['constraints']['EntityType'])) {
+      throw new InvalidArgumentException('Value is not a valid entity.');
+    }
+    else {
+      $this->id = $value;
+    }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getString() {
+    if ($entity = $this->getValue()) {
+      return $entity->label();
+    }
+    return '';
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
index 5b78a05..84082f1 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
@@ -11,10 +11,13 @@
 use Drupal\Core\TypedData\TypedDataInterface;
 
 /**
- * Defines the 'entity_reference' entity field item.
+ * Defines the 'entity_reference_item' entity field item.
  *
- * Required settings (below the definition's 'settings' key) are:
- *  - target_type: The entity type to reference.
+ * Supported settings (below the definition's 'settings' key) are:
+ * - target_type: The entity type to reference. Required.
+ * - target_bundle: (optional): If set, restricts the entity bundles which may
+ *   may be referenced. May be set to an single bundle, or to an array of
+ *   allowed bundles.
  */
 class EntityReferenceItem extends FieldItemBase {
 
@@ -31,11 +34,11 @@ class EntityReferenceItem extends FieldItemBase {
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
    */
   public function getPropertyDefinitions() {
-    // Definitions vary by entity type, so key them by entity type.
-    $target_type = $this->definition['settings']['target_type'];
+    // Definitions vary by settings, so key them accordingly.
+    $key = implode(',', $this->definition['settings']);
 
-    if (!isset(self::$propertyDefinitions[$target_type])) {
-      static::$propertyDefinitions[$target_type]['target_id'] = array(
+    if (!isset(self::$propertyDefinitions[$key])) {
+      static::$propertyDefinitions[$key]['target_id'] = array(
         // @todo: Lookup the entity type's ID data type and use it here.
         'type' => 'integer',
         'label' => t('Entity ID'),
@@ -43,20 +46,22 @@ public function getPropertyDefinitions() {
           'Range' => array('min' => 0),
         ),
       );
-      static::$propertyDefinitions[$target_type]['entity'] = array(
-        'type' => 'entity',
+      static::$propertyDefinitions[$key]['entity'] = array(
+        'type' => 'entity_reference',
         'constraints' => array(
-          'EntityType' => $target_type,
+          'EntityType' => $this->definition['settings']['target_type'],
         ),
         'label' => t('Entity'),
         'description' => t('The referenced entity'),
         // The entity object is computed out of the entity ID.
         'computed' => TRUE,
         'read-only' => FALSE,
-        'settings' => array('id source' => 'target_id'),
       );
+      if (isset($this->definition['settings']['target_bundle'])) {
+        static::$propertyDefinitions[$key]['entity']['constraints']['Bundle'] = $this->definition['settings']['target_bundle'];
+      }
     }
-    return static::$propertyDefinitions[$target_type];
+    return static::$propertyDefinitions[$key];
   }
 
   /**
@@ -87,15 +92,35 @@ public function __isset($property_name) {
    * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
    */
   public function setValue($values, $notify = TRUE) {
-    // Treat the values as property value of the entity property, if no array is
-    // given.
     if (isset($values) && !is_array($values)) {
-      $values = array('entity' => $values);
+      // Directly update the property instead of invoking the parent, so it can
+      // handle objects and IDs.
+      $this->properties['entity']->setValue($values, $notify);
+      // If notify was FALSE, ensure the target_id property gets synched.
+      if (!$notify) {
+        $this->set('target_id', $this->properties['entity']->getTargetIdentifier(), FALSE);
+      }
     }
-    // Make sure that the 'entity' property gets set as 'target_id'.
-    if (isset($values['target_id']) && !isset($values['entity'])) {
-      $values['entity'] = $values['target_id'];
+    else {
+      // Make sure that the 'entity' property gets set as 'target_id'.
+      if (isset($values['target_id']) && !isset($values['entity'])) {
+        $values['entity'] = $values['target_id'];
+      }
+      parent::setValue($values, $notify);
     }
-    parent::setValue($values, $notify);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    // Make sure that the target ID and the target property stay in sync.
+    if ($property_name == 'target_id') {
+      $this->properties['entity']->setValue($this->target_id, FALSE);
+    }
+    elseif ($property_name == 'entity') {
+      $this->set('target_id', $this->properties['entity']->getTargetIdentifier(), FALSE);
+    }
+    parent::onChange($property_name);
   }
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
index 4dc7657..bfdf497 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
@@ -73,11 +73,11 @@ public function getValue() {
    * Overrides \Drupal\Core\TypedData\TypedData::setValue().
    */
   public function setValue($values, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
+    $this->fields = $values;
+    // Notify the parent of any changes.
     if ($notify && isset($this->parent)) {
       $this->parent->onChange($this->name);
     }
-    $this->fields = $values;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
deleted file mode 100644
index eb6ae7d..0000000
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
+++ /dev/null
@@ -1,219 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\Entity\Field\Type\EntityWrapper.
- */
-
-namespace Drupal\Core\Entity\Field\Type;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\TypedData;
-use Drupal\Core\TypedData\TypedDataInterface;
-use ArrayIterator;
-use IteratorAggregate;
-use InvalidArgumentException;
-
-/**
- * Defines an 'entity' data type, e.g. the computed 'entity' property of entity references.
- *
- * This object wraps the regular entity object and implements the
- * ComplexDataInterface by forwarding most of its methods to the wrapped entity
- * (if set).
- *
- * The plain value of this wrapper is the entity object, i.e. an instance of
- * Drupal\Core\Entity\EntityInterface. For setting the value the entity object
- * or the entity ID may be passed, whereas passing the ID is only supported if
- * an 'entity type' constraint is specified.
- *
- * Supported constraints (below the definition's 'constraints' key) are:
- *  - 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
- *    the entity object.
- */
-class EntityWrapper extends TypedData implements IteratorAggregate, ComplexDataInterface {
-
-  /**
-   * The referenced entity type.
-   *
-   * @var string
-   */
-  protected $entityType;
-
-  /**
-   * The entity ID if no 'id source' is used.
-   *
-   * @var string
-   */
-  protected $id;
-
-  /**
-   * If set, a new entity to create and reference.
-   *
-   * @var \Drupal\Core\Entity\EntityInterface
-   */
-  protected $newEntity;
-
-  /**
-   * Overrides TypedData::__construct().
-   */
-  public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
-    parent::__construct($definition, $name, $parent);
-    $this->entityType = isset($this->definition['constraints']['EntityType']) ? $this->definition['constraints']['EntityType'] : NULL;
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getValue().
-   */
-  public function getValue() {
-    if (isset($this->newEntity)) {
-      return $this->newEntity;
-    }
-    if (!empty($this->definition['settings']['id source'])) {
-      $this->id = $this->parent->__get($this->definition['settings']['id source']);
-    }
-    return $this->id ? entity_load($this->entityType, $this->id) : NULL;
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::setValue().
-   *
-   * Both the entity ID and the entity object may be passed as value.
-   */
-  public function setValue($value, $notify = TRUE) {
-    // Support passing in the entity object. If it's not yet saved we have
-    // to store the whole entity such that it could be saved later on.
-    if ($value instanceof EntityInterface && $value->isNew()) {
-      $this->newEntity = $value;
-      $this->entityType = $value->entityType();
-      $value = FALSE;
-    }
-    elseif ($value instanceof EntityInterface) {
-      $this->entityType = $value->entityType();
-      $value = $value->id();
-      unset($this->newEntity);
-    }
-    elseif (isset($value) && !(is_scalar($value) && !empty($this->definition['constraints']['EntityType']))) {
-      throw new InvalidArgumentException('Value is not a valid entity.');
-    }
-    // Update the 'id source' property, if given.
-    if (!empty($this->definition['settings']['id source'])) {
-      $this->parent->__set($this->definition['settings']['id source'], $value, $notify);
-    }
-    else {
-      // Notify the parent of any changes to be made.
-      if ($notify && isset($this->parent)) {
-        $this->parent->onChange($this->name);
-      }
-      $this->id = $value;
-    }
-  }
-
-  /**
-   * Overrides \Drupal\Core\TypedData\TypedData::getString().
-   */
-  public function getString() {
-    if ($entity = $this->getValue()) {
-      return $entity->label();
-    }
-    return '';
-  }
-
-  /**
-   * Implements \IteratorAggregate::getIterator().
-   */
-  public function getIterator() {
-    if ($entity = $this->getValue()) {
-      return $entity->getIterator();
-    }
-    return new ArrayIterator(array());
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::get().
-   */
-  public function get($property_name) {
-    // @todo: Allow navigating through the tree without data as well.
-    if ($entity = $this->getValue()) {
-      return $entity->get($property_name);
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
-   */
-  public function set($property_name, $value, $notify = TRUE) {
-    $this->get($property_name)->setValue($value, FALSE);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getProperties().
-   */
-  public function getProperties($include_computed = FALSE) {
-    if ($entity = $this->getValue()) {
-      return $entity->getProperties($include_computed);
-    }
-    return array();
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinition().
-   */
-  public function getPropertyDefinition($name) {
-    $definitions = $this->getPropertyDefinitions();
-    if (isset($definitions[$name])) {
-      return $definitions[$name];
-    }
-    else {
-      return FALSE;
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-    // @todo: Support getting definitions if multiple bundles are specified.
-    return \Drupal::entityManager()->getStorageController($this->entityType)->getFieldDefinitions($this->definition['constraints']);
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyValues().
-   */
-  public function getPropertyValues() {
-    if ($entity = $this->getValue()) {
-      return $entity->getPropertyValues();
-    }
-    return array();
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::setPropertyValues().
-   */
-  public function setPropertyValues($values) {
-    if ($entity = $this->getValue()) {
-      $entity->setPropertyValues($values);
-    }
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::isEmpty().
-   */
-  public function isEmpty() {
-    return !$this->getValue();
-  }
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::onChange().
-   */
-  public function onChange($property_name) {
-    // Notify the parent of changes.
-    if (isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
-  }
-}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index 4829bb7..528b67e 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -60,10 +60,6 @@ public function filterEmptyValues() {
    * Overrides \Drupal\Core\TypedData\ItemList::setValue().
    */
   public function setValue($values, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     if (!isset($values) || $values === array()) {
       $this->list = $values;
     }
@@ -91,6 +87,10 @@ public function setValue($values, $notify = TRUE) {
         }
       }
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
index 2db12e1..f6f24a6 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
@@ -35,12 +35,12 @@ public function getPropertyDefinitions() {
         'label' => t('Language code'),
       );
       static::$propertyDefinitions['language'] = array(
-        'type' => 'language',
+        'type' => 'language_reference',
         'label' => t('Language object'),
+        'description' => t('The referenced language'),
         // The language object is retrieved via the language code.
         'computed' => TRUE,
         'read-only' => FALSE,
-        'settings' => array('langcode source' => 'value'),
       );
     }
     return static::$propertyDefinitions;
@@ -50,15 +50,35 @@ public function getPropertyDefinitions() {
    * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
    */
   public function setValue($values, $notify = TRUE) {
-    // Treat the values as property value of the language property, if no array
-    // is given.
     if (isset($values) && !is_array($values)) {
-      $values = array('language' => $values);
+      // Directly update the property instead of invoking the parent, so it can
+      // handle objects and IDs.
+      $this->properties['language']->setValue($values, $notify);
+      // If notify was FALSE, ensure the value property gets synched.
+      if (!$notify) {
+        $this->set('value', $this->properties['language']->getTargetIdentifier(), FALSE);
+      }
     }
-    // Make sure that the 'language' property gets set as 'value'.
-    if (isset($values['value']) && !isset($values['language'])) {
-      $values['language'] = $values['value'];
+    else {
+      // Make sure that the 'language' property gets set as 'value'.
+      if (isset($values['value']) && !isset($values['language'])) {
+        $values['language'] = $values['value'];
+      }
+      parent::setValue($values, $notify);
     }
-    parent::setValue($values, $notify);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    // Make sure that the value and the language property stay in sync.
+    if ($property_name == 'value') {
+      $this->properties['language']->setValue($this->value, FALSE);
+    }
+    elseif ($property_name == 'language') {
+      $this->set('value', $this->properties['language']->getTargetIdentifier(), FALSE);
+    }
+    parent::onChange($property_name);
   }
 }
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/LanguageReference.php b/core/lib/Drupal/Core/Entity/Field/Type/LanguageReference.php
new file mode 100644
index 0000000..d422229
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Field/Type/LanguageReference.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Field\Type\LanguageReference.
+ */
+
+namespace Drupal\Core\Entity\Field\Type;
+
+use Drupal\Core\TypedData\DataReferenceBase;
+
+/**
+ * Defines the 'language_reference' data type.
+ *
+ * This serves as 'language' property of entity reference field items and gets
+ * its value set from the parent, i.e. LanguageItem.
+ *
+ * The plain value is the language object, i.e. an instance of
+ * \Drupal\Core\Language\Language. For setting the value the language object or
+ * the language code as string may be passed.
+ */
+class LanguageReference extends DataReferenceBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetDefinition() {
+    return array(
+      'type' => 'language',
+    );
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 595064e..a681ffe 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -128,7 +128,7 @@ public function isEmpty();
   /**
    * React to changes to a child property.
    *
-   * Note that this is invoked before any changes are applied.
+   * Note that this is invoked after any changes have been applied.
    *
    * @param $property_name
    *   The name of the property which is changed.
diff --git a/core/lib/Drupal/Core/TypedData/DataDefinitionException.php b/core/lib/Drupal/Core/TypedData/DataDefinitionException.php
new file mode 100644
index 0000000..02178a3
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataDefinitionException.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\MissingContextException.
+ */
+
+namespace Drupal\Core\TypedData;
+
+use Exception;
+
+/**
+ * Exception thrown when a data definition lacks required information.
+ */
+class DataDefinitionException extends Exception {
+
+}
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceBase.php b/core/lib/Drupal/Core/TypedData/DataReferenceBase.php
new file mode 100644
index 0000000..939e00c
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceBase.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataReferenceBase.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Base class for typed data references.
+ *
+ * Implementing classes have to implement at least
+ * \Drupal\Core\TypedData\DataReferenceInterface::getTargetDefinition() and
+ * \Drupal\Core\TypedData\DataReferenceInterface::getTargetIdentifier().
+ */
+abstract class DataReferenceBase extends TypedData implements DataReferenceInterface  {
+
+  /**
+   * The referenced data.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataInterface
+   */
+  protected $target;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTarget() {
+    return $this->target;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getValue() {
+    return $this->getTarget()->getValue();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setValue($value, $notify = TRUE) {
+    $this->target = \Drupal::typedData()->create($this->getTargetDefinition(), $value);
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getString() {
+    return (string) $this->getType() . ':' . $this->getTargetIdentifier();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTargetIdentifier() {
+    $target = $this->getTarget();
+    return isset($target) ? $target->id() : NULL;
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php
new file mode 100644
index 0000000..8a7ac82
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/DataReferenceInterface.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\DataReferenceInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for typed data references.
+ */
+interface DataReferenceInterface  {
+
+  /**
+   * Gets the data definition of the referenced data.
+   *
+   * @return array
+   *   The data definition of the referenced data.
+   */
+  public function getTargetDefinition();
+
+  /**
+   * Gets the referenced data.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface|null|false
+   *   The referenced typed data object, or NULL if the reference is unset, or
+   *   FALSE if the reference is invalid; i.e., the referenced is stale.
+   */
+  public function getTarget();
+
+  /**
+   * Gets the identifier of the referenced data.
+   *
+   * @return int|string|null
+   *   The identifier of the referenced data, or NULL if the reference is unset.
+   */
+  public function getTargetIdentifier();
+}
diff --git a/core/lib/Drupal/Core/TypedData/IdentifiableInterface.php b/core/lib/Drupal/Core/TypedData/IdentifiableInterface.php
new file mode 100644
index 0000000..09f8c92
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/IdentifiableInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\IdentifiableInterface.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * Interface for identifiable typed data.
+ */
+interface IdentifiableInterface {
+
+  /**
+   * Returns the identifier.
+   *
+   * @return string|int|null
+   *   The object identifier, or NULL if the object does not yet have an identifier.
+   */
+  public function id();
+}
diff --git a/core/lib/Drupal/Core/TypedData/ItemList.php b/core/lib/Drupal/Core/TypedData/ItemList.php
index ee69432..9b39e83 100644
--- a/core/lib/Drupal/Core/TypedData/ItemList.php
+++ b/core/lib/Drupal/Core/TypedData/ItemList.php
@@ -42,10 +42,6 @@ public function getValue() {
    *   An array of values of the field items, or NULL to unset the field.
    */
   public function setValue($values, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     if (!isset($values) || $values === array()) {
       $this->list = $values;
     }
@@ -72,6 +68,10 @@ public function setValue($values, $notify = TRUE) {
         }
       }
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 2711cd4..451e1af 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -41,7 +41,7 @@ public function getItemDefinition();
   /**
    * React to changes to a child item.
    *
-   * Note that this is invoked before any changes are applied.
+   * Note that this is invoked after any changes have been applied.
    *
    * @param $delta
    *   The delta of the item which is changed.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php
index f9c4985..b58a8e2 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Binary.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Binary.php
@@ -51,10 +51,6 @@ public function getValue() {
    * Supports a PHP file resource or a (absolute) stream resource URI as value.
    */
   public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     if (!isset($value)) {
       $this->handle = NULL;
       $this->uri = NULL;
@@ -68,6 +64,10 @@ public function setValue($value, $notify = TRUE) {
     else {
       $this->handle = $value;
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php
index c9b8c6b..20e27fe 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Date.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Date.php
@@ -32,10 +32,6 @@ class Date extends TypedData {
    * Overrides TypedData::setValue().
    */
   public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     // Don't try to create a date from an empty value.
     // It would default to the current time.
     if (!isset($value)) {
@@ -44,5 +40,9 @@ public function setValue($value, $notify = TRUE) {
     else {
       $this->value = $value instanceOf DrupalDateTime ? $value : new DrupalDateTime($value);
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php
index 52ba97d..64e1f4a 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Duration.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Duration.php
@@ -32,10 +32,6 @@ class Duration extends TypedData {
    * Overrides TypedData::setValue().
    */
   public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     // Catch any exceptions thrown due to invalid values being passed.
     try {
       if ($value instanceof DateInterval || !isset($value)) {
@@ -61,6 +57,10 @@ public function setValue($value, $notify = TRUE) {
       // validation fail.
       $this->value = $e;
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/Type/Language.php b/core/lib/Drupal/Core/TypedData/Type/Language.php
index b910216..d5c7b24 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Language.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Language.php
@@ -7,9 +7,9 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use InvalidArgumentException;
-use Drupal\Core\Language\Language as LanguageObject;
+use Drupal\Core\TypedData\IdentifiableInterface;
 use Drupal\Core\TypedData\TypedData;
+use InvalidArgumentException;
 
 /**
  * Defines the 'language' data type.
@@ -17,15 +17,8 @@
  * The plain value of a language is the language object, i.e. an instance of
  * \Drupal\Core\Language\Language. For setting the value the language object or
  * the language code as string may be passed.
- *
- * Optionally, this class may be used as computed property, see the supported
- * settings below. E.g., it is used as 'language' property of language items.
- *
- * Supported settings (below the definition's 'settings' key) are:
- *  - langcode source: If used as computed property, the langcode property used
- *    to load the language object.
  */
-class Language extends TypedData {
+class Language extends TypedData implements IdentifiableInterface {
 
   /**
    * The language code of the language if no 'langcode source' is used.
@@ -35,16 +28,20 @@ class Language extends TypedData {
   protected $langcode;
 
   /**
+   * @var \Drupal\Core\Language
+   */
+  protected $language;
+
+  /**
    * Overrides TypedData::getValue().
+   *
+   * @return \Drupal\Core\Language\Language|null
    */
   public function getValue() {
-    if (!empty($this->definition['settings']['langcode source'])) {
-      $this->langcode = $this->parent->__get($this->definition['settings']['langcode source']);
-    }
-   if ($this->langcode) {
-      $language = language_load($this->langcode);
-      return $language ?: new LanguageObject(array('langcode' => $this->langcode));
+    if (!isset($this->language) && $this->langcode) {
+      $this->language = language_load($this->langcode);
     }
+    return $this->language;
   }
 
   /**
@@ -55,22 +52,18 @@ public function getValue() {
   public function setValue($value, $notify = TRUE) {
     // Support passing language objects.
     if (is_object($value)) {
-      $value = $value->langcode;
+      $this->language = $value;
     }
     elseif (isset($value) && !is_scalar($value)) {
       throw new InvalidArgumentException('Value is no valid langcode or language object.');
     }
-    // Update the 'langcode source' property, if given.
-    if (!empty($this->definition['settings']['langcode source'])) {
-      $this->parent->__set($this->definition['settings']['langcode source'], $value, $notify);
-    }
     else {
-      // Notify the parent of any changes to be made.
-      if ($notify && isset($this->parent)) {
-        $this->parent->onChange($this->name);
-      }
       $this->langcode = $value;
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
@@ -80,4 +73,17 @@ public function getString() {
     $language = $this->getValue();
     return $language ? $language->name : '';
   }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function id() {
+    if (isset($this->langcode)) {
+      return $this->langcode;
+    }
+    elseif (isset($this->language)) {
+      return $this->language->langcode;
+    }
+  }
+
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Map.php b/core/lib/Drupal/Core/TypedData/Type/Map.php
index 43e3790..f2d0941 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Map.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Map.php
@@ -78,10 +78,6 @@ public function setValue($values, $notify = TRUE) {
     if (isset($values) && !is_array($values)) {
       throw new \InvalidArgumentException("Invalid values given. Values must be represented as an associative array.");
     }
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     $this->values = $values;
 
     // Update any existing property objects.
@@ -92,6 +88,10 @@ public function setValue($values, $notify = TRUE) {
       }
       $property->setValue($value, FALSE);
     }
+    // Notify the parent of any changes.
+    if ($notify && isset($this->parent)) {
+      $this->parent->onChange($this->name);
+    }
   }
 
   /**
@@ -125,16 +125,16 @@ public function get($property_name) {
    * Implements \Drupal\Core\TypedData\ComplexDataInterface::set().
    */
   public function set($property_name, $value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
-    if ($notify && isset($this->parent)) {
-      $this->parent->onChange($this->name);
-    }
     if ($this->getPropertyDefinition($property_name)) {
-      $this->get($property_name)->setValue($value);
+      $this->get($property_name)->setValue($value, $notify);
     }
     else {
       // Just set the plain value, which allows adding a new entry to the map.
       $this->values[$property_name] = $value;
+      // Directly notify ourselves.
+      if ($notify) {
+        $this->onChange($property_name, $value);
+      }
     }
   }
 
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
index 7ea33f0..022abd4 100644
--- a/core/lib/Drupal/Core/TypedData/TypedData.php
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -81,11 +81,11 @@ public function getValue() {
    * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue().
    */
   public function setValue($value, $notify = TRUE) {
-    // Notify the parent of any changes to be made.
+    $this->value = $value;
+    // Notify the parent of any changes.
     if ($notify && isset($this->parent)) {
       $this->parent->onChange($this->name);
     }
-    $this->value = $value;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index 6e6d001..7344cf3 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -10,6 +10,7 @@
 use InvalidArgumentException;
 use Drupal\Component\Plugin\Discovery\ProcessDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\HookDiscovery;
 use Drupal\Core\TypedData\Validation\MetadataFactory;
@@ -58,8 +59,8 @@ class TypedDataManager extends PluginManagerBase {
   public function __construct() {
     $this->discovery = new HookDiscovery('data_type_info');
     $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
+    $this->discovery = new DerivativeDiscoveryDecorator($this->discovery);
     $this->discovery = new CacheDecorator($this->discovery, 'typed_data:types');
-
     $this->factory = new TypedDataFactory($this->discovery);
   }
 
@@ -208,18 +209,16 @@ public function getInstance(array $options) {
    * @todo: Add type-hinting to $object once entities implement the
    *   TypedDataInterface.
    */
-  public function getPropertyInstance($object, $property_name, $value = NULL) {
-    if ($root = $object->getRoot()) {
-      $key = $root->getType() . ':' . $object->getPropertyPath() . '.';
-      // If we are creating list items, we always use 0 in the key as all list
-      // items look the same.
-      $key .= is_numeric($property_name) ? 0 : $property_name;
-    }
-    else {
-      // Missing context, thus we cannot determine a unique key for prototyping.
-      // Fall back to create a new prototype on each call.
-      $key = FALSE;
+  public function getPropertyInstance(TypedDataInterface $object, $property_name, $value = NULL) {
+    $definition = $object->getRoot()->getDefinition();
+    $key = $definition['type'];
+    if (isset($definition['settings'])) {
+      $key .= ':' . implode(',', $definition['settings']);
     }
+    $key .= ':' . $object->getPropertyPath() . '.';
+    // If we are creating list items, we always use 0 in the key as all list
+    // items look the same.
+    $key .= is_numeric($property_name) ? 0 : $property_name;
 
     // Make sure we have a prototype. Then, clone the prototype and set object
     // specific values, i.e. the value and the context.
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php
index f6ea289..ebd844b 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraint.php
@@ -20,7 +20,7 @@
  * @Plugin(
  *   id = "Bundle",
  *   label = @Translation("Bundle", context = "Validation"),
- *   type = "entity"
+ *   type = { "entity", "entity_reference" }
  * )
  */
 class BundleConstraint extends Constraint {
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php
index a84251e..42f09ca 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/BundleConstraintValidator.php
@@ -18,10 +18,7 @@ class BundleConstraintValidator extends ConstraintValidator {
   /**
    * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
    */
-  public function validate($typed_data, Constraint $constraint) {
-    // If the entity is contained in a reference, unwrap it first.
-    $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE;
-
+  public function validate($entity, Constraint $constraint) {
     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/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php
index 1d38ae5..d480fa1 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraint.php
@@ -20,7 +20,7 @@
  * @Plugin(
  *   id = "EntityType",
  *   label = @Translation("Entity type", context = "Validation"),
- *   type = "entity"
+ *   type = { "entity", "entity_reference" }
  * )
  */
 class EntityTypeConstraint extends Constraint {
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
index 31c7cb2..304a85c 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/EntityTypeConstraintValidator.php
@@ -19,9 +19,7 @@ class EntityTypeConstraintValidator extends ConstraintValidator {
   /**
    * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
    */
-  public function validate($typed_data, Constraint $constraint) {
-    // If the entity is contained in a reference, unwrap it first.
-    $entity = isset($typed_data) && !($typed_data instanceof EntityInterface) ? $typed_data->getValue() : FALSE;
+  public function validate($entity, Constraint $constraint) {
 
     if (!empty($entity) && $entity->entityType() != $constraint->type) {
       $this->context->addViolation($constraint->message, array('%type' => $constraint->type));
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index 9895b1f..0ac6171 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -1549,7 +1549,7 @@ function comment_preprocess_block(&$variables) {
 function comment_prepare_author(Comment $comment) {
   // The account has been pre-loaded by CommentRenderController::buildContent().
   $account = $comment->uid->entity;
-  if (!$account) {
+  if (empty($account->uid)) {
     $account = entity_create('user', array('uid' => 0, 'name' => $comment->name->value, 'homepage' => $comment->homepage->value));
   }
   return $account;
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 a387cf4..11e34db 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
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\Query\QueryException;
+use Drupal\Core\Entity\Field\Type\EntityReference;
 
 /**
  * Adds tables and fields to the SQL entity query.
@@ -192,7 +193,7 @@ function addField($field, $type, $langcode) {
           $next_index_prefix = $relationship_specifier;
         }
         // Check for a valid relationship.
-        if (isset($propertyDefinitions[$relationship_specifier]['constraints']['EntityType']) && isset($propertyDefinitions[$relationship_specifier]['settings']['id source'])) {
+        if (isset($propertyDefinitions[$relationship_specifier]['constraints']['EntityType']) && $entity->{$specifier}->get('entity') instanceof EntityReference) {
           // If it is, use the entity type.
           $entity_type = $propertyDefinitions[$relationship_specifier]['constraints']['EntityType'];
           $entity_info = entity_get_info($entity_type);
diff --git a/core/modules/file/lib/Drupal/file/Type/FileItem.php b/core/modules/file/lib/Drupal/file/Type/FileItem.php
index 0b2c74b..6409115 100644
--- a/core/modules/file/lib/Drupal/file/Type/FileItem.php
+++ b/core/modules/file/lib/Drupal/file/Type/FileItem.php
@@ -32,6 +32,9 @@ public function getPropertyDefinitions() {
       static::$propertyDefinitions['fid'] = array(
         'type' => 'integer',
         'label' => t('Referenced file id.'),
+        'constraints' => array(
+          'Range' => array('min' => 0),
+        ),
       );
       static::$propertyDefinitions['display'] = array(
         'type' => 'boolean',
@@ -42,7 +45,7 @@ public function getPropertyDefinitions() {
         'label' => t('A description of the file.'),
       );
       static::$propertyDefinitions['entity'] = array(
-        'type' => 'entity',
+        'type' => 'entity_reference',
         'constraints' => array(
           'EntityType' => 'file',
         ),
@@ -61,15 +64,36 @@ public function getPropertyDefinitions() {
    * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
    */
   public function setValue($values, $notify = TRUE) {
-    // Treat the values as property value of the entity property, if no array is
-    // given.
     if (isset($values) && !is_array($values)) {
-      $values = array('entity' => $values);
+      // Directly update the property instead of invoking the parent, so it can
+      // handle objects and IDs.
+      $this->properties['entity']->setValue($values, $notify);
+      // If notify was FALSE, ensure the fid property gets synched.
+      if (!$notify) {
+        $this->set('fid', $this->properties['entity']->getTargetIdentifier(), FALSE);
+      }
+    }
+    else {
+      // Make sure that the 'entity' property gets set as 'fid'.
+      if (isset($values['fid']) && !isset($values['entity'])) {
+        $values['entity'] = $values['fid'];
+      }
+      parent::setValue($values, $notify);
+    }
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    // Make sure that the target ID and the target property stay in sync.
+    if ($property_name == 'fid') {
+      $this->properties['entity']->setValue($this->fid, FALSE);
     }
-    // Make sure that the 'entity' property gets set as 'fid'.
-    if (isset($values['fid']) && !isset($values['entity'])) {
-      $values['entity'] = $values['fid'];
+    elseif ($property_name == 'entity') {
+      $this->set('fid', $this->properties['entity']->getTargetIdentifier(), FALSE);
     }
-    parent::setValue($values, $notify);
+    parent::onChange($property_name);
   }
 }
diff --git a/core/modules/image/lib/Drupal/image/Type/ImageItem.php b/core/modules/image/lib/Drupal/image/Type/ImageItem.php
index f6cedf2..104e678 100644
--- a/core/modules/image/lib/Drupal/image/Type/ImageItem.php
+++ b/core/modules/image/lib/Drupal/image/Type/ImageItem.php
@@ -31,6 +31,9 @@ public function getPropertyDefinitions() {
       static::$propertyDefinitions['fid'] = array(
         'type' => 'integer',
         'label' => t('Referenced file id.'),
+        'constraints' => array(
+          'Range' => array('min' => 0),
+        ),
       );
       static::$propertyDefinitions['alt'] = array(
         'type' => 'boolean',
@@ -49,7 +52,7 @@ public function getPropertyDefinitions() {
         'label' => t('The height of the image in pixels.'),
       );
       static::$propertyDefinitions['entity'] = array(
-        'type' => 'entity',
+        'type' => 'entity_reference',
         'constraints' => array(
           'EntityType' => 'file',
         ),
@@ -68,15 +71,35 @@ public function getPropertyDefinitions() {
    * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
    */
   public function setValue($values, $notify = TRUE) {
-    // Treat the values as property value of the entity property, if no array is
-    // given.
     if (isset($values) && !is_array($values)) {
-      $values = array('entity' => $values);
+      // Directly update the property instead of invoking the parent, so it can
+      // handle objects and IDs.
+      $this->properties['entity']->setValue($values, $notify);
+      // If notify was FALSE, ensure the fid property gets synched.
+      if (!$notify) {
+        $this->set('fid', $this->properties['entity']->getTargetIdentifier(), FALSE);
+      }
+    }
+    else {
+      // Make sure that the 'entity' property gets set as 'fid'.
+      if (isset($values['fid']) && !isset($values['entity'])) {
+        $values['entity'] = $values['fid'];
+      }
+      parent::setValue($values, $notify);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    // Make sure that the target ID and the target property stay in sync.
+    if ($property_name == 'fid') {
+      $this->properties['entity']->setValue($this->fid, FALSE);
     }
-    // Make sure that the 'entity' property gets set as 'fid'.
-    if (isset($values['fid']) && !isset($values['entity'])) {
-      $values['entity'] = $values['fid'];
+    elseif ($property_name == 'entity') {
+      $this->set('fid', $this->properties['entity']->getTargetIdentifier(), FALSE);
     }
-    parent::setValue($values, $notify);
+    parent::onChange($property_name);
   }
 }
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
index bc6ae1a..469b7da 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
@@ -97,6 +97,7 @@ function testContentTypeLanguageConfiguration() {
     $this->drupalPost("admin/structure/types/manage/{$type2->type}", $edit, t('Save content type'));
     $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type2->name)));
     $this->drupalLogout();
+    drupal_static_reset('language_list');
 
     // Verify language selection is not present on the node add form.
     $this->drupalLogin($web_user);
@@ -153,6 +154,7 @@ function testContentTypeDirLang() {
     $edit = array();
     $edit['predefined_langcode'] = 'ar';
     $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+    drupal_static_reset('language_list');
 
     // Install Spanish language.
     $edit = array();
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 1aebdd5..85eefd0 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2473,7 +2473,7 @@ function node_access($op, $node, $account = NULL, $langcode = NULL) {
 
   // If no language code was provided, default to the node's langcode.
   if (empty($langcode)) {
-    $langcode = $node->langcode;
+    $langcode = $node->language()->langcode;
     // If the Language module is enabled, try to use the language from content
     // negotiation.
     if (module_exists('language')) {
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 6b74ce3..462d5de 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -339,7 +339,7 @@ protected function assertSave($entity_type) {
   public function testIntrospection() {
     // All entity variations have to have the same results.
     foreach (entity_test_entity_types() as $entity_type) {
-      $this->assertIntrospection($entity_type);
+      $this->checkIntrospection($entity_type);
     }
   }
 
@@ -349,17 +349,11 @@ public function testIntrospection() {
    * @param string $entity_type
    *   The entity type to run the tests with.
    */
-  protected function assertIntrospection($entity_type) {
-    // Test getting metadata upfront, i.e. without having an entity object.
-    $definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'EntityType' => $entity_type,
-      ),
-      'label' => 'Test entity',
-    );
-    $wrapped_entity = $this->container->get('typed_data')->create($definition);
-    $definitions = $wrapped_entity->getPropertyDefinitions($definition);
+  protected function checkIntrospection($entity_type) {
+    // Test getting metadata upfront.
+    // @todo: Make this work without having to create entity objects.
+    $entity = entity_create($entity_type, array());
+    $definitions = $entity->getPropertyDefinitions();
     $this->assertEqual($definitions['name']['type'], 'string_field', $entity_type .': Name field found.');
     $this->assertEqual($definitions['user_id']['type'], 'entity_reference_field', $entity_type .': User field found.');
     $this->assertEqual($definitions['field_test_text']['type'], 'text_field', $entity_type .': Test-text-field field found.');
@@ -378,7 +372,7 @@ protected function assertIntrospection($entity_type) {
 
     $userref_properties = $entity->user_id->getPropertyDefinitions();
     $this->assertEqual($userref_properties['target_id']['type'], 'integer', $entity_type .': Entity id property of the user found.');
-    $this->assertEqual($userref_properties['entity']['type'], 'entity', $entity_type .': Entity reference property of the user found.');
+    $this->assertEqual($userref_properties['entity']['type'], 'entity_reference', $entity_type .': Entity reference property of the user found.');
 
     $textfield_properties = $entity->field_test_text->getPropertyDefinitions();
     $this->assertEqual($textfield_properties['value']['type'], 'string', $entity_type .': String value property of the test-text field found.');
@@ -470,21 +464,12 @@ public function testDataStructureInterfaces() {
    */
   protected function assertDataStructureInterfaces($entity_type) {
     $entity = $this->createTestEntity($entity_type);
-    $entity->save();
-    $entity_definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'EntityType' => $entity_type,
-      ),
-      'label' => 'Test entity',
-    );
-    $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $entity);
 
     // 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);
+    $this->getContainedStrings($entity, 0, $strings);
 
     // @todo: Once the user entity has defined properties this should contain
     // the user name and other user entity strings as well.
@@ -532,17 +517,20 @@ public function getContainedStrings(TypedDataInterface $wrapper, $depth, array &
   public function testEntityConstraintValidation() {
     $entity = $this->createTestEntity('entity_test');
     $entity->save();
-    $entity_definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'EntityType' => 'entity_test',
+    // Create a reference field item and let it reference the entity.
+    $definition = array(
+      'type' => 'entity_reference_field',
+      'settings' => array(
+        'target_type' => 'entity_test',
       ),
       'label' => 'Test entity',
     );
-    $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $entity);
+    $reference_field_item = \Drupal::TypedData()->create($definition);
+    $reference = $reference_field_item->get('entity');
+    $reference->setValue($entity);
 
     // Test validation the typed data object.
-    $violations = $wrapped_entity->validate();
+    $violations = $reference->validate();
     $this->assertEqual($violations->count(), 0);
 
     // Test validating an entity of the wrong type.
@@ -552,30 +540,31 @@ public function testEntityConstraintValidation() {
       'type' => 'page',
       'uid' => $user->id(),
     ));
-    // @todo: EntityWrapper can only handle entities with an id.
-    $node->save();
-    $wrapped_entity->setValue($node);
-    $violations = $wrapped_entity->validate();
+    $reference->setValue($node);
+    $violations = $reference->validate();
     $this->assertEqual($violations->count(), 1);
 
     // Test bundle validation.
-    $entity_definition = array(
-      'type' => 'entity',
-      'constraints' => array(
-        'EntityType' => 'node',
-        'Bundle' => 'article',
+    $definition = array(
+      'type' => 'entity_reference_field',
+      'settings' => array(
+        'target_type' => 'node',
+        'target_bundle' => 'article',
       ),
-      'label' => 'Test node',
     );
-    $wrapped_entity = $this->container->get('typed_data')->create($entity_definition, $node);
-
-    $violations = $wrapped_entity->validate();
+    $reference_field_item = \Drupal::TypedData()->create($definition);
+    $reference = $reference_field_item->get('entity');
+    $reference->setValue($node);
+    $violations = $reference->validate();
     $this->assertEqual($violations->count(), 1);
 
-    $node->type = 'article';
+    $node = entity_create('node', array(
+      'type' => 'article',
+      'uid' => $user->id(),
+    ));
     $node->save();
-    $wrapped_entity->setValue($node);
-    $violations = $wrapped_entity->validate();
+    $reference->setValue($node);
+    $violations = $reference->validate();
     $this->assertEqual($violations->count(), 0);
   }
 
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 54ae84b..a9a35f4 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2252,16 +2252,24 @@ function system_data_type_info() {
       'description' => t('A language object.'),
       'class' => '\Drupal\Core\TypedData\Type\Language',
     ),
+    'language_reference' => array(
+      'label' => t('Language reference'),
+      'class' => '\Drupal\Core\Entity\Field\Type\LanguageReference',
+    ),
     'entity' => array(
       'label' => t('Entity'),
       'description' => t('All kind of entities, e.g. nodes, comments or users.'),
-      'class' => '\Drupal\Core\Entity\Field\Type\EntityWrapper',
+      'derivative' => '\Drupal\Core\Entity\Field\Type\EntityDeriver'
     ),
     'entity_translation' => array(
       'label' => t('Entity translation'),
       'description' => t('A translation of an entity'),
       'class' => '\Drupal\Core\Entity\Field\Type\EntityTranslation',
     ),
+    'entity_reference' => array(
+      'label' => t('Entity reference'),
+      'class' => '\Drupal\Core\Entity\Field\Type\EntityReference',
+    ),
     'boolean_field' => array(
       'label' => t('Boolean field item'),
       'description' => t('An entity field containing a boolean value.'),
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
index b558d97..e87d33f 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Type/TaxonomyTermReferenceItem.php
@@ -32,9 +32,12 @@ public function getPropertyDefinitions() {
       static::$propertyDefinitions['tid'] = array(
         'type' => 'integer',
         'label' => t('Referenced taxonomy term id.'),
+        'constraints' => array(
+          'Range' => array('min' => 0),
+        ),
       );
       static::$propertyDefinitions['entity'] = array(
-        'type' => 'entity',
+        'type' => 'entity_reference',
         'constraints' => array(
           'EntityType' => 'taxonomy_term',
         ),
@@ -53,15 +56,35 @@ public function getPropertyDefinitions() {
    * Overrides \Drupal\Core\Entity\Field\FieldItemBase::get().
    */
   public function setValue($values, $notify = TRUE) {
-    // Treat the values as property value of the entity property, if no array is
-    // given.
     if (isset($values) && !is_array($values)) {
-      $values = array('entity' => $values);
+      // Directly update the property instead of invoking the parent, so it can
+      // handle objects and IDs.
+      $this->properties['entity']->setValue($values, $notify);
+      // If notify was FALSE, ensure the tid property gets synched.
+      if (!$notify) {
+        $this->set('tid', $this->properties['entity']->getTargetIdentifier(), FALSE);
+      }
+    }
+    else {
+      // Make sure that the 'entity' property gets set as 'tid'.
+      if (isset($values['tid']) && !isset($values['entity'])) {
+        $values['entity'] = $values['tid'];
+      }
+      parent::setValue($values, $notify);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onChange($property_name) {
+    // Make sure that the target ID and the target property stay in sync.
+    if ($property_name == 'tid') {
+      $this->properties['entity']->setValue($this->tid, FALSE);
     }
-    // Make sure that the 'entity' property gets set as 'tid'.
-    if (isset($values['tid']) && !isset($values['entity'])) {
-      $values['entity'] = $values['tid'];
+    elseif ($property_name == 'entity') {
+      $this->set('tid', $this->properties['entity']->getTargetIdentifier(), FALSE);
     }
-    parent::setValue($values, $notify);
+    parent::onChange($property_name);
   }
 }
