diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 55a3875..158cac6 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2484,7 +2484,15 @@ function state() {
  * @return Drupal\Core\TypedData\TypedDataManager
  */
 function typed_data() {
-  return drupal_container()->get('typed_data');
+  // Use the advanced drupal_static() pattern, since this is called very often.
+  static $drupal_static_fast;
+  if (!isset($drupal_static_fast)) {
+    $drupal_static_fast['manager'] = &drupal_static(__FUNCTION__);
+  }
+  if (!isset($drupal_static_fast['manager'])) {
+    $drupal_static_fast['manager'] = drupal_container()->get('typed_data');
+  }
+  return $drupal_static_fast['manager'];
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index 330f39c..0d225a2 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Config\Entity;
 
 use Drupal\Core\Entity\Entity;
+use Drupal\Core\TypedData\ContextAwareInterface;
 
 /**
  * Defines a base configuration entity class.
@@ -110,4 +111,58 @@ public static function sort($a, $b) {
     }
     return ($a_weight < $b_weight) ? -1 : 1;
   }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getBCEntity().
+   */
+  public function getBCEntity() {
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getOriginalEntity().
+   */
+  public function getOriginalEntity() {
+    return $this;
+  }
+
+ /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName().
+   */
+  public function getName() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getNamespace().
+   */
+  public function getNamespace() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath().
+   */
+  public function getPropertyPath() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent().
+   */
+  public function getParent() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext().
+   */
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index bf515de..bec638a 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -30,6 +30,7 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
    */
   protected $entityCache;
 
+
   /**
    * Entity type for this controller instance.
    *
@@ -56,6 +57,13 @@ class DatabaseStorageController implements EntityStorageControllerInterface {
   protected $entityFieldInfo;
 
   /**
+   * Static cache of field definitions per bundle.
+   *
+   * @var array
+   */
+  protected $fieldDefinitions;
+
+  /**
    * Additional arguments to pass to hook_TYPE_load().
    *
    * Set before calling Drupal\Core\Entity\DatabaseStorageController::attachLoad().
@@ -680,15 +688,17 @@ public function getFieldDefinitions(array $constraints) {
       }
     }
 
-    $definitions = $this->entityFieldInfo['definitions'];
+    $bundle = !empty($constraints['bundle']) ? $constraints['bundle'] : FALSE;
 
     // Add in per-bundle properties.
-    // @todo: Should this be statically cached as well?
-    if (!empty($constraints['bundle']) && isset($this->entityFieldInfo['bundle map'][$constraints['bundle']])) {
-      $definitions += array_intersect_key($this->entityFieldInfo['optional'], array_flip($this->entityFieldInfo['bundle map'][$constraints['bundle']]));
-    }
+    if (!isset($this->fieldDefinitions[$bundle])) {
+      $this->fieldDefinitions[$bundle] = $this->entityFieldInfo['definitions'];
 
-    return $definitions;
+      if ($bundle && isset($this->entityFieldInfo['bundle map'][$constraints['bundle']])) {
+        $this->fieldDefinitions[$bundle] += array_intersect_key($this->entityFieldInfo['optional'], array_flip($this->entityFieldInfo['bundle map'][$constraints['bundle']]));
+      }
+    }
+    return $this->fieldDefinitions[$bundle];
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
index 10a7a77..898c5b1 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -77,20 +77,17 @@ public function __construct($entityType) {
    *   A new entity object.
    */
   public function create(array $values) {
-    $entity = new $this->entityClass(array(), $this->entityType);
+    // We have to determine the bundle first.
+    $bundle = $this->bundleKey ? $values[$this->bundleKey] : FALSE;
+    $entity = new $this->entityClass(array(), $this->entityType, $bundle);
 
-    // Make sure to set the bundle first.
-    if ($this->bundleKey) {
-      $entity->{$this->bundleKey} = $values[$this->bundleKey];
-      unset($values[$this->bundleKey]);
-    }
     // Set all other given values.
     foreach ($values as $name => $value) {
       $entity->$name = $value;
     }
 
     // Assign a new UUID if there is none yet.
-    if ($this->uuidKey && !isset($entity->{$this->uuidKey})) {
+    if ($this->uuidKey && !isset($entity->{$this->uuidKey}->value)) {
       $uuid = new Uuid();
       $entity->{$this->uuidKey}->value = $uuid->generate();
     }
@@ -109,19 +106,20 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
 
     // Attach fields.
     if ($this->entityInfo['fieldable']) {
+      // Prepare BC compatible entities for field API.
+      $bc_entities = array();
+      foreach ($queried_entities as $key => $entity) {
+        $bc_entities[$key] = $entity->getBCEntity();
+      }
+
       if ($load_revision) {
-        field_attach_load_revision($this->entityType, $queried_entities);
+        field_attach_load_revision($this->entityType, $bc_entities);
       }
       else {
-        field_attach_load($this->entityType, $queried_entities);
+        field_attach_load($this->entityType, $bc_entities);
       }
     }
 
-    // Loading is finished, so disable compatibility mode now.
-    foreach ($queried_entities as $entity) {
-      $entity->setCompatibilityMode(FALSE);
-    }
-
     // Call hook_entity_load().
     foreach (module_implements('entity_load') as $module) {
       $function = $module . '_entity_load';
@@ -150,13 +148,14 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
   protected function mapFromStorageRecords(array $records, $load_revision = FALSE) {
 
     foreach ($records as $id => $record) {
-      $entity = new $this->entityClass(array(), $this->entityType);
-      $entity->setCompatibilityMode(TRUE);
-
+      $values = array();
       foreach ($record as $name => $value) {
-        $entity->{$name}[LANGUAGE_DEFAULT][0]['value'] = $value;
+        // Avoid unnecessary array hierarchies to save memory.
+        $values[$name][LANGUAGE_DEFAULT] = $value;
       }
-      $records[$id] = $entity;
+      $bundle = $this->bundleKey ? $record->{$this->bundleKey} : FALSE;
+      // Turn the record into an entity class.
+      $records[$id] = new $this->entityClass($values, $this->entityType, $bundle);
     }
     return $records;
   }
@@ -179,11 +178,6 @@ public function save(EntityInterface $entity) {
 
       // Create the storage record to be saved.
       $record = $this->maptoStorageRecord($entity);
-      // Update the original values so that the compatibility mode works with
-      // the update values, what is required by field API attachers.
-      // @todo Once field API has been converted to use the Field API, move
-      // this after insert/update hooks.
-      $entity->updateOriginalValues();
 
       if (!$entity->isNew()) {
         if ($entity->isDefaultRevision()) {
@@ -215,6 +209,7 @@ public function save(EntityInterface $entity) {
         $this->postSave($entity, FALSE);
         $this->invokeHook('insert', $entity);
       }
+      $entity->updateOriginalValues();
 
       // Ignore slave server temporarily.
       db_ignore_slave();
@@ -270,8 +265,7 @@ protected function saveRevision(EntityInterface $entity) {
   /**
    * Overrides DatabaseStorageController::invokeHook().
    *
-   * Invokes field API attachers in compatibility mode and disables it
-   * afterwards.
+   * Invokes field API attachers with a BC entity.
    */
   protected function invokeHook($hook, EntityInterface $entity) {
     $function = 'field_attach_' . $hook;
@@ -281,9 +275,7 @@ protected function invokeHook($hook, EntityInterface $entity) {
       $function = 'field_attach_delete_revision';
     }
     if (!empty($this->entityInfo['fieldable']) && function_exists($function)) {
-      $entity->setCompatibilityMode(TRUE);
-      $function($this->entityType, $entity);
-      $entity->setCompatibilityMode(FALSE);
+      $function($this->entityType, $entity->getBCEntity());
     }
 
     // Invoke the hook.
@@ -298,7 +290,15 @@ protected function invokeHook($hook, EntityInterface $entity) {
   protected function mapToStorageRecord(EntityInterface $entity) {
     $record = new \stdClass();
     foreach ($this->entityInfo['schema_fields_sql']['base_table'] as $name) {
-      $record->$name = $entity->$name->value;
+      switch ($entity->$name->get('value')->getType()) {
+        // Store dates using timestamps.
+        case 'date':
+          $record->$name = $entity->$name->value->getTimestamp();
+          break;
+        default:
+          $record->$name = $entity->$name->value;
+          break;
+      }
     }
     return $record;
   }
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 979614f..5384789 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\ContextAwareInterface;
 use IteratorAggregate;
 
 /**
@@ -371,4 +372,58 @@ public function isDefaultRevision($new_value = NULL) {
     }
     return $return;
   }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getBCEntity().
+   */
+  public function getBCEntity() {
+    return $this;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getOriginalEntity().
+   */
+  public function getOriginalEntity() {
+    return $this;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName().
+   */
+  public function getName() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getNamespace().
+   */
+  public function getNamespace() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath().
+   */
+  public function getPropertyPath() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent().
+   */
+  public function getParent() {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext().
+   */
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    // @todo: Replace by EntityNG implementation once all entity types have been
+    // converted to use the entity field API.
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityBCDecorator.php b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
new file mode 100644
index 0000000..d19d5b8
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityBCDecorator.php
@@ -0,0 +1,357 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityBCDecorator.
+ */
+
+namespace Drupal\Core\Entity;
+
+use IteratorAggregate;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\TypedData\ContextAwareInterface;
+
+/**
+ * Implements a decorator providing backwards compatible entity field access.
+ *
+ * Allows using entities converted to the new Entity Field API with the Drupal 7
+ * way of accessing ways fields or properties.
+ *
+ * Note: We access the protected 'values' and 'fields' properties of the entity
+ * via the magic getter - which returns them by reference for us. We do so, as
+ * providing references to this arrays makes $entity->values and $entity->fields
+ * to references itself as well, which is problematic during __clone() (this is
+ * something that would not be easy to fix as an unset() on the variable is
+ * problematic with the magic getter/setter then).
+ */
+class EntityBCDecorator implements IteratorAggregate, EntityInterface {
+
+  /**
+   * The EntityInterface object being decorated.
+   *
+   * @var \Drupal\Core\Entity\EntityInterface
+   */
+  protected $decorated;
+
+  /**
+   * Constructs a Drupal\Core\Entity\EntityCompatibilityDecorator object.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $decorated
+   *   The decorated entity.
+   */
+  function __construct(EntityNG $decorated) {
+    $this->decorated = $decorated;
+  }
+
+  /**
+   * Overrides Entity::getOriginalEntity().
+   */
+  public function getOriginalEntity() {
+    return $this->decorated;
+  }
+
+  /**
+   * Overrides Entity::getBCEntity().
+   */
+  public function getBCEntity() {
+    return $this;
+  }
+
+  /**
+   * Allows direct access to the plain values, as done in Drupal 7.
+   */
+  public function &__get($name) {
+    // Make sure $this->decorated->values reflects the latest values.
+    if (!empty($this->decorated->fields[$name])) {
+      foreach ($this->decorated->fields[$name] as $langcode => $field) {
+        $this->decorated->values[$name][$langcode] = $field->getValue();
+      }
+      // Values might be changed by reference, so remove the field object to
+      // avoid them becoming out of sync.
+      unset($this->decorated->fields[$name]);
+    }
+    // Allow accessing field values in entity default languages other than
+    // LANGUAGE_DEFAULT by mapping the values to LANGUAGE_DEFAULT.
+    $langcode = $this->decorated->language()->langcode;
+    if ($langcode != LANGUAGE_DEFAULT && isset($this->decorated->values[$name][LANGUAGE_DEFAULT]) && !isset($this->decorated->values[$name][$langcode])) {
+      $this->decorated->values[$name][$langcode] = &$this->decorated->values[$name][LANGUAGE_DEFAULT];
+    }
+
+    if (!isset($this->decorated->values[$name])) {
+      $this->decorated->values[$name] = NULL;
+    }
+    return $this->decorated->values[$name];
+  }
+
+  /**
+   * Allows directly writing to the plain values, as done in Drupal 7.
+   */
+  public function __set($name, $value) {
+    if (is_array($value) && $definition = $this->decorated->getPropertyDefinition($name)) {
+      // If field API sets a value with a langcode in entity language, move it
+      // to LANGUAGE_DEFAULT.
+      foreach ($value as $langcode => $data) {
+        if ($langcode != LANGUAGE_DEFAULT && $langcode == $this->decorated->language()->langcode) {
+          $value[LANGUAGE_DEFAULT] = $data;
+          unset($value[$langcode]);
+        }
+      }
+    }
+    $this->decorated->values[$name] = $value;
+    unset($this->decorated->fields[$name]);
+  }
+
+  /**
+   * Magic method.
+   */
+  public function __isset($name) {
+    $value = $this->__get($name);
+    return isset($value);
+  }
+
+  /**
+   * Magic method.
+   */
+  public function __unset($name) {
+    $value = &$this->__get($name);
+    $value = array();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
+    return $this->decorated->access($account);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function get($property_name) {
+    return $this->decorated->get($property_name);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function set($property_name, $value) {
+    return $this->decorated->set($property_name, $value);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getProperties($include_computed = FALSE) {
+    return $this->decorated->getProperties($include_computed);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getPropertyValues() {
+    return $this->decorated->getPropertyValues();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function setPropertyValues($values) {
+    return $this->decorated->setPropertyValues($values);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getPropertyDefinition($name) {
+    return $this->decorated->getPropertyDefinition($name);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getPropertyDefinitions() {
+    return $this->decorated->getPropertyDefinitions();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isEmpty() {
+    return $this->decorated->isEmpty();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getIterator() {
+    return $this->decorated->getIterator();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function id() {
+    return $this->decorated->id();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function uuid() {
+    return $this->decorated->uuid();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isNew() {
+    return $this->decorated->isNew();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isNewRevision() {
+    return $this->decorated->isNewRevision();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function setNewRevision($value = TRUE) {
+    return $this->decorated->setNewRevision($value);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function enforceIsNew($value = TRUE) {
+    return $this->decorated->enforceIsNew($value);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function entityType() {
+    return $this->decorated->entityType();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function bundle() {
+    return $this->decorated->bundle();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function label($langcode = NULL) {
+    return $this->decorated->label();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function uri() {
+    return $this->decorated->uri();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function save() {
+    return $this->decorated->save();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function delete() {
+    return $this->decorated->delete();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function createDuplicate() {
+    return $this->decorated->createDuplicate();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function entityInfo() {
+    return $this->decorated->entityInfo();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getRevisionId() {
+    return $this->decorated->getRevisionId();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function isDefaultRevision($new_value = NULL) {
+    return $this->decorated->isDefaultRevision();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function language() {
+    return $this->decorated->language();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getTranslationLanguages($include_default = TRUE) {
+    return $this->decorated->getTranslationLanguages($include_default);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getTranslation($langcode, $strict = TRUE) {
+    return $this->decorated->getTranslation($langcode, $strict);
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getName() {
+    return $this->decorated->getName();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getNamespace() {
+    return $this->decorated->getNamespace();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getPropertyPath() {
+    return $this->decorated->getPropertyPath();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function getParent() {
+    return $this->decorated->getParent();
+  }
+
+  /**
+   * Forwards the call to the decorated entity.
+   */
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    $this->decorated->setContext($name, $parent);
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php b/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
index 812a192..51bc911 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormControllerNG.php
@@ -23,9 +23,7 @@ public function form(array $form, array &$form_state, EntityInterface $entity) {
     // entity properties.
     $info = $entity->entityInfo();
     if (!empty($info['fieldable'])) {
-      $entity->setCompatibilityMode(TRUE);
-      field_attach_form($entity->entityType(), $entity, $form, $form_state, $this->getFormLangcode($form_state));
-      $entity->setCompatibilityMode(FALSE);
+      field_attach_form($entity->entityType(), $entity->getBCEntity(), $form, $form_state, $this->getFormLangcode($form_state));
     }
     return $form;
   }
@@ -40,9 +38,7 @@ public function validate(array $form, array &$form_state) {
     $info = $entity->entityInfo();
 
     if (!empty($info['fieldable'])) {
-      $entity->setCompatibilityMode(TRUE);
-      field_attach_form_validate($entity->entityType(), $entity, $form, $form_state);
-      $entity->setCompatibilityMode(FALSE);
+      field_attach_form_validate($entity->entityType(), $entity->getBCEntity(), $form, $form_state);
     }
 
     // @todo Remove this.
@@ -82,9 +78,7 @@ public function buildEntity(array $form, array &$form_state) {
 
     // Copy field values to the entity.
     if ($info['fieldable']) {
-      $entity->setCompatibilityMode(TRUE);
-      field_attach_submit($entity_type, $entity, $form, $form_state);
-      $entity->setCompatibilityMode(FALSE);
+      field_attach_submit($entity_type, $entity->getBCEntity(), $form, $form_state);
     }
     return $entity;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 9b751d4..812ebcd 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\TypedData\AccessibleInterface;
+use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 
@@ -17,7 +18,7 @@
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
  */
-interface EntityInterface extends ComplexDataInterface, AccessibleInterface, TranslatableInterface {
+interface EntityInterface extends ContextAwareInterface, ComplexDataInterface, AccessibleInterface, TranslatableInterface {
 
   /**
    * Returns the entity identifier (the entity's machine name or numeric ID).
@@ -181,4 +182,22 @@ public function getRevisionId();
    *   $new_value was passed, the previous value is returned.
    */
   public function isDefaultRevision($new_value = NULL);
+
+  /**
+   * Gets a backward compatibility decorator entity.
+   *
+   * @see \Drupal\Core\Entity\EntityInterface::getOriginalEntity()
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   */
+  public function getBCEntity();
+
+  /**
+   * Removes any possibly (backward compatibility) decorator in use.
+   *
+   * @see \Drupal\Core\Entity\EntityInterface::getBCEntity()
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   */
+  public function getOriginalEntity();
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index 35ef89d..b844f53 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -26,6 +26,13 @@
 class EntityNG extends Entity {
 
   /**
+   * Local cache holding the value of the bundle field.
+   *
+   * @var string
+   */
+  protected $bundle = FALSE;
+
+  /**
    * The plain data values of the contained fields.
    *
    * This always holds the original, unchanged values of the entity. The values
@@ -50,22 +57,60 @@ class EntityNG extends Entity {
   protected $fields = array();
 
   /**
-   * Whether the entity is in pre-Entity Field API compatibility mode.
+   * An instance of the backward compatibility decorator.
    *
-   * If set to TRUE, field values are written directly to $this->values, thus
-   * must be plain property values keyed by language code. This must be enabled
-   * when calling legacy field API attachers.
+   * @var EntityBCDecorator
+   */
+  protected $bcEntity;
+
+  /**
+   * Local cache for field definitions.
+   *
+   * @see self::getPropertyDefinitions()
    *
-   * @var bool
+   * @var array
+   */
+  protected $fieldDefinitions;
+
+  /**
+   * Overrides Entity::__construct().
    */
-  protected $compatibilityMode = FALSE;
+  public function __construct(array $values, $entity_type, $bundle = FALSE) {
+    $this->entityType = $entity_type;
+    $this->bundle = $bundle ? $bundle : $this->entityType;
+    foreach ($values as $key => $value) {
+      $this->values[$key] = $value;
+    }
+    $this->init();
+  }
 
+  /**
+   * Initialize the object. Invoked upon construction and wake up.
+   */
+  protected function init() {
+    // We unset all defined properties, so magic getters apply.
+    unset($this->langcode);
+  }
 
   /**
-   * Overrides Entity::id().
+   * Magic __wakeup() implemenation.
+   */
+  public function __wakeup() {
+    $this->init();
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
    */
   public function id() {
-    return $this->get('id')->value;
+    return $this->id->value;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::bundle().
+   */
+  public function bundle() {
+    return $this->bundle;
   }
 
   /**
@@ -105,9 +150,11 @@ protected function getTranslatedField($property_name, $langcode) {
         $this->fields[$property_name][$langcode] = $this->getTranslatedField($property_name, LANGUAGE_DEFAULT);
       }
       else {
-        $value = isset($this->values[$property_name][$langcode]) ? $this->values[$property_name][$langcode] : NULL;
-        $context = array('parent' => $this, 'name' => $property_name);
-        $this->fields[$property_name][$langcode] = typed_data()->create($definition, $value, $context);
+        $value = NULL;
+        if (isset($this->values[$property_name][$langcode])) {
+          $value = $this->values[$property_name][$langcode];
+        }
+        $this->fields[$property_name][$langcode] = typed_data()->getPropertyInstance($this, $property_name, $value);
       }
     }
     return $this->fields[$property_name][$langcode];
@@ -144,18 +191,14 @@ public function getIterator() {
    * Implements ComplexDataInterface::getPropertyDefinition().
    */
   public function getPropertyDefinition($name) {
-    // First try getting property definitions which apply to all entities of
-    // this type. Then if this fails add in definitions of optional properties
-    // as well. That way we can use property definitions of base properties
-    // when determining the optional properties of an entity.
-    $definitions = entity_get_controller($this->entityType)->getFieldDefinitions(array());
-
-    if (isset($definitions[$name])) {
-      return $definitions[$name];
+    if (!isset($this->fieldDefinitions)) {
+      $this->getPropertyDefinitions();
     }
-    // Add in optional properties if any.
-    if ($definitions = $this->getPropertyDefinitions()) {
-      return isset($definitions[$name]) ? $definitions[$name] : FALSE;
+    if (isset($this->fieldDefinitions[$name])) {
+      return $this->fieldDefinitions[$name];
+    }
+    else {
+      return FALSE;
     }
   }
 
@@ -163,10 +206,13 @@ public function getPropertyDefinition($name) {
    * Implements ComplexDataInterface::getPropertyDefinitions().
    */
   public function getPropertyDefinitions() {
-    return entity_get_controller($this->entityType)->getFieldDefinitions(array(
-      'entity type' => $this->entityType,
-      'bundle' => $this->bundle(),
-    ));
+    if (!isset($this->fieldDefinitions)) {
+      $this->fieldDefinitions = entity_get_controller($this->entityType)->getFieldDefinitions(array(
+        'entity type' => $this->entityType,
+        'bundle' => $this->bundle,
+      ));
+    }
+    return $this->fieldDefinitions;
   }
 
   /**
@@ -208,7 +254,13 @@ public function isEmpty() {
    * Implements TranslatableInterface::language().
    */
   public function language() {
-    return $this->get('langcode')->language;
+    $language = $this->get('langcode')->language;
+    if (!$language) {
+      // Make sure we return a proper language object.
+      // @todo Refactor this, see: http://drupal.org/node/1834542.
+      $language = language_default();
+    }
+    return $language;
   }
 
   /**
@@ -236,6 +288,9 @@ public function getTranslation($langcode, $strict = TRUE) {
         $fields[$name] = $this->getTranslatedField($name, $langcode);
       }
     }
+    // @todo: Add a way to get the definition of a translation to the
+    // TranslatableInterface and leverage TypeDataManager::getPropertyInstance
+    // also.
     $translation_definition = array(
       'type' => 'entity_translation',
       'constraints' => array(
@@ -243,11 +298,11 @@ public function getTranslation($langcode, $strict = TRUE) {
         'bundle' => $this->bundle(),
       ),
     );
-    $translation = typed_data()->create($translation_definition, $fields, array(
-      'parent' => $this,
-      'name' => $langcode,
-    ));
+    $translation = typed_data()->create($translation_definition, $fields);
     $translation->setStrictMode($strict);
+    if ($translation instanceof ContextAwareInterface) {
+      $translation->setContext('@' . $langcode, $this);
+    }
     return $translation;
   }
 
@@ -256,58 +311,62 @@ public function getTranslation($langcode, $strict = TRUE) {
    */
   public function getTranslationLanguages($include_default = TRUE) {
     $translations = array();
-    // Build an array with the translation langcodes set as keys.
+    // Build an array with the translation langcodes set as keys. Empty
+    // translations must be filtered out.
     foreach ($this->getProperties() as $name => $property) {
-      if (isset($this->values[$name])) {
-        $translations += $this->values[$name];
+      foreach ($this->fields[$name] as $langcode => $field) {
+        if (!$field->isEmpty()) {
+          $translations[$langcode] = TRUE;
+        }
+        if (isset($this->values[$name])) {
+          foreach ($this->values[$name] as $langcode => $values) {
+            if ($values && !(isset($this->fields[$name][$langcode]) && $this->fields[$name][$langcode]->isEmpty())) {
+              $translations[$langcode] = TRUE;
+            }
+          }
+        }
       }
-      $translations += $this->fields[$name];
     }
     unset($translations[LANGUAGE_DEFAULT]);
 
     if ($include_default) {
       $translations[$this->language()->langcode] = TRUE;
     }
-
-    // Now get languages based upon translation langcodes. Empty languages must
-    // be filtered out as they concern empty/unset properties.
-    $languages = array_intersect_key(language_list(LANGUAGE_ALL), array_filter($translations));
-    return $languages;
+    // Now get languages based upon translation langcodes.
+    return array_intersect_key(language_list(LANGUAGE_ALL), $translations);
   }
 
   /**
-   * Enables or disable the compatibility mode.
+   * Overrides Entity::translations().
    *
-   * @param bool $enabled
-   *   Whether to enable the mode.
-   *
-   * @see EntityNG::compatibilityMode
+   * @todo: Remove once Entity::translations() gets removed.
    */
-  public function setCompatibilityMode($enabled) {
-    $this->compatibilityMode = (bool) $enabled;
-    if ($enabled) {
-      $this->updateOriginalValues();
-      $this->fields = array();
-    }
+  public function translations() {
+    return $this->getTranslationLanguages(FALSE);
   }
 
   /**
-   * Returns whether the compatibility mode is active.
+   * Overrides Entity::getBCEntity().
    */
-  public function getCompatibilityMode() {
-    return $this->compatibilityMode;
+  public function getBCEntity() {
+    if (!isset($this->bcEntity)) {
+      $this->bcEntity = new EntityBCDecorator($this);
+    }
+    return $this->bcEntity;
   }
 
   /**
    * Updates the original values with the interim changes.
-   *
-   * Note: This should be called by the storage controller during a save
-   * operation.
    */
   public function updateOriginalValues() {
-    foreach ($this->fields as $name => $properties) {
-      foreach ($properties as $langcode => $property) {
-        $this->values[$name][$langcode] = $property->getValue();
+    if (!$this->fields) {
+      return;
+    }
+    foreach ($this->getPropertyDefinitions() as $name => $definition) {
+      if (empty($definition['computed']) && !empty($this->fields[$name])) {
+        foreach ($this->fields[$name] as $langcode => $field) {
+          $this->values[$name][$langcode] = $field->getValue();
+        }
       }
     }
   }
@@ -318,23 +377,24 @@ public function updateOriginalValues() {
    * For compatibility mode to work this must return a reference.
    */
   public function &__get($name) {
-    if ($this->compatibilityMode) {
-      if (!isset($this->values[$name])) {
-        $this->values[$name] = NULL;
-      }
-      return $this->values[$name];
-    }
     if (isset($this->fields[$name][LANGUAGE_DEFAULT])) {
       return $this->fields[$name][LANGUAGE_DEFAULT];
     }
     if ($this->getPropertyDefinition($name)) {
-      $return = $this->get($name);
+      $return = $this->getTranslatedField($name, LANGUAGE_DEFAULT);
       return $return;
     }
-    if (!isset($this->$name)) {
-      $this->$name = NULL;
+    // Allow the EntityBCDecorator to directly access the values and fields.
+    // @todo: Remove once the EntityBCDecorator gets removed.
+    if ($name == 'values' || $name == 'fields') {
+      return $this->$name;
     }
-    return $this->$name;
+    // Else directly read/write plain values. That way, fields not yet converted
+    // to the entity field API can always be directly accessed.
+    if (!isset($this->values[$name])) {
+      $this->values[$name] = NULL;
+    }
+    return $this->values[$name];
   }
 
   /**
@@ -346,17 +406,16 @@ public function __set($name, $value) {
       $value = $value->getValue();
     }
 
-    if ($this->compatibilityMode) {
-      $this->values[$name] = $value;
-    }
-    elseif (isset($this->fields[$name][LANGUAGE_DEFAULT])) {
+    if (isset($this->fields[$name][LANGUAGE_DEFAULT])) {
       $this->fields[$name][LANGUAGE_DEFAULT]->setValue($value);
     }
     elseif ($this->getPropertyDefinition($name)) {
-      $this->get($name)->setValue($value);
+      $this->getTranslatedField($name, LANGUAGE_DEFAULT)->setValue($value);
     }
+    // Else directly read/write plain values. That way, fields not yet converted
+    // to the entity field API can always be directly accessed.
     else {
-      $this->$name = $value;
+      $this->values[$name] = $value;
     }
   }
 
@@ -364,24 +423,24 @@ public function __set($name, $value) {
    * Magic method.
    */
   public function __isset($name) {
-    if ($this->compatibilityMode) {
-      return isset($this->values[$name]);
-    }
-    elseif ($this->getPropertyDefinition($name)) {
+    if ($this->getPropertyDefinition($name)) {
       return (bool) count($this->get($name));
     }
+    else {
+      return isset($this->values[$name]);
+    }
   }
 
   /**
    * Magic method.
    */
   public function __unset($name) {
-    if ($this->compatibilityMode) {
-      unset($this->values[$name]);
-    }
-    elseif ($this->getPropertyDefinition($name)) {
+    if ($this->getPropertyDefinition($name)) {
       $this->get($name)->setValue(array());
     }
+    else {
+      unset($this->values[$name]);
+    }
   }
 
   /**
@@ -404,13 +463,51 @@ public function createDuplicate() {
    * Implements a deep clone.
    */
   public function __clone() {
+    $this->bcEntity = NULL;
+
     foreach ($this->fields as $name => $properties) {
       foreach ($properties as $langcode => $property) {
         $this->fields[$name][$langcode] = clone $property;
         if ($property instanceof ContextAwareInterface) {
-          $this->fields[$name][$langcode]->setParent($this);
+          $this->fields[$name][$langcode]->setContext($name, $this);
         }
       }
     }
   }
+
+  /**
+   * Overrides Entity::label() to access the label field with the new API.
+   */
+  public function label($langcode = NULL) {
+    $label = NULL;
+    $entity_info = $this->entityInfo();
+    if (isset($entity_info['label_callback']) && function_exists($entity_info['label_callback'])) {
+      $label = $entity_info['label_callback']($this->entityType, $this, $langcode);
+    }
+    elseif (!empty($entity_info['entity_keys']['label']) && isset($this->{$entity_info['entity_keys']['label']})) {
+      $label = $this->{$entity_info['entity_keys']['label']}->value;
+    }
+    return $label;
+  }
+
+  /**
+   * Implements ContextAwareInterface::getName().
+   */
+  public function getName() {
+    return '';
+  }
+
+  /**
+   * Implements ContextAwareInterface::getNamespace().
+   */
+  public function getNamespace() {
+    return 'Drupal.core.entity.' . $this->entityType;
+  }
+
+  /**
+   * Implements ContextAwareInterface::getPropertyPath().
+   */
+  public function getPropertyPath() {
+    return '';
+  }
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityRenderController.php b/core/lib/Drupal/Core/Entity/EntityRenderController.php
index 2cd0578..6ccb5b9 100644
--- a/core/lib/Drupal/Core/Entity/EntityRenderController.php
+++ b/core/lib/Drupal/Core/Entity/EntityRenderController.php
@@ -37,29 +37,15 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la
 
       drupal_alter('entity_view_mode', $view_mode, $entity, $context);
       $entity->content['#view_mode'] = $view_mode;
-      $prepare[$view_mode][$key] = $entity;
+      $prepare[$view_mode][$entity->id()] = $entity;
     }
 
     // Prepare and build field content, grouped by view mode.
     foreach ($prepare as $view_mode => $prepare_entities) {
-      $call = array();
-      // To ensure hooks are only run once per entity, check for an
-      // entity_view_prepared flag and only process items without it.
-      foreach ($prepare_entities as $entity) {
-        if (empty($entity->entity_view_prepared)) {
-          // Add this entity to the items to be prepared.
-          $call[$entity->id()] = $entity;
-
-          // Mark this item as prepared.
-          $entity->entity_view_prepared = TRUE;
-        }
-      }
+      field_attach_prepare_view($this->entityType, $prepare_entities, $view_mode, $langcode);
+      module_invoke_all('entity_prepare_view', $prepare_entities, $this->entityType);
 
-      if (!empty($call)) {
-        field_attach_prepare_view($this->entityType, $call, $view_mode, $langcode);
-        module_invoke_all('entity_prepare_view', $call, $this->entityType);
-      }
-      foreach ($entities as $entity) {
+      foreach ($prepare_entities as $entity) {
         $entity->content += field_attach_view($this->entityType, $entity, $view_mode, $langcode);
       }
     }
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
index 5c71aa3..ccc46b0 100644
--- a/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
+++ b/core/lib/Drupal/Core/Entity/Field/FieldItemBase.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\Entity\Field;
 
-use Drupal\Core\TypedData\Type\TypedData;
+use Drupal\Core\TypedData\ContextAwareTypedData;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -24,21 +24,7 @@
  *
  * @see \Drupal\Core\Entity\Field\FieldItemInterface
  */
-abstract class FieldItemBase extends TypedData implements IteratorAggregate, FieldItemInterface {
-
-  /**
-   * The item delta or name.
-   *
-   * @var integer
-   */
-  protected $name;
-
-  /**
-   * The parent entity field.
-   *
-   * @var \Drupal\Core\Entity\Field\FieldInterface
-   */
-  protected $parent;
+abstract class FieldItemBase extends ContextAwareTypedData implements IteratorAggregate, FieldItemInterface {
 
   /**
    * The array of properties.
@@ -52,10 +38,10 @@
   protected $properties = array();
 
   /**
-   * Implements TypedDataInterface::__construct().
+   * Overrides ContextAwareTypedData::__construct().
    */
-  public function __construct(array $definition) {
-    $this->definition = $definition;
+  public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) {
+    parent::__construct($definition, $name, $parent);
 
     // Initialize all property objects, but postpone the creating of computed
     // properties to a second step. That way computed properties can safely get
@@ -63,17 +49,15 @@ public function __construct(array $definition) {
     $step2 = array();
     foreach ($this->getPropertyDefinitions() as $name => $definition) {
       if (empty($definition['computed'])) {
-        $context = array('name' => $name, 'parent' => $this);
-        $this->properties[$name] = typed_data()->create($definition, NULL, $context);
+        $this->properties[$name] = typed_data()->getPropertyInstance($this, $name);
       }
       else {
-        $step2[$name] = $definition;
+        $step2[] = $name;
       }
     }
 
-    foreach ($step2 as $name => $definition) {
-      $context = array('name' => $name, 'parent' => $this);
-      $this->properties[$name] = typed_data()->create($definition, NULL, $context);
+    foreach ($step2 as $name) {
+      $this->properties[$name] = typed_data()->getPropertyInstance($this, $name);
     }
   }
 
@@ -96,14 +80,19 @@ public function getValue() {
    */
   public function setValue($values) {
     // Treat the values as property value of the first property, if no array is
-    // given and we only have one property.
-    if (!is_array($values) && count($this->properties) == 1) {
+    // given.
+    if (!is_array($values)) {
       $keys = array_keys($this->properties);
       $values = array($keys[0] => $values);
     }
 
     foreach ($this->properties as $name => $property) {
-      $property->setValue(isset($values[$name]) ? $values[$name] : NULL);
+      if (isset($values[$name])) {
+        $property->setValue($values[$name]);
+      }
+      else {
+        $property->setValue(NULL);
+      }
     }
     // @todo: Throw an exception for invalid values once conversion is
     // totally completed.
@@ -178,35 +167,6 @@ public function __unset($name) {
     }
   }
 
-  /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    $this->name = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   *
-   * @return \Drupal\Core\Entity\Field\FieldInterface
-   */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-  }
 
   /**
    * Implements ComplexDataInterface::getProperties().
@@ -249,7 +209,12 @@ public function getIterator() {
    */
   public function getPropertyDefinition($name) {
     $definitions = $this->getPropertyDefinitions();
-    return isset($definitions[$name]) ? $definitions[$name] : FALSE;
+    if (isset($definitions[$name])) {
+      return $definitions[$name];
+    }
+    else {
+      return FALSE;
+    }
   }
 
   /**
@@ -271,7 +236,7 @@ public function __clone() {
     foreach ($this->properties as $name => $property) {
       $this->properties[$name] = clone $property;
       if ($property instanceof ContextAwareInterface) {
-        $this->properties[$name]->setParent($this);
+        $this->properties[$name]->setContext($name, $this);
       }
     }
   }
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/BooleanItem.php b/core/lib/Drupal/Core/Entity/Field/Type/BooleanItem.php
index 131d0a2..39f61b5 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/BooleanItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/BooleanItem.php
@@ -15,7 +15,7 @@
 class BooleanItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/DateItem.php b/core/lib/Drupal/Core/Entity/Field/Type/DateItem.php
index eb1ab12..b53b97f 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/DateItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/DateItem.php
@@ -15,7 +15,7 @@
 class DateItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
index a60e65e..6c4a6fc 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityReferenceItem.php
@@ -13,13 +13,13 @@
 /**
  * Defines the 'entityreference_field' entity field item.
  *
- * Required settings (below the definition's 'settings' key) are:
- *  - entity type: The entity type to reference.
+ * Available settings (below the definition's 'settings' key) are:
+ *   - entity type: (required) The entity type to reference.
  */
 class EntityReferenceItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
@@ -71,8 +71,11 @@ public function setValue($values) {
     if (isset($values['value'])) {
       $this->properties['value']->setValue($values['value']);
     }
+    elseif (isset($values['entity'])) {
+      $this->properties['entity']->setValue($values['entity']);
+    }
     else {
-      $this->properties['entity']->setValue(isset($values['entity']) ? $values['entity'] : NULL);
+      $this->properties['entity']->setValue(NULL);
     }
     unset($values['entity'], $values['value']);
     if ($values) {
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
index 280fca9..99b6011 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
@@ -7,10 +7,9 @@
 
 namespace Drupal\Core\Entity\Field\Type;
 
-use Drupal\Core\TypedData\Type\TypedData;
 use Drupal\Core\TypedData\AccessibleInterface;
 use Drupal\Core\TypedData\ComplexDataInterface;
-use Drupal\Core\TypedData\ContextAwareInterface;
+use Drupal\Core\TypedData\ContextAwareTypedData;
 use Drupal\Core\TypedData\TypedDataInterface;
 use ArrayIterator;
 use IteratorAggregate;
@@ -19,7 +18,7 @@
 /**
  * Makes translated entity properties available via the Field API.
  */
-class EntityTranslation extends TypedData implements IteratorAggregate, AccessibleInterface, ComplexDataInterface, ContextAwareInterface {
+class EntityTranslation extends ContextAwareTypedData implements IteratorAggregate, AccessibleInterface, ComplexDataInterface {
 
   /**
    * The array of translated properties, each being an instance of
@@ -30,20 +29,6 @@ class EntityTranslation extends TypedData implements IteratorAggregate, Accessib
   protected $properties = array();
 
   /**
-   * The language code of the translation.
-   *
-   * @var string
-   */
-  protected $langcode;
-
-  /**
-   * The parent entity.
-   *
-   * @var \Drupal\Core\Entity\EntityInterface
-   */
-  protected $parent;
-
-  /**
    * Whether the entity translation acts in strict mode.
    *
    * @var boolean
@@ -73,37 +58,6 @@ public function setStrictMode($strict = TRUE) {
   }
 
   /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    // The name of the translation is the language code.
-    return $this->langcode;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    // The name of the translation is the language code.
-    $this->langcode = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   *
-   * @return \Drupal\Core\Entity\EntityInterface
-   */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-  }
-  /**
    * Implements TypedDataInterface::getValue().
    */
   public function getValue() {
@@ -186,7 +140,12 @@ public function getIterator() {
    */
   public function getPropertyDefinition($name) {
     $definitions = $this->getPropertyDefinitions();
-    return isset($definitions[$name]) ? $definitions[$name] : FALSE;
+    if (isset($definitions[$name])) {
+      return $definitions[$name];
+    }
+    else {
+      return FALSE;
+    }
   }
 
   /**
@@ -235,7 +194,10 @@ public function isEmpty() {
    */
   public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
     $method = $operation . 'Access';
-    return entity_access_controller($this->parent->entityType())->$method($this->parent, $this->langcode, $account);
+    // @todo Add a way to set and get the langcode so that's more obvious what
+    // we're doing here.
+    $langocde = substr($this->getName(), 1);
+    return entity_access_controller($this->parent->entityType())->$method($this->parent, $langocde, $account);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
index 97449e7..ffc9fd1 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityWrapper.php
@@ -8,9 +8,9 @@
 namespace Drupal\Core\Entity\Field\Type;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\TypedData\Type\TypedData;
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\Core\TypedData\ContextAwareInterface;
+use Drupal\Core\TypedData\ContextAwareTypedData;
 use Drupal\Core\TypedData\TypedDataInterface;
 use ArrayIterator;
 use IteratorAggregate;
@@ -36,21 +36,7 @@
  *  - id source: If used as computed property, the ID property used to load
  *    the entity object.
  */
-class EntityWrapper extends TypedData implements IteratorAggregate, ComplexDataInterface, ContextAwareInterface {
-
-  /**
-   * The name.
-   *
-   * @var string
-   */
-  protected $name;
-
-  /**
-   * The parent data structure.
-   *
-   * @var mixed
-   */
-  protected $parent;
+class EntityWrapper extends ContextAwareTypedData implements IteratorAggregate, ComplexDataInterface {
 
   /**
    * The referenced entity type.
@@ -67,10 +53,10 @@ class EntityWrapper extends TypedData implements IteratorAggregate, ComplexDataI
   protected $id;
 
   /**
-   * Implements TypedDataInterface::__construct().
+   * Overrides ContextAwareTypedData::__construct().
    */
-  public function __construct(array $definition) {
-    $this->definition = $definition + array('constraints' => array());
+  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;
   }
 
@@ -120,8 +106,10 @@ public function setValue($value) {
    * Implements TypedDataInterface::getString().
    */
   public function getString() {
-    $entity = $this->getValue();
-    return $entity ? $entity->label() : '';
+    if ($entity = $this->getValue()) {
+      return $entity->label();
+    }
+    return '';
   }
 
   /**
@@ -135,17 +123,20 @@ public function validate($value = NULL) {
    * Implements IteratorAggregate::getIterator().
    */
   public function getIterator() {
-    $entity = $this->getValue();
-    return $entity ? $entity->getIterator() : new ArrayIterator(array());
+    if ($entity = $this->getValue()) {
+      return $entity->getIterator();
+    }
+    return new ArrayIterator(array());
   }
 
   /**
    * Implements ComplexDataInterface::get().
    */
   public function get($property_name) {
-    $entity = $this->getValue();
     // @todo: Allow navigating through the tree without data as well.
-    return $entity ? $entity->get($property_name) : NULL;
+    if ($entity = $this->getValue()) {
+      return $entity->get($property_name);
+    }
   }
 
   /**
@@ -156,39 +147,13 @@ public function set($property_name, $value) {
   }
 
   /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    $this->name = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-  }
-
-  /**
    * Implements ComplexDataInterface::getProperties().
    */
   public function getProperties($include_computed = FALSE) {
-    $entity = $this->getValue();
-    return $entity ? $entity->getProperties($include_computed) : array();
+    if ($entity = $this->getValue()) {
+      return $entity->getProperties($include_computed);
+    }
+    return array();
   }
 
   /**
@@ -196,7 +161,12 @@ public function getProperties($include_computed = FALSE) {
    */
   public function getPropertyDefinition($name) {
     $definitions = $this->getPropertyDefinitions();
-    return isset($definitions[$name]) ? $definitions[$name] : FALSE;
+    if (isset($definitions[$name])) {
+      return $definitions[$name];
+    }
+    else {
+      return FALSE;
+    }
   }
 
   /**
@@ -211,8 +181,10 @@ public function getPropertyDefinitions() {
    * Implements ComplexDataInterface::getPropertyValues().
    */
   public function getPropertyValues() {
-    $entity = $this->getValue();
-    return $entity ? $entity->getPropertyValues() : array();
+    if ($entity = $this->getValue()) {
+      return $entity->getPropertyValues();
+    }
+    return array();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index cc5951c..dd5eedd 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -8,9 +8,10 @@
 namespace Drupal\Core\Entity\Field\Type;
 
 use Drupal\Core\Entity\Field\FieldInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
-use Drupal\Core\TypedData\Type\TypedData;
 use Drupal\user\Plugin\Core\Entity\User;
+use Drupal\Core\TypedData\ContextAwareInterface;
+use Drupal\Core\TypedData\ContextAwareTypedData;
+use Drupal\Core\TypedData\TypedDataInterface;
 use ArrayIterator;
 use IteratorAggregate;
 use InvalidArgumentException;
@@ -26,21 +27,7 @@
  *
  * @see \Drupal\Core\Entity\Field\FieldInterface
  */
-class Field extends TypedData implements IteratorAggregate, FieldInterface {
-
-  /**
-   * The entity field name.
-   *
-   * @var string
-   */
-  protected $name;
-
-  /**
-   * The parent entity.
-   *
-   * @var \Drupal\Core\Entity\EntityInterface
-   */
-  protected $parent;
+class Field extends ContextAwareTypedData implements IteratorAggregate, FieldInterface {
 
   /**
    * Numerically indexed array of field items, implementing the
@@ -51,12 +38,29 @@ class Field extends TypedData implements IteratorAggregate, FieldInterface {
   protected $list = array();
 
   /**
+   * Overrides ContextAwareTypedData::__construct().
+   */
+  public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) {
+    parent::__construct($definition, $name, $parent);
+    // Always initialize one empty item as usually that will be needed. That
+    // way prototypes created by
+    // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will
+    // already have one field item ready for use after cloning.
+    $this->list[0] = $this->createItem(0);
+  }
+
+  /**
    * Implements TypedDataInterface::getValue().
    */
   public function getValue() {
     $values = array();
     foreach ($this->list as $delta => $item) {
-      $values[$delta] = !$item->isEmpty() ? $item->getValue() : NULL;
+      if (!$item->isEmpty()) {
+        $values[$delta] = $item->getValue();
+      }
+      else {
+        $values[$delta] = NULL;
+      }
     }
     return $values;
   }
@@ -88,7 +92,7 @@ public function setValue($values) {
           throw new InvalidArgumentException('Unable to set a value with a non-numeric delta in a list.');
         }
         elseif (!isset($this->list[$delta])) {
-          $this->list[$delta] = $this->createItem($value);
+          $this->list[$delta] = $this->createItem($delta, $value);
         }
         else {
           $this->list[$delta]->setValue($value);
@@ -144,7 +148,7 @@ public function offsetGet($offset) {
     // Allow getting not yet existing items as well.
     // @todo: Maybe add a public createItem() method in addition?
     elseif (!isset($this->list[$offset])) {
-      $this->list[$offset] = $this->createItem();
+      $this->list[$offset] = $this->createItem($offset);
     }
     return $this->list[$offset];
   }
@@ -154,9 +158,15 @@ public function offsetGet($offset) {
    *
    * @return \Drupal\Core\TypedData\TypedDataInterface
    */
-  protected function createItem($value = NULL) {
-    $context = array('parent' => $this);
-    return typed_data()->create(array('list' => FALSE) + $this->definition, $value, $context);
+  protected function createItem($offset = 0, $value = NULL) {
+    return typed_data()->getPropertyInstance($this, $offset, $value);
+  }
+
+  /**
+   * Implements ListInterface::getItemDefinition().
+   */
+  public function getItemDefinition() {
+    return array('list' => FALSE) + $this->definition;
   }
 
   /**
@@ -194,36 +204,6 @@ public function count() {
   }
 
   /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    $this->name = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   *
-   * @return \Drupal\Core\Entity\EntityInterface
-   */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-  }
-
-  /**
    * Delegate.
    */
   public function getPropertyDefinition($name) {
@@ -290,6 +270,9 @@ public function isEmpty() {
   public function __clone() {
     foreach ($this->list as $delta => $property) {
       $this->list[$delta] = clone $property;
+      if ($property instanceof ContextAwareInterface) {
+        $this->list[$delta]->setContext($delta, $this);
+      }
     }
   }
 
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/IntegerItem.php b/core/lib/Drupal/Core/Entity/Field/Type/IntegerItem.php
index 1f4b4e6..2d1585f 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/IntegerItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/IntegerItem.php
@@ -15,7 +15,7 @@
 class IntegerItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
index f7b2a91..a6e548e 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/LanguageItem.php
@@ -16,9 +16,9 @@
 class LanguageItem extends FieldItemBase {
 
   /**
-   * Array of property definitions of contained properties.
+   * Definitions of the contained properties.
    *
-   * @see PropertyEntityReferenceItem::getPropertyDefinitions()
+   * @see self::getPropertyDefinitions()
    *
    * @var array
    */
@@ -61,8 +61,11 @@ public function setValue($values) {
     if (!empty($values['value'])) {
       $this->properties['value']->setValue($values['value']);
     }
+    elseif (isset($values['language'])) {
+      $this->properties['language']->setValue($values['language']);
+    }
     else {
-      $this->properties['language']->setValue(isset($values['language']) ? $values['language'] : NULL);
+      $this->properties['language']->setValue(NULL);
     }
     unset($values['language'], $values['value']);
     if ($values) {
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/StringItem.php b/core/lib/Drupal/Core/Entity/Field/Type/StringItem.php
index 7c8c57a..6d98a52 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/StringItem.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/StringItem.php
@@ -15,7 +15,7 @@
 class StringItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
index 67d6cab..7229a77 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
@@ -65,8 +65,13 @@ public function __construct(DiscoveryInterface $decorated, $cache_key, $cache_bi
    * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
    */
   public function getDefinition($plugin_id) {
-    $definitions = $this->getDefinitions();
-    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
+    if (!isset($this->definitions)) {
+      // Initialize definitions.
+      $this->getDefinitions();
+    }
+    if (isset($this->definitions[$plugin_id])) {
+      return $this->definitions[$plugin_id];
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
index 71364ce..d6bffc6 100644
--- a/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ComplexDataInterface.php
@@ -12,6 +12,9 @@
 /**
  * Interface for complex data; i.e. data containing named and typed properties.
  *
+ * The name of a property has to be a valid PHP variable name, starting with
+ * an alphabetic character.
+ *
  * This is implemented by entities as well as by field item classes of
  * entities.
  *
diff --git a/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php b/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php
index 41c3031..143a9ef 100644
--- a/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ContextAwareInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\Core\TypedData\ContextAwareInterface.
+ * Contains \Drupal\Core\TypedData\ContextAwareInterface.
  */
 
 namespace Drupal\Core\TypedData;
@@ -23,36 +23,49 @@
   public function getName();
 
   /**
-   * Sets the name of a property or item.
+   * Returns the parent data structure; i.e. either complex data or a list.
    *
-   * This method is supposed to be used by the parental data structure in order
-   * to provide appropriate context only.
+   * @return \Drupal\Core\TypedData\ComplexDataInterface|\Drupal\Core\TypedData\ListInterface
+   *   The parent data structure, either complex data or a list; or NULL if this
+   *   is the root of the typed data tree.
+   */
+  public function getParent();
+
+  /**
+   * Returns the typed data namespace of the typed data tree.
    *
-   * @param string $name
-   *   The name to set for a property or item.
+   * A namespace to identify the current typed data tree, e.g. for the the tree
+   * of typed data objects of an entity it could be
+   * Drupal.core.entity.entity_type.
    *
-   * @see ContextAwareInterface::getName()
+   * @return string
+   *   The namespace of the typed data tree, or NULL if it is not specified.
    */
-  public function setName($name);
+  public function getNamespace();
 
   /**
-   * Returns the parent data structure; i.e. either complex data or a list.
+   * Returns the property path of the data.
+   *
+   * The trail of property names relative to the root of the typed data tree,
+   * separated by dots; e.g. 'field_text.0.format'.
    *
-   * @return Drupal\Core\TypedData\ComplexDataInterface|Drupal\Core\TypedData\ListInterface
-   *   The parent data structure; either complex data or a list.
+   * @return string
+   *   The property path relative to the root of the typed tree, or an empty
+   *   string if this is the root.
    */
-  public function getParent();
+  public function getPropertyPath();
 
   /**
-   * Sets the parent of a property or item.
-   *
-   * This method is supposed to be used by the parental data structure in order
-   * to provide appropriate context only.
+   * Sets the context of a property or item via a context aware parent.
    *
-   * @param mixed $parent
-   *   The parent data structure; either complex data or a list.
+   * This method is supposed to be called by the factory only.
    *
-   * @see ContextAwareInterface::getParent()
+   * @param string $name
+   *   (optional) The name of the property or the delta of the the list item,
+   *   or NULL if it is the root of a typed data tree. Defaults to NULL.
+   * @param \Drupal\Core\TypedData\ContextAwareInterface $parent
+   *   (optional) The parent object of the data property, or NULL if it is the
+   *   root of a typed data tree. Defaults to NULL.
    */
-  public function setParent($parent);
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL);
 }
diff --git a/core/lib/Drupal/Core/TypedData/ContextAwareTypedData.php b/core/lib/Drupal/Core/TypedData/ContextAwareTypedData.php
new file mode 100644
index 0000000..75b7077
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/ContextAwareTypedData.php
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\ContextAwareTypedData.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * An abstract base class for context aware typed data.
+ *
+ * This implementation requires parent typed data objects to implement the
+ * ContextAwareInterface also, such that the context can be derived from the
+ * parents.
+ *
+ * Classes deriving from this base class have to declare $value
+ * or override getValue() or setValue().
+ */
+abstract class ContextAwareTypedData extends TypedData implements ContextAwareInterface {
+
+  /**
+   * The typed data namespace.
+   *
+   * @var string
+   */
+  protected $namespace;
+
+  /**
+   * The property path.
+   *
+   * @var string
+   */
+  protected $propertyPath = '';
+
+  /**
+   * The parent typed data object.
+   *
+   * @var \Drupal\Core\TypedData\ContextAwareInterface
+   */
+  protected $parent;
+
+  /**
+   * Constructs a TypedData object given its definition and context.
+   *
+   * @param array $definition
+   *   The data definition.
+   * @param string $name
+   *   (optional) The name of the created property, or NULL if it is the root
+   *   of a typed data tree. Defaults to NULL.
+   * @param \Drupal\Core\TypedData\ContextAwareInterface $parent
+   *   (optional) The parent object of the data property, or NULL if it is the
+   *   root of a typed data tree. Defaults to NULL.
+   *
+   * @see Drupal\Core\TypedData\TypedDataManager::create()
+   */
+  public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) {
+    $this->definition = $definition;
+    $this->setContext($name, $parent);
+  }
+
+  /**
+   * Implements ContextAwareInterface::setContext().
+   */
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    $this->parent = $parent;
+    if (isset($name) && isset($parent)) {
+      $this->propertyPath = $this->parent->getPropertyPath();
+      $this->propertyPath = $this->propertyPath ? $this->propertyPath . '.' . $name : $name;
+    }
+    else {
+      $this->propertyPath = '';
+    }
+    if (!isset($this->namespace) && isset($parent)) {
+      $this->namespace = $parent->getNamespace();
+    }
+  }
+
+  /**
+   * Implements ContextAwareInterface::getName().
+   */
+  public function getName() {
+    $position = strrpos($this->propertyPath, '.');
+    return substr($this->propertyPath, $position !== FALSE ? $position + 1 : 0);
+  }
+
+  /**
+   * Implements ContextAwareInterface::getNamespace().
+   */
+  public function getNamespace() {
+    return $this->namespace;
+  }
+
+  /**
+   * Implements ContextAwareInterface::getPropertyPath().
+   */
+  public function getPropertyPath() {
+    return $this->propertyPath;
+  }
+
+  /**
+   * Implements ContextAwareInterface::getParent().
+   *
+   * @return \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public function getParent() {
+    return $this->parent;
+  }
+
+  /**
+   * Implements TypedDataInterface::validate().
+   */
+  public function validate() {
+    // @todo: Implement validate() method.
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/ListInterface.php b/core/lib/Drupal/Core/TypedData/ListInterface.php
index 4ce6af0..72d05d5 100644
--- a/core/lib/Drupal/Core/TypedData/ListInterface.php
+++ b/core/lib/Drupal/Core/TypedData/ListInterface.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Definition of Drupal\Core\TypedData\ListInterface.
+ * Contains Drupal\Core\TypedData\ListInterface.
  */
 
 namespace Drupal\Core\TypedData;
@@ -29,4 +29,12 @@
    *   TRUE if the list is empty, FALSE otherwise.
    */
   public function isEmpty();
+
+  /**
+   * Gets the definition of a contained item.
+   *
+   * @return array
+   *   The data definition of contained items.
+   */
+  public function getItemDefinition();
 }
diff --git a/core/lib/Drupal/Core/TypedData/Type/Binary.php b/core/lib/Drupal/Core/TypedData/Type/Binary.php
index 7ec5e6b..303b71c 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Binary.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Binary.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 use InvalidArgumentException;
 
 /**
@@ -17,7 +17,7 @@
  * http://php.net/manual/en/language.types.resource.php. For setting the value
  * a PHP file resource or a (absolute) stream resource URI may be passed.
  */
-class Binary extends TypedData implements TypedDataInterface {
+class Binary extends TypedData {
 
   /**
    * The file resource URI.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Boolean.php b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
index 5714599..c8797ef 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Boolean.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Boolean.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * The boolean data type.
@@ -15,7 +15,7 @@
  * The plain value of a boolean is a regular PHP boolean. For setting the value
  * any PHP variable that casts to a boolean may be passed.
  */
-class Boolean extends TypedData implements TypedDataInterface {
+class Boolean extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Date.php b/core/lib/Drupal/Core/TypedData/Type/Date.php
index 3c0f3a9..a534280 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Date.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Date.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\TypedData\Type;
 
 use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 use InvalidArgumentException;
 
 /**
@@ -19,7 +19,7 @@
  * class will work, including a DateTime object, a timestamp, a string
  * date, or an array of date parts.
  */
-class Date extends TypedData implements TypedDataInterface {
+class Date extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Duration.php b/core/lib/Drupal/Core/TypedData/Type/Duration.php
index 9fd8ceb..d979dcf 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Duration.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Duration.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 use DateInterval;
 use InvalidArgumentException;
 
@@ -19,7 +19,7 @@
  * supported by DateInterval::__construct, or an integer in seconds may be
  * passed.
  */
-class Duration extends TypedData implements TypedDataInterface {
+class Duration extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Float.php b/core/lib/Drupal/Core/TypedData/Type/Float.php
index 798499c..3e8369b 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Float.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Float.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * The float data type.
@@ -15,7 +15,7 @@
  * The plain value of a float is a regular PHP float. For setting the value
  * any PHP variable that casts to a float may be passed.
  */
-class Float extends TypedData implements TypedDataInterface {
+class Float extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Integer.php b/core/lib/Drupal/Core/TypedData/Type/Integer.php
index 4e9b59a..4303511 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Integer.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Integer.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * The integer data type.
@@ -15,7 +15,7 @@
  * The plain value of an integer is a regular PHP integer. For setting the value
  * any PHP variable that casts to an integer may be passed.
  */
-class Integer extends TypedData implements TypedDataInterface {
+class Integer extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/Language.php b/core/lib/Drupal/Core/TypedData/Type/Language.php
index 67f4df8..50ae4b8 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Language.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Language.php
@@ -7,9 +7,8 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\ContextAwareInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
 use InvalidArgumentException;
+use Drupal\Core\TypedData\ContextAwareTypedData;
 
 /**
  * Defines the 'language' data type.
@@ -25,21 +24,7 @@
  *  - langcode source: If used as computed property, the langcode property used
  *    to load the language object.
  */
-class Language extends TypedData implements TypedDataInterface, ContextAwareInterface {
-
-  /**
-   * The name.
-   *
-   * @var string
-   */
-  protected $name;
-
-  /**
-   * The parent data structure.
-   *
-   * @var mixed
-   */
-  protected $parent;
+class Language extends ContextAwareTypedData {
 
   /**
    * The language code of the language if no 'langcode source' is used.
@@ -49,40 +34,14 @@ class Language extends TypedData implements TypedDataInterface, ContextAwareInte
   protected $langcode;
 
   /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    $this->name = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-  }
-
-  /**
    * Implements TypedDataInterface::getValue().
    */
   public function getValue() {
     $source = $this->getLanguageCodeSource();
     $langcode = $source ? $source->getValue() : $this->langcode;
-    return $langcode ? language_load($langcode) : NULL;
+    if ($langcode) {
+      return language_load($langcode);
+    }
   }
 
   /**
diff --git a/core/lib/Drupal/Core/TypedData/Type/String.php b/core/lib/Drupal/Core/TypedData/Type/String.php
index fef3248..9248239 100644
--- a/core/lib/Drupal/Core/TypedData/Type/String.php
+++ b/core/lib/Drupal/Core/TypedData/Type/String.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * The string data type.
@@ -15,7 +15,7 @@
  * The plain value of a string is a regular PHP string. For setting the value
  * any PHP variable that casts to a string may be passed.
  */
-class String extends TypedData implements TypedDataInterface {
+class String extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/Type/TypedData.php b/core/lib/Drupal/Core/TypedData/Type/TypedData.php
deleted file mode 100644
index 1e70c53..0000000
--- a/core/lib/Drupal/Core/TypedData/Type/TypedData.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\Core\TypedData\Type\TypedData.
- */
-
-namespace Drupal\Core\TypedData\Type;
-
-use Drupal\Core\TypedData\TypedDataInterface;
-
-/**
- * The abstract base class for typed data.
- *
- * Classes deriving from this base class have to declare $value
- * or override getValue() or setValue().
- */
-abstract class TypedData implements TypedDataInterface {
-
-  /**
-   * The data definition.
-   *
-   * @var array
-   */
-  protected $definition;
-
-  /**
-   * Constructs a TypedData object given its definition.
-   *
-   * @param array $definition
-   *   The data definition.
-   *
-   * @see Drupal\Core\TypedData\TypedDataManager::create()
-   */
-  public function __construct(array $definition) {
-    $this->definition = $definition;
-  }
-
-  /**
-   * Implements TypedDataInterface::getType().
-   */
-  public function getType() {
-    return $this->definition['type'];
-  }
-
-  /**
-   * Implements TypedDataInterface::getDefinition().
-   */
-  public function getDefinition() {
-    return $this->definition;
-  }
-
-  /**
-   * Implements TypedDataInterface::getValue().
-   */
-  public function getValue() {
-    return $this->value;
-  }
-
-  /**
-   * Implements TypedDataInterface::setValue().
-   */
-  public function setValue($value) {
-    $this->value = $value;
-  }
-
-  /**
-   * Implements TypedDataInterface::getString().
-   */
-  public function getString() {
-    return (string) $this->getValue();
-  }
-}
diff --git a/core/lib/Drupal/Core/TypedData/Type/Uri.php b/core/lib/Drupal/Core/TypedData/Type/Uri.php
index 010fa03..52b9c3f 100644
--- a/core/lib/Drupal/Core/TypedData/Type/Uri.php
+++ b/core/lib/Drupal/Core/TypedData/Type/Uri.php
@@ -7,14 +7,14 @@
 
 namespace Drupal\Core\TypedData\Type;
 
-use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\Core\TypedData\TypedData;
 
 /**
  * The URI data type.
  *
  * The plain value of a URI is an absolute URI represented as PHP string.
  */
-class Uri extends TypedData implements TypedDataInterface {
+class Uri extends TypedData {
 
   /**
    * The data value.
diff --git a/core/lib/Drupal/Core/TypedData/TypedData.php b/core/lib/Drupal/Core/TypedData/TypedData.php
new file mode 100644
index 0000000..053bb70
--- /dev/null
+++ b/core/lib/Drupal/Core/TypedData/TypedData.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\TypedData\TypedData.
+ */
+
+namespace Drupal\Core\TypedData;
+
+/**
+ * The abstract base class for typed data.
+ *
+ * Classes deriving from this base class have to declare $value
+ * or override getValue() or setValue().
+ */
+abstract class TypedData implements TypedDataInterface {
+
+  /**
+   * The data definition.
+   *
+   * @var array
+   */
+  protected $definition;
+
+  /**
+   * Constructs a TypedData object given its definition.
+   *
+   * @param array $definition
+   *   The data definition.
+   *
+   * @see Drupal\Core\TypedData\TypedDataManager::create()
+   */
+  public function __construct(array $definition) {
+    $this->definition = $definition;
+  }
+
+  /**
+   * Implements TypedDataInterface::getType().
+   */
+  public function getType() {
+    return $this->definition['type'];
+  }
+
+  /**
+   * Implements TypedDataInterface::getDefinition().
+   */
+  public function getDefinition() {
+    return $this->definition;
+  }
+
+  /**
+   * Implements TypedDataInterface::getValue().
+   */
+  public function getValue() {
+    return $this->value;
+  }
+
+  /**
+   * Implements TypedDataInterface::setValue().
+   */
+  public function setValue($value) {
+    $this->value = $value;
+  }
+
+  /**
+   * Implements TypedDataInterface::getString().
+   */
+  public function getString() {
+    return (string) $this->getValue();
+  }
+}
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataFactory.php b/core/lib/Drupal/Core/TypedData/TypedDataFactory.php
index d245157..4d63d7d 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataFactory.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataFactory.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\TypedData;
 
+use InvalidArgumentException;
 use Drupal\Component\Plugin\Factory\DefaultFactory;
 use Drupal\Component\Plugin\Exception\PluginException;
 
@@ -25,28 +26,36 @@ class TypedDataFactory extends DefaultFactory {
    *   The id of a plugin, i.e. the data type.
    * @param array $configuration
    *   The plugin configuration, i.e. the data definition.
+   * @param string $name
+   *   (optional) If a property or list item is to be created, the name of the
+   *   property or the delta of the the list item. Otherwise, leave it empty.
+   * @param mixed $parent
+   *   (optional) If a property or list item is to be created, the parent typed
+   *   data object implementing either theListInterface or the
+   *   ComplexDataInterface. Otherwise, leave it empty.
    *
-   * @return Drupal\Core\TypedData\TypedDataInterface
+   * @return \Drupal\Core\TypedData\TypedDataInterface
    */
-  public function createInstance($plugin_id, array $configuration) {
+  public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
+
     $type_definition = $this->discovery->getDefinition($plugin_id);
 
-    // Allow per-data definition overrides of the used classes and generally
-    // default to the data type definition.
-    $definition = $configuration + $type_definition;
+    if (!isset($type_definition)) {
+      throw new InvalidArgumentException(format_string('Invalid data type %plugin_id has been given.', array('%plugin_id' => $plugin_id)));
+    }
 
-    if (empty($definition['list'])) {
-      if (empty($definition['class'])) {
-        throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
-      }
-      $plugin_class = $definition['class'];
+    // Allow per-data definition overrides of the used classes.
+    $key = empty($configuration['list']) ? 'class' : 'list class';
+    if (isset($configuration[$key])) {
+      $class = $configuration[$key];
     }
-    else {
-      if (empty($definition['list class'])) {
-        throw new PluginException(sprintf('The plugin (%s) did not specify a list instance class.', $plugin_id));
-      }
-      $plugin_class = $definition['list class'];
+    elseif (isset($type_definition[$key])) {
+      $class = $type_definition[$key];
+    }
+
+    if (!isset($class)) {
+      throw new PluginException(sprintf('The plugin (%s) did not specify an instance class.', $plugin_id));
     }
-    return new $plugin_class($definition, $plugin_id, $this->discovery);
+    return new $class($configuration, $name, $parent);
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/TypedDataManager.php b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
index a1522d8..3e747cf 100644
--- a/core/lib/Drupal/Core/TypedData/TypedDataManager.php
+++ b/core/lib/Drupal/Core/TypedData/TypedDataManager.php
@@ -2,11 +2,12 @@
 
 /**
  * @file
- * Definition of Drupal\Core\TypedData\TypedDataManager.
+ * Contains \Drupal\Core\TypedData\TypedDataManager.
  */
 
 namespace Drupal\Core\TypedData;
 
+use InvalidArgumentException;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\HookDiscovery;
@@ -16,6 +17,13 @@
  */
 class TypedDataManager extends PluginManagerBase {
 
+  /**
+   * An array of typed data property prototypes.
+   *
+   * @var array
+   */
+  protected $prototypes = array();
+
   public function __construct() {
     $this->discovery = new CacheDecorator(new HookDiscovery('data_type_info'), 'typed_data:types');
     $this->factory = new TypedDataFactory($this->discovery);
@@ -28,11 +36,18 @@ public function __construct() {
    *   The id of a plugin, i.e. the data type.
    * @param array $configuration
    *   The plugin configuration, i.e. the data definition.
+   * @param string $name
+   *   (optional) If a property or list item is to be created, the name of the
+   *   property or the delta of the the list item. Otherwise, leave it empty.
+   * @param mixed $parent
+   *   (optional) If a property or list item is to be created, the parent typed
+   *   data object implementing either theListInterface or the
+   *   ComplexDataInterface. Otherwise, leave it empty.
    *
-   * @return Drupal\Core\TypedData\TypedDataInterface
+   * @return \Drupal\Core\TypedData\TypedDataInterface
    */
-  public function createInstance($plugin_id, array $configuration) {
-    return $this->factory->createInstance($plugin_id, $configuration);
+  public function createInstance($plugin_id, array $configuration, $name = NULL, $parent = NULL) {
+    return $this->factory->createInstance($plugin_id, $configuration, $name, $parent);
   }
 
   /**
@@ -70,42 +85,123 @@ public function createInstance($plugin_id, array $configuration) {
    * @param mixed $value
    *   (optional) The data value. If set, it has to match one of the supported
    *   data type format as documented for the data type classes.
-   * @param array $context
-   *   (optional) An array describing the context of the data object, e.g. its
-   *   name or parent data structure. The context should be passed if a typed
-   *   data object is created as part of a data structure. The following keys
-   *   are supported:
-   *   - name: The name associated with the data.
-   *   - parent: The parent object containing the data. Must be an instance of
-   *     Drupal\Core\TypedData\ComplexDataInterface or
-   *     Drupal\Core\TypedData\ListInterface.
-   *
-   * @return Drupal\Core\TypedData\TypedDataInterface
+   * @param string $name
+   *   (optional) If a property or list item is to be created, the name of the
+   *   property or the delta of the the list item. Otherwise, leave it empty.
+   * @param mixed $parent
+   *   (optional) If a property or list item is to be created, the parent typed
+   *   data object implementing either theListInterface or the
+   *   ComplexDataInterface. Otherwise, leave it empty.
    *
    * @see typed_data()
-   * @see Drupal\Core\TypedData\Type\Integer
-   * @see Drupal\Core\TypedData\Type\Float
-   * @see Drupal\Core\TypedData\Type\String
-   * @see Drupal\Core\TypedData\Type\Boolean
-   * @see Drupal\Core\TypedData\Type\Duration
-   * @see Drupal\Core\TypedData\Type\Date
-   * @see Drupal\Core\TypedData\Type\Uri
-   * @see Drupal\Core\TypedData\Type\Binary
-   * @see Drupal\Core\Entity\Field\EntityWrapper
+   * @see \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance()
+   * @see \Drupal\Core\TypedData\Type\Integer
+   * @see \Drupal\Core\TypedData\Type\Float
+   * @see \Drupal\Core\TypedData\Type\String
+   * @see \Drupal\Core\TypedData\Type\Boolean
+   * @see \Drupal\Core\TypedData\Type\Duration
+   * @see \Drupal\Core\TypedData\Type\Date
+   * @see \Drupal\Core\TypedData\Type\Uri
+   * @see \Drupal\Core\TypedData\Type\Binary
+   * @see \Drupal\Core\Entity\Field\EntityWrapper
    */
-  function create(array $definition, $value = NULL, array $context = array()) {
-    $wrapper = $this->createInstance($definition['type'], $definition);
+  public function create(array $definition, $value = NULL, $name = NULL, $parent = NULL) {
+    $wrapper = $this->factory->createInstance($definition['type'], $definition, $name, $parent);
     if (isset($value)) {
       $wrapper->setValue($value);
     }
-    if ($wrapper instanceof ContextAwareInterface) {
-      if (isset($context['name'])) {
-        $wrapper->setName($context['name']);
+    return $wrapper;
+  }
+
+  /**
+   * Implements \Drupal\Component\Plugin\PluginManagerInterface::getInstance().
+   *
+   * @param array $options
+   *   An array of options with the following keys:
+   *   - object: The parent typed data object, implementing the
+   *     ContextAwareInterface and either the ListInterface or the
+   *     ComplexDataInterface.
+   *   - property: The name of the property to instantiate, or the delta of the
+   *     the list item to instantiate.
+   *   - value: The value to set. If set, it has to match one of the supported
+   *     data type formats as documented by the data type classes.
+   *
+   * @throws \InvalidArgumentException
+   *   If the given property is not known, or the passed object does not
+   *   implement the ListInterface or the ComplexDataInterface.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The new property instance.
+   *
+   * @see \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance()
+   */
+  public function getInstance(array $options) {
+    return $this->getPropertyInstance($options['object'], $options['property'], $options['value']);
+  }
+
+  /**
+   * Get a typed data instance for a property of a given typed data object.
+   *
+   * If the object has a namespace and property path specified, this method
+   * will use prototyping for fast and efficient instantiation of many property
+   * objects with the same namespace and property path; e.g., if
+   * comment.comment_body.0.value is instantiated very often when multiple
+   * comments are used.
+   *
+   * @param ContextAwareInterface $object
+   *   The parent typed data object, implementing the ContextAwareInterface and
+   *   either the ListInterface or the ComplexDataInterface.
+   * @param string $property_name
+   *   The name of the property to instantiate, or the delta of an list item.
+   * @param mixed $value
+   *   (optional) The data value. If set, it has to match one of the supported
+   *   data type formats as documented by the data type classes.
+   *
+   * @throws \InvalidArgumentException
+   *   If the given property is not known, or the passed object does not
+   *   implement the ListInterface or the ComplexDataInterface.
+   *
+   * @return \Drupal\Core\TypedData\TypedDataInterface
+   *   The new property instance.
+   *
+   * @see \Drupal\Core\TypedData\TypedDataManager::create()
+   */
+  public function getPropertyInstance(ContextAwareInterface $object, $property_name, $value = NULL) {
+    $namespace = $object->getNamespace();
+    $key = $namespace . ':' . $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;
+
+    // If a namespace is given, make sure we have a prototype. Then, clone the
+    // prototype and set object specific values, i.e. the value and the context.
+    if (!$namespace || !isset($this->prototypes[$key])) {
+      if ($object instanceof ComplexDataInterface) {
+        $definition = $object->getPropertyDefinition($property_name);
       }
-      if (isset($context['parent'])) {
-        $wrapper->setParent($context['parent']);
+      elseif ($object instanceof ListInterface) {
+        $definition = $object->getItemDefinition();
       }
+      else {
+        throw new InvalidArgumentException("The passed object has to either implement the ComplexDataInterface or the ListInterface.");
+      }
+      // Make sure we have got a valid definition.
+      if (!$definition) {
+        throw new InvalidArgumentException('Property ' . check_plain($property_name) . ' is unknown.');
+      }
+
+      $this->prototypes[$key] = $this->create($definition, NULL, $property_name, $object);
     }
-    return $wrapper;
+
+    $property = clone $this->prototypes[$key];
+    // Update the parent relationship if necessary.
+    if ($property instanceof ContextAwareInterface) {
+      $property->setContext($property_name, $object);
+    }
+    // Set the passed data value.
+    if (isset($value)) {
+      $property->setValue($value);
+    }
+    return $property;
   }
 }
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index b81fed3..6e92304 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -109,41 +109,40 @@ function comment_admin_overview($form, &$form_state, $arg) {
   foreach ($comments as $comment) {
     // Remove the first node title from the node_titles array and attach to
     // the comment.
-    $comment->node_title = array_shift($node_titles);
-    $comment_body = field_get_items('comment', $comment, 'comment_body');
-    $options[$comment->cid] = array(
+    $node_title = array_shift($node_titles);
+    $options[$comment->cid->value] = array(
       'subject' => array(
         'data' => array(
           '#type' => 'link',
-          '#title' => $comment->subject,
-          '#href' => 'comment/' . $comment->cid,
-          '#options' => array('attributes' => array('title' => truncate_utf8($comment_body[0]['value'], 128)), 'fragment' => 'comment-' . $comment->cid),
+          '#title' => $comment->subject->value,
+          '#href' => 'comment/' . $comment->id(),
+          '#options' => array('attributes' => array('title' => truncate_utf8($comment->comment_body->value, 128)), 'fragment' => 'comment-' . $comment->id()),
         ),
       ),
-      'author' => theme('username', array('account' => $comment)),
+      'author' => theme('username', array('account' => comment_prepare_author($comment))),
       'posted_in' => array(
         'data' => array(
           '#type' => 'link',
-          '#title' => $comment->node_title,
-          '#href' => 'node/' . $comment->nid,
+          '#title' => $node_title,
+          '#href' => 'node/' . $comment->nid->value,
         ),
       ),
-      'changed' => format_date($comment->changed, 'short'),
+      'changed' => format_date($comment->changed->value->getTimestamp(), 'short'),
     );
     $links = array();
     $links['edit'] = array(
       'title' => t('edit'),
-      'href' => 'comment/' . $comment->cid . '/edit',
+      'href' => 'comment/' . $comment->cid->value . '/edit',
       'query' => $destination,
     );
     if (module_invoke('translation_entity', 'translate_access', $comment)) {
       $links['translate'] = array(
         'title' => t('translate'),
-        'href' => 'comment/' . $comment->cid . '/translations',
+        'href' => 'comment/' . $comment->id() . '/translations',
         'query' => $destination,
       );
     }
-    $options[$comment->cid]['operations']['data'] = array(
+    $options[$comment->id()]['operations']['data'] = array(
       '#type' => 'operations',
       '#links' => $links,
     );
@@ -194,10 +193,10 @@ function comment_admin_overview_submit($form, &$form_state) {
       $comment = comment_load($value);
 
       if ($operation == 'unpublish') {
-        $comment->status = COMMENT_NOT_PUBLISHED;
+        $comment->status->value = COMMENT_NOT_PUBLISHED;
       }
       elseif ($operation == 'publish') {
-        $comment->status = COMMENT_PUBLISHED;
+        $comment->status->value = COMMENT_PUBLISHED;
       }
       comment_save($comment);
     }
@@ -226,7 +225,7 @@ function comment_multiple_delete_confirm($form, &$form_state) {
   $comment_counter = 0;
   foreach (array_filter($edit['comments']) as $cid => $value) {
     $comment = comment_load($cid);
-    if (is_object($comment) && is_numeric($comment->cid)) {
+    if (is_object($comment) && is_numeric($comment->cid->value)) {
       $subject = db_query('SELECT subject FROM {comment} WHERE cid = :cid', array(':cid' => $cid))->fetchField();
       $form['comments'][$cid] = array('#type' => 'hidden', '#value' => $cid, '#prefix' => '<li>', '#suffix' => check_plain($subject) . '</li>');
       $comment_counter++;
@@ -290,11 +289,11 @@ function comment_confirm_delete_page($cid) {
 function comment_confirm_delete($form, &$form_state, Comment $comment) {
   $form_state['comment'] = $comment;
   // Always provide entity id in the same form key as in the entity edit form.
-  $form['cid'] = array('#type' => 'value', '#value' => $comment->cid);
+  $form['cid'] = array('#type' => 'value', '#value' => $comment->cid->value);
   return confirm_form(
     $form,
-    t('Are you sure you want to delete the comment %title?', array('%title' => $comment->subject)),
-    'node/' . $comment->nid,
+    t('Are you sure you want to delete the comment %title?', array('%title' => $comment->subject->value)),
+    'node/' . $comment->nid->value,
     t('Any replies to this comment will be lost. This action cannot be undone.'),
     t('Delete'),
     t('Cancel'),
@@ -307,11 +306,11 @@ function comment_confirm_delete($form, &$form_state, Comment $comment) {
 function comment_confirm_delete_submit($form, &$form_state) {
   $comment = $form_state['comment'];
   // Delete the comment and its replies.
-  comment_delete($comment->cid);
+  comment_delete($comment->cid->value);
   drupal_set_message(t('The comment and all its replies have been deleted.'));
-  watchdog('content', 'Deleted comment @cid and its replies.', array('@cid' => $comment->cid));
+  watchdog('content', 'Deleted comment @cid and its replies.', array('@cid' => $comment->cid->value));
   // Clear the cache so an anonymous user sees that his comment was deleted.
   cache_invalidate_tags(array('content' => TRUE));
 
-  $form_state['redirect'] = "node/$comment->nid";
+  $form_state['redirect'] = "node/{$comment->nid->value}";
 }
diff --git a/core/modules/comment/comment.api.php b/core/modules/comment/comment.api.php
index 95c3e1d..c8548a3 100644
--- a/core/modules/comment/comment.api.php
+++ b/core/modules/comment/comment.api.php
@@ -23,7 +23,7 @@
  */
 function hook_comment_presave(Drupal\comment\Comment $comment) {
   // Remove leading & trailing spaces from the comment subject.
-  $comment->subject = trim($comment->subject);
+  $comment->subject->value = trim($comment->subject->value);
 }
 
 /**
@@ -34,7 +34,7 @@ function hook_comment_presave(Drupal\comment\Comment $comment) {
  */
 function hook_comment_insert(Drupal\comment\Comment $comment) {
   // Reindex the node when comments are added.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -45,7 +45,7 @@ function hook_comment_insert(Drupal\comment\Comment $comment) {
  */
 function hook_comment_update(Drupal\comment\Comment $comment) {
   // Reindex the node when comments are updated.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -75,7 +75,7 @@ function hook_comment_load(Drupal\comment\Comment $comments) {
  */
 function hook_comment_view(Drupal\comment\Comment $comment, $view_mode, $langcode) {
   // how old is the comment
-  $comment->time_ago = time() - $comment->changed;
+  $comment->time_ago->value = time() - $comment->changed->value->getTimestamp();
 }
 
 /**
@@ -117,7 +117,7 @@ function hook_comment_view_alter(&$build, Drupal\comment\Comment $comment) {
  *   The comment the action is being performed on.
  */
 function hook_comment_publish(Drupal\comment\Comment $comment) {
-  drupal_set_message(t('Comment: @subject has been published', array('@subject' => $comment->subject)));
+  drupal_set_message(t('Comment: @subject has been published', array('@subject' => $comment->subject->value)));
 }
 
 /**
@@ -127,7 +127,7 @@ function hook_comment_publish(Drupal\comment\Comment $comment) {
  *   The comment the action is being performed on.
  */
 function hook_comment_unpublish(Drupal\comment\Comment $comment) {
-  drupal_set_message(t('Comment: @subject has been unpublished', array('@subject' => $comment->subject)));
+  drupal_set_message(t('Comment: @subject has been unpublished', array('@subject' => $comment->subject->value)));
 }
 
 /**
@@ -147,7 +147,7 @@ function hook_comment_unpublish(Drupal\comment\Comment $comment) {
 function hook_comment_predelete(Drupal\comment\Comment $comment) {
   // Delete a record associated with the comment in a custom table.
   db_delete('example_comment_table')
-    ->condition('cid', $comment->cid)
+    ->condition('cid', $comment->cid->value)
     ->execute();
 }
 
@@ -166,7 +166,7 @@ function hook_comment_predelete(Drupal\comment\Comment $comment) {
  * @see entity_delete_multiple()
  */
 function hook_comment_delete(Drupal\comment\Comment $comment) {
-  drupal_set_message(t('Comment: @subject has been deleted', array('@subject' => $comment->subject)));
+  drupal_set_message(t('Comment: @subject has been deleted', array('@subject' => $comment->subject->value)));
 }
 
 /**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index ab757a9..2d7e022 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -147,8 +147,8 @@ function comment_node_type_load($name) {
  */
 function comment_uri(Comment $comment) {
   return array(
-    'path' => 'comment/' . $comment->cid,
-    'options' => array('fragment' => 'comment-' . $comment->cid),
+    'path' => 'comment/' . $comment->cid->value,
+    'options' => array('fragment' => 'comment-' . $comment->cid->value),
   );
 }
 
@@ -474,10 +474,10 @@ function comment_block_view($delta = '') {
  *   The comment listing set to the page on which the comment appears.
  */
 function comment_permalink($cid) {
-  if (($comment = comment_load($cid)) && ($node = node_load($comment->nid))) {
+  if (($comment = comment_load($cid)) && ($node = $comment->nid->entity)) {
 
     // Find the current display page for this comment.
-    $page = comment_get_display_page($comment->cid, $node->type);
+    $page = comment_get_display_page($comment->cid->value, $node->type);
 
     // @todo: Cleaner sub request handling.
     $request = drupal_container()->get('request');
@@ -906,7 +906,7 @@ function comment_prepare_thread(&$comments) {
   $divs = 0;
 
   foreach ($comments as $key => $comment) {
-    if ($first_new && $comment->new != MARK_READ) {
+    if ($first_new && $comment->new->value != MARK_READ) {
       // Assign the anchor only for the first new comment. This avoids duplicate
       // id attributes on a page.
       $first_new = FALSE;
@@ -915,7 +915,7 @@ function comment_prepare_thread(&$comments) {
 
     // The $divs element instructs #prefix whether to add an indent div or
     // close existing divs (a negative value).
-    $comment->depth = count(explode('.', $comment->thread)) - 1;
+    $comment->depth = count(explode('.', $comment->thread->value)) - 1;
     if ($comment->depth > $divs) {
       $comment->divs = 1;
       $divs++;
@@ -968,25 +968,25 @@ function comment_links(Comment $comment, Node $node) {
     if (user_access('administer comments') && user_access('post comments')) {
       $links['comment-delete'] = array(
         'title' => t('delete'),
-        'href' => "comment/$comment->cid/delete",
+        'href' => "comment/{$comment->cid->value}/delete",
         'html' => TRUE,
       );
       $links['comment-edit'] = array(
         'title' => t('edit'),
-        'href' => "comment/$comment->cid/edit",
+        'href' => "comment/{$comment->cid->value}/edit",
         'html' => TRUE,
       );
       $links['comment-reply'] = array(
         'title' => t('reply'),
-        'href' => "comment/reply/$comment->nid/$comment->cid",
+        'href' => "comment/reply/{$comment->nid->value}/{$comment->cid->value}",
         'html' => TRUE,
       );
-      if ($comment->status == COMMENT_NOT_PUBLISHED) {
+      if ($comment->status->value == COMMENT_NOT_PUBLISHED) {
         $links['comment-approve'] = array(
           'title' => t('approve'),
-          'href' => "comment/$comment->cid/approve",
+          'href' => "comment/{$comment->cid->value}/approve",
           'html' => TRUE,
-          'query' => array('token' => drupal_get_token("comment/$comment->cid/approve")),
+          'query' => array('token' => drupal_get_token("comment/{$comment->cid->value}/approve")),
         );
       }
     }
@@ -994,13 +994,13 @@ function comment_links(Comment $comment, Node $node) {
       if (comment_access('edit', $comment)) {
         $links['comment-edit'] = array(
           'title' => t('edit'),
-          'href' => "comment/$comment->cid/edit",
+          'href' => "comment/{$comment->cid->value}/edit",
           'html' => TRUE,
         );
       }
       $links['comment-reply'] = array(
         'title' => t('reply'),
-        'href' => "comment/reply/$comment->nid/$comment->cid",
+        'href' => "comment/reply/{$comment->nid->value}/{$comment->cid->value}",
         'html' => TRUE,
       );
     }
@@ -1347,7 +1347,7 @@ function comment_user_cancel($edit, $account, $method) {
     case 'user_cancel_block_unpublish':
       $comments = entity_load_multiple_by_properties('comment', array('uid' => $account->uid));
       foreach ($comments as $comment) {
-        $comment->status = 0;
+        $comment->status->value = 0;
         comment_save($comment);
       }
       break;
@@ -1355,7 +1355,7 @@ function comment_user_cancel($edit, $account, $method) {
     case 'user_cancel_reassign':
       $comments = entity_load_multiple_by_properties('comment', array('uid' => $account->uid));
       foreach ($comments as $comment) {
-        $comment->uid = 0;
+        $comment->uid->value = 0;
         comment_save($comment);
       }
       break;
@@ -1390,7 +1390,7 @@ function comment_access($op, Comment $comment) {
   global $user;
 
   if ($op == 'edit') {
-    return ($user->uid && $user->uid == $comment->uid && $comment->status == COMMENT_PUBLISHED && user_access('edit own comments')) || user_access('administer comments');
+    return ($user->uid && $user->uid == $comment->uid->value && $comment->status->value == COMMENT_PUBLISHED && user_access('edit own comments')) || user_access('administer comments');
   }
 }
 
@@ -1570,7 +1570,7 @@ function comment_get_display_page($cid, $node_type) {
  * @see comment_menu()
  */
 function comment_edit_page(Comment $comment) {
-  drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject)), PASS_THROUGH);
+  drupal_set_title(t('Edit comment %comment', array('%comment' => $comment->subject->value)), PASS_THROUGH);
   return entity_get_form($comment);
 }
 
@@ -1584,28 +1584,25 @@ function comment_preview(Comment $comment) {
   $preview_build = array();
 
   if (!form_get_errors()) {
-    $comment_body = field_get_items('comment', $comment, 'comment_body');
-    $comment->format = $comment_body[0]['format'];
+    $comment->format = $comment->comment_body->format;
     // Attach the user and time information.
-    if (!empty($comment->name)) {
-      $account = user_load_by_name($comment->name);
+    if (!empty($comment->name->value)) {
+      $account = user_load_by_name($comment->name->value);
     }
     elseif ($user->uid && empty($comment->is_anonymous)) {
       $account = $user;
     }
 
     if (!empty($account->uid)) {
-      $comment->uid = $account->uid;
-      $comment->name = check_plain($account->name);
-      $comment->signature = $account->signature;
-      $comment->signature_format = $account->signature_format;
+      $comment->uid->value = $account->uid;
+      $comment->name->value = check_plain($account->name);
     }
-    elseif (empty($comment->name)) {
-      $comment->name = config('user.settings')->get('anonymous');
+    elseif (empty($comment->name->value)) {
+      $comment->name->value = config('user.settings')->get('anonymous');
     }
 
-    $comment->created = !empty($comment->created) ? $comment->created : REQUEST_TIME;
-    $comment->changed = REQUEST_TIME;
+    $comment->created->value = !empty($comment->created->value) ? $comment->created->value : REQUEST_TIME;
+    $comment->changed->value = REQUEST_TIME;
     $comment->in_preview = TRUE;
     $comment_build = comment_view($comment);
     $comment_build['#weight'] = -100;
@@ -1613,15 +1610,15 @@ function comment_preview(Comment $comment) {
     $preview_build['comment_preview'] = $comment_build;
   }
 
-  if ($comment->pid) {
+  if ($comment->pid->value) {
     $build = array();
-    $comment = comment_load($comment->pid);
-    if ($comment && $comment->status == COMMENT_PUBLISHED) {
+    $comment = $comment->pid->entity;
+    if ($comment && $comment->status->value == COMMENT_PUBLISHED) {
       $build = comment_view($comment);
     }
   }
   else {
-    $build = node_view(node_load($comment->nid));
+    $build = node_view($comment->nid->entity);
   }
 
   $preview_build['comment_output_below'] = $build;
@@ -1640,6 +1637,24 @@ function comment_preprocess_block(&$variables) {
 }
 
 /**
+ * Prepares a user account object for rendering comment authors.
+ *
+ * This helper handles anonymous authors in addition to registered comment
+ * authors.
+ *
+ * @return \Drupal\user\Plugin\Core\Entity\User
+ *   A user account, for use with theme_username() or the user_picture template.
+ */
+function comment_prepare_author(Comment $comment) {
+  // The account has been pre-loaded by CommentRenderController::buildContent().
+  $account = $comment->uid->entity;
+  if (!$account) {
+    $account = entity_create('user', array('uid' => 0, 'name' => $comment->name->value, 'homepage' => $comment->homepage->value));
+  }
+  return $account;
+}
+
+/**
  * Preprocesses variables for comment.tpl.php.
  *
  * @see comment.tpl.php
@@ -1649,38 +1664,46 @@ function template_preprocess_comment(&$variables) {
   $node = $variables['elements']['#node'];
   $variables['comment'] = $comment;
   $variables['node'] = $node;
-  $variables['author'] = theme('username', array('account' => $comment));
-  $variables['created'] = format_date($comment->created);
-  $variables['changed'] = format_date($comment->changed);
 
-  $variables['new'] = !empty($comment->new) ? t('new') : '';
+  $account = comment_prepare_author($comment);
+  $variables['author'] = theme('username', array('account' => $account));
+  $variables['created'] = format_date($comment->created->value->getTimestamp());
+  $variables['changed'] = format_date($comment->changed->value->getTimestamp());
+  $variables['new'] = $comment->new->value ? t('new') : '';
+
   if (theme_get_setting('toggle_comment_user_picture')) {
     // To change user picture settings (e.g., image style), edit the 'compact'
     // view mode on the User entity.
-    $variables['user_picture'] = user_view($comment->account, 'compact');
+    $variables['user_picture'] = user_view($account, 'compact');
   }
   else {
     $variables['user_picture'] = array();
   }
-  $variables['signature'] = $comment->signature;
+  if (config('user.settings')->get('signatures') && !empty($account->signature)) {
+    $variables['signature'] = check_markup($account->signature, $account->signature_format, '', TRUE) ;
+  }
+  else {
+    $variables['signature'] = '';
+  }
 
   $uri = $comment->uri();
   $uri['options'] += array('attributes' => array('class' => 'permalink', 'rel' => 'bookmark'));
 
-  $variables['title'] = l($comment->subject, $uri['path'], $uri['options']);
+  $variables['title'] = l($comment->subject->value, $uri['path'], $uri['options']);
   $variables['permalink'] = l(t('Permalink'), $uri['path'], $uri['options']);
   $variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['author'], '!datetime' => $variables['created']));
 
-  if ($comment->pid > 0) {
+  if ($comment->pid->value) {
     // Fetch and store the parent comment information for use in templates.
-    $comment_parent = comment_load($comment->pid);
+    $comment_parent = $comment->pid->entity;
+    $account_parent = comment_prepare_author($comment);
     $variables['parent_comment'] = $comment_parent;
-    $variables['parent_author'] = theme('username', array('account' => $comment_parent));
-    $variables['parent_created'] = format_date($comment_parent->created);
-    $variables['parent_changed'] = format_date($comment_parent->changed);
+    $variables['parent_author'] = theme('username', array('account' => $account_parent));
+    $variables['parent_created'] = format_date($comment_parent->created->value->getTimestamp());
+    $variables['parent_changed'] = format_date($comment_parent->changed->value->getTimestamp());
     $uri_parent = $comment_parent->uri();
     $uri_parent['options'] += array('attributes' => array('class' => 'permalink', 'rel' => 'bookmark'));
-    $variables['parent_title'] = l($comment_parent->subject, $uri_parent['path'], $uri_parent['options']);
+    $variables['parent_title'] = l($comment_parent->subject->value, $uri_parent['path'], $uri_parent['options']);
     $variables['parent_permalink'] = l(t('Parent permalink'), $uri_parent['path'], $uri_parent['options']);
     $variables['parent'] = t('In reply to !parent_title by !parent_username',
         array('!parent_username' => $variables['parent_author'], '!parent_title' => $variables['parent_title']));
@@ -1696,7 +1719,7 @@ function template_preprocess_comment(&$variables) {
   }
 
   // Preprocess fields.
-  field_attach_preprocess('comment', $comment, $variables['elements'], $variables);
+  field_attach_preprocess('comment', $comment->getBCEntity(), $variables['elements'], $variables);
 
   // Helpful $content variable for templates.
   foreach (element_children($variables['elements']) as $key) {
@@ -1708,7 +1731,7 @@ function template_preprocess_comment(&$variables) {
     $variables['status'] = 'preview';
   }
   else {
-    $variables['status'] = ($comment->status == COMMENT_NOT_PUBLISHED) ? 'unpublished' : 'published';
+    $variables['status'] = ($comment->status->value == COMMENT_NOT_PUBLISHED) ? 'unpublished' : 'published';
   }
 
   // Gather comment classes.
@@ -1719,14 +1742,14 @@ function template_preprocess_comment(&$variables) {
   if ($variables['new']) {
     $variables['attributes']['class'][] = 'new';
   }
-  if (!$comment->uid) {
+  if (!$comment->uid->value) {
     $variables['attributes']['class'][] = 'by-anonymous';
   }
   else {
-    if ($comment->uid == $variables['node']->uid) {
+    if ($comment->uid->value == $variables['node']->uid) {
       $variables['attributes']['class'][] = 'by-node-author';
     }
-    if ($comment->uid == $variables['user']->uid) {
+    if ($comment->uid->value == $variables['user']->uid) {
       $variables['attributes']['class'][] = 'by-viewer';
     }
   }
@@ -1888,9 +1911,9 @@ function comment_action_info() {
  * @ingroup actions
  */
 function comment_publish_action(Comment $comment = NULL, $context = array()) {
-  if (isset($comment->subject)) {
-    $subject = $comment->subject;
-    $comment->status = COMMENT_PUBLISHED;
+  if (isset($comment->subject->value)) {
+    $subject = $comment->subject->value;
+    $comment->status->value = COMMENT_PUBLISHED;
   }
   else {
     $cid = $context['cid'];
@@ -1915,9 +1938,9 @@ function comment_publish_action(Comment $comment = NULL, $context = array()) {
  * @ingroup actions
  */
 function comment_unpublish_action(Comment $comment = NULL, $context = array()) {
-  if (isset($comment->subject)) {
-    $subject = $comment->subject;
-    $comment->status = COMMENT_NOT_PUBLISHED;
+  if (isset($comment->subject->value)) {
+    $subject = $comment->subject->value;
+    $comment->status->value = COMMENT_NOT_PUBLISHED;
   }
   else {
     $cid = $context['cid'];
@@ -1948,8 +1971,8 @@ function comment_unpublish_by_keyword_action(Comment $comment, $context) {
   foreach ($context['keywords'] as $keyword) {
     $text = drupal_render($comment);
     if (strpos($text, $keyword) !== FALSE) {
-      $comment->status = COMMENT_NOT_PUBLISHED;
-      watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject));
+      $comment->status->value = COMMENT_NOT_PUBLISHED;
+      watchdog('action', 'Unpublished comment %subject.', array('%subject' => $comment->subject->value));
       break;
     }
   }
@@ -1992,7 +2015,7 @@ function comment_unpublish_by_keyword_action_submit($form, $form_state) {
 function comment_save_action(Comment $comment) {
   comment_save($comment);
   cache_invalidate_tags(array('content' => TRUE));
-  watchdog('action', 'Saved comment %title', array('%title' => $comment->subject));
+  watchdog('action', 'Saved comment %title', array('%title' => $comment->subject->value));
 }
 
 /**
@@ -2062,8 +2085,8 @@ function comment_rdf_mapping() {
  */
 function comment_file_download_access($field, EntityInterface $entity, File $file) {
   if ($entity->entityType() == 'comment') {
-    if (user_access('access comments') && $entity->status == COMMENT_PUBLISHED || user_access('administer comments')) {
-      $node = node_load($entity->nid);
+    if (user_access('access comments') && $entity->status->value == COMMENT_PUBLISHED || user_access('administer comments')) {
+      $node = $entity->nid->entity;
       return node_access('view', $node);
     }
     return FALSE;
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index 6263660..e333dd7 100644
--- a/core/modules/comment/comment.pages.inc
+++ b/core/modules/comment/comment.pages.inc
@@ -56,18 +56,15 @@ function comment_reply(Node $node, $pid = NULL) {
       if (user_access('access comments')) {
         // Load the parent comment.
         $comment = comment_load($pid);
-        if ($comment->status == COMMENT_PUBLISHED) {
+        if ($comment->status->value == COMMENT_PUBLISHED) {
           // If that comment exists, make sure that the current comment and the
           // parent comment both belong to the same parent node.
-          if ($comment->nid != $node->nid) {
+          if ($comment->nid->value != $node->nid) {
             // Attempting to reply to a comment not belonging to the current nid.
             drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
             drupal_goto("node/$node->nid");
           }
           // Display the parent comment
-          $comment->node_type = 'comment_node_' . $node->type;
-          field_attach_load('comment', array($comment->cid => $comment));
-          $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
           $build['comment_parent'] = comment_view($comment);
         }
         else {
@@ -120,11 +117,11 @@ function comment_approve($cid) {
   }
 
   if ($comment = comment_load($cid)) {
-    $comment->status = COMMENT_PUBLISHED;
+    $comment->status->value = COMMENT_PUBLISHED;
     comment_save($comment);
 
     drupal_set_message(t('Comment approved.'));
-    drupal_goto('node/' . $comment->nid);
+    drupal_goto('node/' . $comment->nid->value);
   }
   throw new NotFoundHttpException();
 }
diff --git a/core/modules/comment/comment.tokens.inc b/core/modules/comment/comment.tokens.inc
index c77cb67..4b93a71 100644
--- a/core/modules/comment/comment.tokens.inc
+++ b/core/modules/comment/comment.tokens.inc
@@ -122,79 +122,75 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
       switch ($name) {
         // Simple key values on the comment.
         case 'cid':
-          $replacements[$original] = $comment->cid;
+          $replacements[$original] = $comment->cid->value;
           break;
 
         // Poster identity information for comments
         case 'hostname':
-          $replacements[$original] = $sanitize ? check_plain($comment->hostname) : $comment->hostname;
+          $replacements[$original] = $sanitize ? check_plain($comment->hostname->value) : $comment->hostname->value;
           break;
 
         case 'name':
-          $name = ($comment->uid == 0) ? config('user.settings')->get('anonymous') : $comment->name;
+          $name = ($comment->uid->value == 0) ? config('user.settings')->get('anonymous') : $comment->name->value;
           $replacements[$original] = $sanitize ? filter_xss($name) : $name;
           break;
 
         case 'mail':
-          if ($comment->uid != 0) {
-            $account = user_load($comment->uid);
+          if ($comment->uid->value != 0) {
+            $account = user_load($comment->uid->value);
             $mail = $account->mail;
           }
           else {
-            $mail = $comment->mail;
+            $mail = $comment->mail->value;
           }
           $replacements[$original] = $sanitize ? check_plain($mail) : $mail;
           break;
 
         case 'homepage':
-          $replacements[$original] = $sanitize ? check_url($comment->homepage) : $comment->homepage;
+          $replacements[$original] = $sanitize ? check_url($comment->homepage->value) : $comment->homepage->value;
           break;
 
         case 'title':
-          $replacements[$original] = $sanitize ? filter_xss($comment->subject) : $comment->subject;
+          $replacements[$original] = $sanitize ? filter_xss($comment->subject->value) : $comment->subject->value;
           break;
 
         case 'body':
-          if ($items = field_get_items('comment', $comment, 'comment_body', $langcode)) {
-            $instance = field_info_instance('comment', 'body', 'comment_body');
-            $field_langcode = field_language('comment', $comment, 'comment_body', $langcode);
-            $replacements[$original] = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'value') : $items[0]['value'];
-          }
+          $replacements[$original] = $sanitize ? $comment->comment_body->processed : $comment->comment_body->value;
           break;
 
         // Comment related URLs.
         case 'url':
-          $url_options['fragment']  = 'comment-' . $comment->cid;
-          $replacements[$original] = url('comment/' . $comment->cid, $url_options);
+          $url_options['fragment']  = 'comment-' . $comment->cid->value;
+          $replacements[$original] = url('comment/' . $comment->cid->value, $url_options);
           break;
 
         case 'edit-url':
           $url_options['fragment'] = NULL;
-          $replacements[$original] = url('comment/' . $comment->cid . '/edit', $url_options);
+          $replacements[$original] = url('comment/' . $comment->cid->value . '/edit', $url_options);
           break;
 
         // Default values for the chained tokens handled below.
         case 'author':
-          $replacements[$original] = $sanitize ? filter_xss($comment->name) : $comment->name;
+          $replacements[$original] = $sanitize ? filter_xss($comment->name->value) : $comment->name->value;
           break;
 
         case 'parent':
-          if (!empty($comment->pid)) {
-            $parent = comment_load($comment->pid);
+          if (!empty($comment->pid->value)) {
+            $parent = comment_load($comment->pid->value);
             $replacements[$original] = $sanitize ? filter_xss($parent->subject) : $parent->subject;
           }
           break;
 
         case 'created':
-          $replacements[$original] = format_date($comment->created, 'medium', '', NULL, $langcode);
+          $replacements[$original] = format_date($comment->created->value->getTimestamp(), 'medium', '', NULL, $langcode);
           break;
 
         case 'changed':
-          $replacements[$original] = format_date($comment->changed, 'medium', '', NULL, $langcode);
+          $replacements[$original] = format_date($comment->changed->value->getTimestamp(), 'medium', '', NULL, $langcode);
           break;
 
         case 'node':
-          $node = node_load($comment->nid);
+          $node = $comment->nid->entity;
           $title = $node->label();
           $replacements[$original] = $sanitize ? filter_xss($title) : $title;
           break;
@@ -203,23 +199,23 @@ function comment_tokens($type, $tokens, array $data = array(), array $options =
 
     // Chained token relationships.
     if ($node_tokens = token_find_with_prefix($tokens, 'node')) {
-      $node = node_load($comment->nid);
+      $node = $comment->nid->entity;
       $replacements += token_generate('node', $node_tokens, array('node' => $node), $options);
     }
 
     if ($date_tokens = token_find_with_prefix($tokens, 'created')) {
-      $replacements += token_generate('date', $date_tokens, array('date' => $comment->created), $options);
+      $replacements += token_generate('date', $date_tokens, array('date' => $comment->created->value->getTimestamp()), $options);
     }
 
     if ($date_tokens = token_find_with_prefix($tokens, 'changed')) {
-      $replacements += token_generate('date', $date_tokens, array('date' => $comment->changed), $options);
+      $replacements += token_generate('date', $date_tokens, array('date' => $comment->changed->value->getTimestamp()), $options);
     }
 
-    if (($parent_tokens = token_find_with_prefix($tokens, 'parent')) && $parent = comment_load($comment->pid)) {
+    if (($parent_tokens = token_find_with_prefix($tokens, 'parent')) && $parent = $comment->pid->entity) {
       $replacements += token_generate('comment', $parent_tokens, array('comment' => $parent), $options);
     }
 
-    if (($author_tokens = token_find_with_prefix($tokens, 'author')) && $account = user_load($comment->uid)) {
+    if (($author_tokens = token_find_with_prefix($tokens, 'author')) && $account = $comment->uid->entity) {
       $replacements += token_generate('user', $author_tokens, array('user' => $account), $options);
     }
   }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index 9baa5c5..865ead9 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -9,28 +9,26 @@
 
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\EntityFormController;
+use Drupal\Core\Entity\EntityFormControllerNG;
 
 /**
  * Base for controller for comment forms.
  */
-class CommentFormController extends EntityFormController {
+class CommentFormController extends EntityFormControllerNG {
 
   /**
    * Overrides Drupal\Core\Entity\EntityFormController::form().
    */
   public function form(array $form, array &$form_state, EntityInterface $comment) {
     global $user;
-
-    $node = node_load($comment->nid);
-    $form_state['comment']['node'] = $node;
+    $node = $comment->nid->entity;
 
     // Use #comment-form as unique jump target, regardless of node type.
     $form['#id'] = drupal_html_id('comment_form');
     $form['#theme'] = array('comment_form__node_' . $node->type, 'comment_form');
 
     $anonymous_contact = variable_get('comment_anonymous_' . $node->type, COMMENT_ANONYMOUS_MAYNOT_CONTACT);
-    $is_admin = (!empty($comment->cid) && user_access('administer comments'));
+    $is_admin = (!empty($comment->cid->value) && user_access('administer comments'));
 
     if (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT) {
       $form['#attached']['library'][] = array('system', 'jquery.cookie');
@@ -39,8 +37,8 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
 
     // If not replying to a comment, use our dedicated page callback for new
     // comments on nodes.
-    if (empty($comment->cid) && empty($comment->pid)) {
-      $form['#action'] = url('comment/reply/' . $comment->nid);
+    if (empty($comment->cid->value) && empty($comment->pid->value)) {
+      $form['#action'] = url('comment/reply/' . $comment->nid->value);
     }
 
     if (isset($form_state['comment_preview'])) {
@@ -62,16 +60,16 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
 
     // Prepare default values for form elements.
     if ($is_admin) {
-      $author = (!$comment->uid && $comment->name ? $comment->name : $comment->registered_name);
-      $status = (isset($comment->status) ? $comment->status : COMMENT_NOT_PUBLISHED);
-      $date = (!empty($comment->date) ? $comment->date : format_date($comment->created, 'custom', 'Y-m-d H:i O'));
+      $author = $comment->name->value;
+      $status = (isset($comment->status->value) ? $comment->status->value : COMMENT_NOT_PUBLISHED);
+      $date = (!empty($comment->date) ? $comment->date : format_date($comment->created->value->getTimestamp(), 'custom', 'Y-m-d H:i O'));
     }
     else {
       if ($user->uid) {
         $author = $user->name;
       }
       else {
-        $author = ($comment->name ? $comment->name : '');
+        $author = ($comment->name->value ? $comment->name->value : '');
       }
       $status = (user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED);
       $date = '';
@@ -116,7 +114,7 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
     $form['author']['mail'] = array(
       '#type' => 'email',
       '#title' => t('E-mail'),
-      '#default_value' => $comment->mail,
+      '#default_value' => $comment->mail->value,
       '#required' => (!$user->uid && $anonymous_contact == COMMENT_ANONYMOUS_MUST_CONTACT),
       '#maxlength' => 64,
       '#size' => 30,
@@ -127,7 +125,7 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
     $form['author']['homepage'] = array(
       '#type' => 'url',
       '#title' => t('Homepage'),
-      '#default_value' => $comment->homepage,
+      '#default_value' => $comment->homepage->value,
       '#maxlength' => 255,
       '#size' => 30,
       '#access' => $is_admin || (!$user->uid && $anonymous_contact != COMMENT_ANONYMOUS_MAYNOT_CONTACT),
@@ -158,36 +156,27 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
       '#type' => 'textfield',
       '#title' => t('Subject'),
       '#maxlength' => 64,
-      '#default_value' => $comment->subject,
+      '#default_value' => $comment->subject->value,
       '#access' => variable_get('comment_subject_field_' . $node->type, 1) == 1,
     );
 
     // Used for conditional validation of author fields.
     $form['is_anonymous'] = array(
       '#type' => 'value',
-      '#value' => ($comment->cid ? !$comment->uid : !$user->uid),
+      '#value' => ($comment->cid->value ? !$comment->uid->value : !$user->uid),
     );
 
-    // Add internal comment properties.
-    foreach (array('cid', 'pid', 'nid', 'uid') as $key) {
-      $form[$key] = array('#type' => 'value', '#value' => $comment->$key);
-    }
-    $form['node_type'] = array('#type' => 'value', '#value' => 'comment_node_' . $node->type);
-
     // Make the comment inherit the current content language unless specifically
     // set.
     if ($comment->isNew()) {
       $language_content = language(LANGUAGE_TYPE_CONTENT);
-      $comment->langcode = $language_content->langcode;
+      $comment->langcode->value = $language_content->langcode;
     }
 
-    $form['langcode'] = array(
-      '#type' => 'value',
-      '#value' => $comment->langcode,
-    );
-
-    // Attach fields.
-    $comment->node_type = 'comment_node_' . $node->type;
+    // Add internal comment properties.
+    foreach (array('cid', 'pid', 'nid', 'uid', 'node_type', 'langcode') as $key) {
+      $form[$key] = array('#type' => 'value', '#value' => $comment->$key->value);
+    }
 
     return parent::form($form, $form_state, $comment);
   }
@@ -198,7 +187,7 @@ public function form(array $form, array &$form_state, EntityInterface $comment)
   protected function actions(array $form, array &$form_state) {
     $element = parent::actions($form, $form_state);
     $comment = $this->getEntity($form_state);
-    $node = $form_state['comment']['node'];
+    $node = $comment->nid->entity;
     $preview_mode = variable_get('comment_preview_' . $node->type, DRUPAL_OPTIONAL);
 
     // No delete action on the comment form.
@@ -209,7 +198,7 @@ protected function actions(array $form, array &$form_state) {
 
     // Only show the save button if comment previews are optional or if we are
     // already previewing the submission.
-    $element['submit']['#access'] = ($comment->cid && user_access('administer comments')) || $preview_mode != DRUPAL_REQUIRED || isset($form_state['comment_preview']);
+    $element['submit']['#access'] = ($comment->cid->value && user_access('administer comments')) || $preview_mode != DRUPAL_REQUIRED || isset($form_state['comment_preview']);
 
     $element['preview'] = array(
       '#type' => 'submit',
@@ -266,51 +255,47 @@ public function validate(array $form, array &$form_state) {
   }
 
   /**
+   * Overrides EntityFormController::buildEntity().
+   */
+  public function buildEntity(array $form, array &$form_state) {
+    $comment = parent::buildEntity($form, $form_state);
+    $date = !empty($form_state['values']['date']) ? $form_state['values']['date'] : 'now';
+    $comment->created->value = $date;
+    $comment->changed->value = REQUEST_TIME;
+    return $comment;
+  }
+
+  /**
    * Overrides Drupal\Core\Entity\EntityFormController::submit().
    */
   public function submit(array $form, array &$form_state) {
     $comment = parent::submit($form, $form_state);
 
-    if (empty($comment->date)) {
-      $comment->date = 'now';
-    }
-    $date = new DrupalDateTime($comment->date);
-    $comment->created = $date->getTimestamp();
-    $comment->changed = REQUEST_TIME;
-
     // If the comment was posted by a registered user, assign the author's ID.
     // @todo Too fragile. Should be prepared and stored in comment_form()
     // already.
-    if (!$comment->is_anonymous && !empty($comment->name) && ($account = user_load_by_name($comment->name))) {
-      $comment->uid = $account->uid;
+    if (!$comment->is_anonymous && !empty($comment->name->value) && ($account = user_load_by_name($comment->name->value))) {
+      $comment->uid->value = $account->uid;
     }
     // If the comment was posted by an anonymous user and no author name was
     // required, use "Anonymous" by default.
-    if ($comment->is_anonymous && (!isset($comment->name) || $comment->name === '')) {
-      $comment->name = config('user.settings')->get('anonymous');
+    if ($comment->is_anonymous && (!isset($comment->name->value) || $comment->name->value === '')) {
+      $comment->name->value = config('user.settings')->get('anonymous');
     }
 
     // Validate the comment's subject. If not specified, extract from comment
     // body.
-    if (trim($comment->subject) == '') {
+    if (trim($comment->subject->value) == '') {
       // The body may be in any format, so:
       // 1) Filter it into HTML
       // 2) Strip out all HTML tags
       // 3) Convert entities back to plain-text.
-      $field = field_info_field('comment_body');
-      $langcode = field_is_translatable('comment', $field) ? $this->getFormLangcode($form_state) : LANGUAGE_NOT_SPECIFIED;
-      $comment_body = $comment->comment_body[$langcode][0];
-      if (isset($comment_body['format'])) {
-        $comment_text = check_markup($comment_body['value'], $comment_body['format']);
-      }
-      else {
-        $comment_text = check_plain($comment_body['value']);
-      }
+      $comment_text = $comment->comment_body->processed;
       $comment->subject = truncate_utf8(trim(decode_entities(strip_tags($comment_text))), 29, TRUE);
       // Edge cases where the comment body is populated only by HTML tags will
       // require a default subject.
-      if ($comment->subject == '') {
-        $comment->subject = t('(No subject)');
+      if ($comment->subject->value == '') {
+        $comment->subject->value = t('(No subject)');
       }
     }
 
@@ -346,13 +331,13 @@ public function save(array $form, array &$form_state) {
       }
 
       comment_save($comment);
-      $form_state['values']['cid'] = $comment->cid;
+      $form_state['values']['cid'] = $comment->cid->value;
 
       // Add an entry to the watchdog log.
-      watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject), WATCHDOG_NOTICE, l(t('view'), 'comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid)));
+      watchdog('content', 'Comment posted: %subject.', array('%subject' => $comment->subject->value), WATCHDOG_NOTICE, l(t('view'), 'comment/' . $comment->cid->value, array('fragment' => 'comment-' . $comment->cid->value)));
 
       // Explain the approval queue if necessary.
-      if ($comment->status == COMMENT_NOT_PUBLISHED) {
+      if ($comment->status->value == COMMENT_NOT_PUBLISHED) {
         if (!user_access('administer comments')) {
           drupal_set_message(t('Your comment has been queued for review by site administrators and will be published after approval.'));
         }
@@ -362,16 +347,16 @@ public function save(array $form, array &$form_state) {
       }
       $query = array();
       // Find the current display page for this comment.
-      $page = comment_get_display_page($comment->cid, $node->type);
+      $page = comment_get_display_page($comment->cid->value, $node->type);
       if ($page > 0) {
         $query['page'] = $page;
       }
       // Redirect to the newly posted comment.
-      $redirect = array('node/' . $node->nid, array('query' => $query, 'fragment' => 'comment-' . $comment->cid));
+      $redirect = array('node/' . $node->nid, array('query' => $query, 'fragment' => 'comment-' . $comment->cid->value));
     }
     else {
-      watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject), WATCHDOG_WARNING);
-      drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject)), 'error');
+      watchdog('content', 'Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject->value), WATCHDOG_WARNING);
+      drupal_set_message(t('Comment: unauthorized comment submitted or comment submitted to a closed post %subject.', array('%subject' => $comment->subject->value)), 'error');
       // Redirect the user to the node they are commenting on.
       $redirect = 'node/' . $node->nid;
     }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentRenderController.php b/core/modules/comment/lib/Drupal/comment/CommentRenderController.php
index bc21cd2..bc35285 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentRenderController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentRenderController.php
@@ -27,13 +27,17 @@ public function buildContent(array $entities = array(), $view_mode = 'full', $la
       return $return;
     }
 
-    // Attach user account.
-    user_attach_accounts($entities);
+    // Pre-load associated users into cache to leverage multiple loading.
+    $uids = array();
+    foreach ($entities as $entity) {
+      $uids[] = $entity->uid->value;
+    }
+    user_load_multiple(array_unique($uids));
 
     parent::buildContent($entities, $view_mode, $langcode);
 
     foreach ($entities as $entity) {
-      $node = node_load($entity->nid);
+      $node = $entity->nid->entity;
       if (!$node) {
         throw new \InvalidArgumentException(t('Invalid node for comment.'));
       }
@@ -76,7 +80,7 @@ protected function alterBuild(array &$build, EntityInterface $comment, $view_mod
       }
 
       // Add anchor for each comment.
-      $prefix .= "<a id=\"comment-$comment->cid\"></a>\n";
+      $prefix .= "<a id=\"comment-{$comment->cid->value}\"></a>\n";
       $build['#prefix'] = $prefix;
 
       // Close all open divs.
diff --git a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
index 50dde39..1b61d34 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentStorageController.php
@@ -8,7 +8,8 @@
 namespace Drupal\comment;
 
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\Entity\DatabaseStorageController;
+use Drupal\Core\Entity\DatabaseStorageControllerNG;
+use Drupal\Component\Uuid\Uuid;
 use LogicException;
 
 /**
@@ -17,7 +18,7 @@
  * This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
  * required special handling for comment entities.
  */
-class CommentStorageController extends DatabaseStorageController {
+class CommentStorageController extends DatabaseStorageControllerNG {
   /**
    * The thread for which a lock was acquired.
    */
@@ -33,23 +34,46 @@ protected function buildQuery($ids, $revision_id = FALSE) {
     $query->innerJoin('node', 'n', 'base.nid = n.nid');
     $query->addField('n', 'type', 'node_type');
     $query->innerJoin('users', 'u', 'base.uid = u.uid');
+    // @todo: Move to a computed 'name' field instead.
     $query->addField('u', 'name', 'registered_name');
-    $query->fields('u', array('uid', 'signature', 'signature_format'));
     return $query;
   }
 
   /**
    * Overrides Drupal\Core\Entity\DatabaseStorageController::attachLoad().
    */
-  protected function attachLoad(&$comments, $load_revision = FALSE) {
-    // Set up standard comment properties.
-    foreach ($comments as $key => $comment) {
-      $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
-      $comment->new = node_mark($comment->nid, $comment->changed);
-      $comment->node_type = 'comment_node_' . $comment->node_type;
-      $comments[$key] = $comment;
+  protected function attachLoad(&$records, $load_revision = FALSE) {
+    // Prepare standard comment fields.
+    foreach ($records as $key => $record) {
+      $record->name = $record->uid ? $record->registered_name : $record->name;
+      $record->node_type = 'comment_node_' . $record->node_type;
+      $records[$key] = $record;
     }
-    parent::attachLoad($comments, $load_revision);
+    parent::attachLoad($records, $load_revision);
+  }
+
+  /**
+   * Overrides Drupal\Core\Entity\DatabaseStorageControllerNG::create().
+   */
+  public function create(array $values) {
+    // We have to determine the bundle first.
+    if (empty($values['node_type']) && !empty($values['nid'])) {
+      $node = node_load($values['nid']);
+      $values['node_type'] = 'comment_node_' . $node->type;
+    }
+    $comment = new $this->entityClass(array(), $this->entityType, $values['node_type']);
+
+    // Set all other given values.
+    foreach ($values as $name => $value) {
+      $comment->$name = $value;
+    }
+
+    // Assign a new UUID if there is none yet.
+    if ($this->uuidKey && !isset($comment->{$this->uuidKey})) {
+      $uuid = new Uuid();
+      $comment->{$this->uuidKey}->value = $uuid->generate();
+    }
+    return $comment;
   }
 
   /**
@@ -61,20 +85,19 @@ protected function attachLoad(&$comments, $load_revision = FALSE) {
   protected function preSave(EntityInterface $comment) {
     global $user;
 
-    if (!isset($comment->status)) {
-      $comment->status = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
+    if (!isset($comment->status->value)) {
+      $comment->status->value = user_access('skip comment approval') ? COMMENT_PUBLISHED : COMMENT_NOT_PUBLISHED;
     }
     // Make sure we have a proper bundle name.
-    if (!isset($comment->node_type)) {
-      $node = node_load($comment->nid);
-      $comment->node_type = 'comment_node_' . $node->type;
+    if (!isset($comment->node_type->value)) {
+      $comment->node_type->value = 'comment_node_' . $comment->nid->entity->type;
     }
-    if (!$comment->cid) {
+    if ($comment->isNew()) {
       // Add the comment to database. This next section builds the thread field.
       // Also see the documentation for comment_view().
-      if (!empty($comment->thread)) {
+      if (!empty($comment->thread->value)) {
         // Allow calling code to set thread itself.
-        $thread = $comment->thread;
+        $thread = $comment->thread->value;
       }
       else {
         if ($this->threadLock) {
@@ -82,10 +105,10 @@ protected function preSave(EntityInterface $comment) {
           // is extended in a faulty manner.
           throw new LogicException('preSave is called again without calling postSave() or releaseThreadLock()');
         }
-        if ($comment->pid == 0) {
+        if ($comment->pid->value == 0) {
           // This is a comment with no parent comment (depth 0): we start
           // by retrieving the maximum thread level.
-          $max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid))->fetchField();
+          $max = db_query('SELECT MAX(thread) FROM {comment} WHERE nid = :nid', array(':nid' => $comment->nid->value))->fetchField();
           // Strip the "/" from the end of the thread.
           $max = rtrim($max, '/');
           // We need to get the value at the correct depth.
@@ -98,14 +121,14 @@ protected function preSave(EntityInterface $comment) {
           // the thread value at the proper depth.
 
           // Get the parent comment:
-          $parent = comment_load($comment->pid);
+          $parent = $comment->pid->entity;
           // Strip the "/" from the end of the parent thread.
-          $parent->thread = (string) rtrim((string) $parent->thread, '/');
-          $prefix = $parent->thread . '.';
+          $parent->thread->value = (string) rtrim((string) $parent->thread->value, '/');
+          $prefix = $parent->thread->value . '.';
           // Get the max value in *this* thread.
           $max = db_query("SELECT MAX(thread) FROM {comment} WHERE thread LIKE :thread AND nid = :nid", array(
-            ':thread' => $parent->thread . '.%',
-            ':nid' => $comment->nid,
+            ':thread' => $parent->thread->value . '.%',
+            ':nid' => $comment->nid->value,
           ))->fetchField();
 
           if ($max == '') {
@@ -119,7 +142,7 @@ protected function preSave(EntityInterface $comment) {
             $max = rtrim($max, '/');
             // Get the value at the correct depth.
             $parts = explode('.', $max);
-            $parent_depth = count(explode('.', $parent->thread));
+            $parent_depth = count(explode('.', $parent->thread->value));
             $n = comment_alphadecimal_to_int($parts[$parent_depth]);
           }
         }
@@ -128,23 +151,23 @@ protected function preSave(EntityInterface $comment) {
         // has the lock, just move to the next integer.
         do {
           $thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
-        } while (!lock()->acquire("comment:$comment->nid:$thread"));
+        } while (!lock()->acquire("comment:{$comment->nid->value}:$thread"));
         $this->threadLock = $thread;
       }
-      if (empty($comment->created)) {
-        $comment->created = REQUEST_TIME;
+      if (empty($comment->created->value)) {
+        $comment->created->value = REQUEST_TIME;
       }
-      if (empty($comment->changed)) {
-        $comment->changed = $comment->created;
+      if (empty($comment->changed->value)) {
+        $comment->changed->value = $comment->created->value;
       }
       // We test the value with '===' because we need to modify anonymous
       // users as well.
-      if ($comment->uid === $user->uid && isset($user->name)) {
-        $comment->name = $user->name;
+      if ($comment->uid->value === $user->uid && isset($user->name)) {
+        $comment->name->value = $user->name;
       }
       // Add the values which aren't passed into the function.
-      $comment->thread = $thread;
-      $comment->hostname = ip_address();
+      $comment->thread->value = $thread;
+      $comment->hostname->value = ip_address();
     }
   }
 
@@ -154,8 +177,8 @@ protected function preSave(EntityInterface $comment) {
   protected function postSave(EntityInterface $comment, $update) {
     $this->releaseThreadLock();
     // Update the {node_comment_statistics} table prior to executing the hook.
-    $this->updateNodeStatistics($comment->nid);
-    if ($comment->status == COMMENT_PUBLISHED) {
+    $this->updateNodeStatistics($comment->nid->value);
+    if ($comment->status->value == COMMENT_PUBLISHED) {
       module_invoke_all('comment_publish', $comment);
     }
   }
@@ -172,7 +195,7 @@ protected function postDelete($comments) {
     comment_delete_multiple($child_cids);
 
     foreach ($comments as $comment) {
-      $this->updateNodeStatistics($comment->nid);
+      $this->updateNodeStatistics($comment->nid->value);
     }
   }
 
@@ -246,4 +269,105 @@ protected function releaseThreadLock() {
       $this->threadLock = '';
     }
   }
+
+  /**
+   * Implements \Drupal\Core\Entity\DataBaseStorageControllerNG::basePropertyDefinitions().
+   */
+  public function baseFieldDefinitions() {
+    $properties['cid'] = array(
+      'label' => t('ID'),
+      'description' => t('The comment ID.'),
+      'type' => 'integer_field',
+      'read-only' => TRUE,
+    );
+    $properties['uuid'] = array(
+      'label' => t('UUID'),
+      'description' => t('The comment UUID.'),
+      'type' => 'string_field',
+    );
+    $properties['pid'] = array(
+      'label' => t('Parent ID'),
+      'description' => t('The parent comment ID if this is a reply to a comment.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'comment'),
+    );
+    $properties['nid'] = array(
+      'label' => t('Node ID'),
+      'description' => t('The ID of the node of which this comment is a reply.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'node'),
+      'required' => TRUE,
+    );
+    $properties['langcode'] = array(
+      'label' => t('Language code'),
+      'description' => t('The comment language code.'),
+      'type' => 'language_field',
+    );
+    $properties['subject'] = array(
+      'label' => t('Subject'),
+      'description' => t('The comment title or subject.'),
+      'type' => 'string_field',
+    );
+    $properties['uid'] = array(
+      'label' => t('User ID'),
+      'description' => t('The user ID of the comment author.'),
+      'type' => 'entityreference_field',
+      'settings' => array('entity type' => 'user'),
+    );
+    $properties['name'] = array(
+      'label' => t('Name'),
+      'description' => t("The comment author's name."),
+      'type' => 'string_field',
+    );
+    $properties['mail'] = array(
+      'label' => t('e-mail'),
+      'description' => t("The comment author's e-mail address."),
+      'type' => 'string_field',
+    );
+    $properties['homepage'] = array(
+      'label' => t('Homepage'),
+      'description' => t("The comment author's home page address."),
+      'type' => 'string_field',
+    );
+    $properties['hostname'] = array(
+      'label' => t('Hostname'),
+      'description' => t("The comment author's hostname."),
+      'type' => 'string_field',
+    );
+    $properties['created'] = array(
+      'label' => t('Created'),
+      'description' => t('The time that the comment was created.'),
+      'type' => 'date_field',
+    );
+    $properties['changed'] = array(
+      'label' => t('Changed'),
+      'description' => t('The time that the comment was last edited.'),
+      'type' => 'date_field',
+    );
+    $properties['status'] = array(
+      'label' => t('Publishing status'),
+      'description' => t('A boolean indicating whether the comment is published.'),
+      'type' => 'boolean_field',
+    );
+    $properties['thread'] = array(
+      'label' => t('Thread place'),
+      'description' => t("The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length."),
+      'type' => 'string_field',
+    );
+    $properties['node_type'] = array(
+      // @todo: The bundle property should be stored so it's queryable.
+      'label' => t('Node type'),
+      'description' => t("The comment node type."),
+      'type' => 'string_field',
+      'queryable' => FALSE,
+    );
+    $properties['new'] = array(
+      'label' => t('Comment new marker'),
+      'description' => t("The comment 'new' marker for the current user (0 read, 1 new, 2 updated)."),
+      'type' => 'integer_field',
+      'computed' => TRUE,
+      'class' => '\Drupal\comment\FieldNewItem',
+    );
+    return $properties;
+  }
 }
diff --git a/core/modules/comment/lib/Drupal/comment/FieldNewItem.php b/core/modules/comment/lib/Drupal/comment/FieldNewItem.php
new file mode 100644
index 0000000..7ef4092
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/FieldNewItem.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\FieldNewItem.
+ */
+
+namespace Drupal\comment;
+
+use Drupal\Core\Entity\Field\Type\IntegerItem;
+
+/**
+ * The field item for the 'new' field.
+ */
+class FieldNewItem extends IntegerItem {
+
+  /**
+   * Definitions of the contained properties.
+   *
+   * @see self::getPropertyDefinitions()
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
+   */
+  public function getPropertyDefinitions() {
+
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = array(
+        'type' => 'integer',
+        'label' => t('Integer value'),
+        'class' => '\Drupal\comment\FieldNewValue',
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+}
diff --git a/core/modules/comment/lib/Drupal/comment/FieldNewValue.php b/core/modules/comment/lib/Drupal/comment/FieldNewValue.php
new file mode 100644
index 0000000..b742c53
--- /dev/null
+++ b/core/modules/comment/lib/Drupal/comment/FieldNewValue.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\FieldNewValue.
+ */
+
+namespace Drupal\comment;
+
+use Drupal\Core\TypedData\ContextAwareTypedData;
+use Drupal\Core\TypedData\ReadOnlyException;
+use InvalidArgumentException;
+
+/**
+ * A computed property for the integer value of the 'new' field.
+ *
+ * @todo: Declare the list of allowed values once supported.
+ */
+class FieldNewValue extends ContextAwareTypedData {
+
+  /**
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue().
+   */
+  public function getValue($langcode = NULL) {
+    if (!isset($this->value)) {
+      if (!isset($this->parent)) {
+        throw new InvalidArgumentException('Computed properties require context for computation.');
+      }
+      $field = $this->parent->getParent();
+      $entity = $field->getParent();
+      $this->value = node_mark($entity->nid->value, $entity->changed->value->getTimestamp());
+    }
+    return $this->value;
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue().
+   */
+  public function setValue($value) {
+    if (isset($value)) {
+      throw new ReadOnlyException('Unable to set a computed property.');
+    }
+  }
+}
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
index e0bbfd9..7530ed0 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
@@ -8,7 +8,7 @@
 namespace Drupal\comment\Plugin\Core\Entity;
 
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\Entity;
+use Drupal\Core\Entity\EntityNG;
 use Drupal\Core\Annotation\Plugin;
 use Drupal\Core\Annotation\Translation;
 
@@ -24,7 +24,7 @@
  *   form_controller_class = {
  *     "default" = "Drupal\comment\CommentFormController"
  *   },
- *   translation_controller_class = "Drupal\comment\CommentTranslationController",
+ *   translation_controller_class = "Drupal\translation_entity\EntityTranslationControllerNG",
  *   base_table = "comment",
  *   uri_callback = "comment_uri",
  *   fieldable = TRUE,
@@ -43,45 +43,51 @@
  *   }
  * )
  */
-class Comment extends Entity implements ContentEntityInterface {
+class Comment extends EntityNG implements ContentEntityInterface {
 
   /**
    * The comment ID.
    *
-   * @var integer
+   * @todo Rename to 'id'.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $cid;
 
   /**
    * The comment UUID.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $uuid;
 
   /**
    * The parent comment ID if this is a reply to a comment.
    *
-   * @var integer
+   * @todo: Rename to 'parent_id'.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $pid;
 
   /**
    * The ID of the node to which the comment is attached.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $nid;
 
   /**
    * The comment language code.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
-  public $langcode = LANGUAGE_NOT_SPECIFIED;
+  public $langcode;
 
   /**
    * The comment title.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $subject;
 
@@ -89,25 +95,25 @@ class Comment extends Entity implements ContentEntityInterface {
   /**
    * The comment author ID.
    *
-   * @var integer
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
-  public $uid = 0;
+  public $uid;
 
   /**
    * The comment author's name.
    *
    * For anonymous authors, this is the value as typed in the comment form.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
-  public $name = '';
+  public $name;
 
   /**
    * The comment author's e-mail address.
    *
    * For anonymous authors, this is the value as typed in the comment form.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $mail;
 
@@ -116,21 +122,100 @@ class Comment extends Entity implements ContentEntityInterface {
    *
    * For anonymous authors, this is the value as typed in the comment form.
    *
-   * @var string
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
   public $homepage;
 
   /**
-   * Implements Drupal\Core\Entity\EntityInterface::id().
+   * The comment author's hostname.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
    */
-  public function id() {
-    return $this->cid;
+  public $hostname;
+
+  /**
+   * The time that the comment was created.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $created;
+
+  /**
+   * The time that the comment was last edited.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $changed;
+
+  /**
+   * A boolean field indicating whether the comment is published.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $status;
+
+  /**
+   * The alphadecimal representation of the comment's place in a thread.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $thread;
+
+  /**
+   * The comment node type.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $node_type;
+
+  /**
+   * The comment 'new' marker for the current user.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  public $new;
+
+  /**
+   * The plain data values of the contained properties.
+   *
+   * Define default values.
+   *
+   * @var array
+   */
+  protected $values = array(
+    'langcode' => array(LANGUAGE_DEFAULT => array(0 => array('value' => LANGUAGE_NOT_SPECIFIED))),
+    'name' => array(LANGUAGE_DEFAULT => array(0 => array('value' => ''))),
+    'uid' => array(LANGUAGE_DEFAULT => array(0 => array('value' => 0))),
+  );
+
+  /**
+   * Initialize the object. Invoked upon construction and wake up.
+   */
+  protected function init() {
+    parent::init();
+    // We unset all defined properties, so magic getters apply.
+    unset($this->cid);
+    unset($this->uuid);
+    unset($this->pid);
+    unset($this->nid);
+    unset($this->subject);
+    unset($this->uid);
+    unset($this->name);
+    unset($this->mail);
+    unset($this->homepage);
+    unset($this->hostname);
+    unset($this->created);
+    unset($this->changed);
+    unset($this->status);
+    unset($this->thread);
+    unset($this->node_type);
+    unset($this->new);
   }
 
   /**
-   * Implements Drupal\Core\Entity\EntityInterface::bundle().
+   * Implements Drupal\Core\Entity\EntityInterface::id().
    */
-  public function bundle() {
-    return $this->node_type;
+  public function id() {
+    return $this->get('cid')->value;
   }
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentActionsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentActionsTest.php
index 4a2e9ca..a93d02c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentActionsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentActionsTest.php
@@ -35,28 +35,28 @@ function testCommentPublishUnpublishActions() {
     $comment_text = $this->randomName();
     $subject = $this->randomName();
     $comment = $this->postComment($this->node, $comment_text, $subject);
-    $comment = comment_load($comment->id);
+    $comment = comment_load($comment->id());
 
     // Unpublish a comment (direct form: doesn't actually save the comment).
     comment_unpublish_action($comment);
-    $this->assertEqual($comment->status, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
+    $this->assertEqual($comment->status->value, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
     $this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $subject), 'Found watchdog message');
     $this->clearWatchdog();
 
     // Unpublish a comment (indirect form: modify the comment in the database).
-    comment_unpublish_action(NULL, array('cid' => $comment->cid));
-    $this->assertEqual(comment_load($comment->cid)->status, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
+    comment_unpublish_action(NULL, array('cid' => $comment->cid->value));
+    $this->assertEqual(comment_load($comment->cid->value)->status->value, COMMENT_NOT_PUBLISHED, 'Comment was unpublished');
     $this->assertWatchdogMessage('Unpublished comment %subject.', array('%subject' => $subject), 'Found watchdog message');
 
     // Publish a comment (direct form: doesn't actually save the comment).
     comment_publish_action($comment);
-    $this->assertEqual($comment->status, COMMENT_PUBLISHED, 'Comment was published');
+    $this->assertEqual($comment->status->value, COMMENT_PUBLISHED, 'Comment was published');
     $this->assertWatchdogMessage('Published comment %subject.', array('%subject' => $subject), 'Found watchdog message');
     $this->clearWatchdog();
 
     // Publish a comment (indirect form: modify the comment in the database).
-    comment_publish_action(NULL, array('cid' => $comment->cid));
-    $this->assertEqual(comment_load($comment->cid)->status, COMMENT_PUBLISHED, 'Comment was published');
+    comment_publish_action(NULL, array('cid' => $comment->cid->value));
+    $this->assertEqual(comment_load($comment->cid->value)->status->value, COMMENT_PUBLISHED, 'Comment was published');
     $this->assertWatchdogMessage('Published comment %subject.', array('%subject' => $subject), 'Found watchdog message');
     $this->clearWatchdog();
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php
index 5e46be4..50a86b7 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentAnonymousTest.php
@@ -54,7 +54,7 @@ function testAnonymous() {
     $this->setCommentAnonymous('1');
 
     // Attempt to edit anonymous comment.
-    $this->drupalGet('comment/' . $anonymous_comment1->id . '/edit');
+    $this->drupalGet('comment/' . $anonymous_comment1->id() . '/edit');
     $edited_comment = $this->postComment(NULL, $this->randomName(), $this->randomName());
     $this->assertTrue($this->commentExists($edited_comment, FALSE), 'Modified reply found.');
     $this->drupalLogout();
@@ -99,7 +99,7 @@ function testAnonymous() {
 
     // Make sure the user data appears correctly when editing the comment.
     $this->drupalLogin($this->admin_user);
-    $this->drupalGet('comment/' . $anonymous_comment3->id . '/edit');
+    $this->drupalGet('comment/' . $anonymous_comment3->id() . '/edit');
     $this->assertRaw($author_name, "The anonymous user's name is correct when editing the comment.");
     $this->assertRaw($author_mail, "The anonymous user's e-mail address is correct when editing the comment.");
 
@@ -107,19 +107,19 @@ function testAnonymous() {
     $this->performCommentOperation($anonymous_comment3, 'unpublish');
 
     $this->drupalGet('admin/content/comment/approval');
-    $this->assertRaw('comments[' . $anonymous_comment3->id . ']', 'Comment was unpublished.');
+    $this->assertRaw('comments[' . $anonymous_comment3->id() . ']', 'Comment was unpublished.');
 
     // Publish comment.
     $this->performCommentOperation($anonymous_comment3, 'publish', TRUE);
 
     $this->drupalGet('admin/content/comment');
-    $this->assertRaw('comments[' . $anonymous_comment3->id . ']', 'Comment was published.');
+    $this->assertRaw('comments[' . $anonymous_comment3->id() . ']', 'Comment was published.');
 
     // Delete comment.
     $this->performCommentOperation($anonymous_comment3, 'delete');
 
     $this->drupalGet('admin/content/comment');
-    $this->assertNoRaw('comments[' . $anonymous_comment3->id . ']', 'Comment was deleted.');
+    $this->assertNoRaw('comments[' . $anonymous_comment3->id() . ']', 'Comment was deleted.');
     $this->drupalLogout();
 
     // Reset.
@@ -162,7 +162,7 @@ function testAnonymous() {
     $this->assertFieldByName('subject', '', 'Subject field found.');
     $this->assertFieldByName("comment_body[$langcode][0][value]", '', 'Comment field found.');
 
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $anonymous_comment3->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $anonymous_comment3->id());
     $this->assertText('You are not authorized to view comments', 'Error attempting to post reply.');
     $this->assertNoText($author_name, 'Comment not displayed.');
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php
index 62a7812..e123fc4 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentApprovalTest.php
@@ -47,7 +47,7 @@ function testApprovalAdminInterface() {
     // Get unapproved comment id.
     $this->drupalLogin($this->admin_user);
     $anonymous_comment4 = $this->getUnapprovedComment($subject);
-    $anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
+    $anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
     $this->drupalLogout();
 
     $this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
@@ -69,8 +69,8 @@ function testApprovalAdminInterface() {
     $this->drupalGet('admin/content/comment/approval');
     $this->assertText(t('Unapproved comments (@count)', array('@count' => 2)), 'Two unapproved comments waiting for approval.');
     $edit = array(
-      "comments[{$comments[0]->id}]" => 1,
-      "comments[{$comments[1]->id}]" => 1,
+      "comments[{$comments[0]->id()}]" => 1,
+      "comments[{$comments[1]->id()}]" => 1,
     );
     $this->drupalPost(NULL, $edit, t('Update'));
     $this->assertText(t('Unapproved comments (@count)', array('@count' => 0)), 'All comments were approved.');
@@ -78,9 +78,9 @@ function testApprovalAdminInterface() {
     // Delete multiple comments in one operation.
     $edit = array(
       'operation' => 'delete',
-      "comments[{$comments[0]->id}]" => 1,
-      "comments[{$comments[1]->id}]" => 1,
-      "comments[{$anonymous_comment4->id}]" => 1,
+      "comments[{$comments[0]->id()}]" => 1,
+      "comments[{$comments[1]->id()}]" => 1,
+      "comments[{$anonymous_comment4->id()}]" => 1,
     );
     $this->drupalPost(NULL, $edit, t('Update'));
     $this->assertText(t('Are you sure you want to delete these comments and all their children?'), 'Confirmation required.');
@@ -111,7 +111,7 @@ function testApprovalNodeInterface() {
     // Get unapproved comment id.
     $this->drupalLogin($this->admin_user);
     $anonymous_comment4 = $this->getUnapprovedComment($subject);
-    $anonymous_comment4 = entity_create('comment', array('id' => $anonymous_comment4, 'subject' => $subject, 'comment' => $body));
+    $anonymous_comment4 = entity_create('comment', array('cid' => $anonymous_comment4, 'subject' => $subject, 'comment_body' => $body, 'nid' => $this->node->nid));
     $this->drupalLogout();
 
     $this->assertFalse($this->commentExists($anonymous_comment4), 'Anonymous comment was not published.');
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
index 895fbd4..d0bac40 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentBlockTest.php
@@ -85,10 +85,10 @@ function testRecentCommentBlock() {
     $this->assertText($block['title'], 'Block was found.');
 
     // Test the only the 2 latest comments are shown and in the proper order.
-    $this->assertNoText($comment1->subject, 'Comment not found in block.');
-    $this->assertText($comment2->subject, 'Comment found in block.');
-    $this->assertText($comment3->comment, 'Comment found in block.');
-    $this->assertTrue(strpos($this->drupalGetContent(), $comment3->comment) < strpos($this->drupalGetContent(), $comment2->subject), 'Comments were ordered correctly in block.');
+    $this->assertNoText($comment1->subject->value, 'Comment not found in block.');
+    $this->assertText($comment2->subject->value, 'Comment found in block.');
+    $this->assertText($comment3->comment_body->value, 'Comment found in block.');
+    $this->assertTrue(strpos($this->drupalGetContent(), $comment3->comment_body->value) < strpos($this->drupalGetContent(), $comment2->subject->value), 'Comments were ordered correctly in block.');
 
     // Set the number of recent comments to show to 10.
     $this->drupalLogout();
@@ -103,21 +103,21 @@ function testRecentCommentBlock() {
     $comment4 = $this->postComment($this->node, $this->randomName(), $this->randomName());
 
     // Test that all four comments are shown.
-    $this->assertText($comment1->subject, 'Comment found in block.');
-    $this->assertText($comment2->subject, 'Comment found in block.');
-    $this->assertText($comment3->comment, 'Comment found in block.');
-    $this->assertText($comment4->subject, 'Comment found in block.');
+    $this->assertText($comment1->subject->value, 'Comment found in block.');
+    $this->assertText($comment2->subject->value, 'Comment found in block.');
+    $this->assertText($comment3->comment_body->value, 'Comment found in block.');
+    $this->assertText($comment4->subject->value, 'Comment found in block.');
 
     // Test that links to comments work when comments are across pages.
     $this->setCommentsPerPage(1);
     $this->drupalGet('');
-    $this->clickLink($comment1->subject);
-    $this->assertText($comment1->subject, 'Comment link goes to correct page.');
+    $this->clickLink($comment1->subject->value);
+    $this->assertText($comment1->subject->value, 'Comment link goes to correct page.');
     $this->drupalGet('');
-    $this->clickLink($comment2->subject);
-    $this->assertText($comment2->subject, 'Comment link goes to correct page.');
-    $this->clickLink($comment4->subject);
-    $this->assertText($comment4->subject, 'Comment link goes to correct page.');
+    $this->clickLink($comment2->subject->value);
+    $this->assertText($comment2->subject->value, 'Comment link goes to correct page.');
+    $this->clickLink($comment4->subject->value);
+    $this->assertText($comment4->subject->value, 'Comment link goes to correct page.');
     // Check that when viewing a comment page from a link to the comment, that
     // rel="canonical" is added to the head of the document.
     $this->assertRaw('<link rel="canonical"', 'Canonical URL was found in the HTML head');
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentContentRebuildTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentContentRebuildTest.php
index 2674152..ddaf89d 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentContentRebuildTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentContentRebuildTest.php
@@ -34,7 +34,7 @@ function testCommentRebuild() {
     $subject_text = $this->randomName();
     $comment_text = $this->randomName();
     $comment = $this->postComment($this->node, $comment_text, $subject_text, TRUE);
-    $comment_loaded = comment_load($comment->id);
+    $comment_loaded = comment_load($comment->id());
     $this->assertTrue($this->commentExists($comment), 'Comment found.');
 
     // Add the property to the content array and then see if it still exists on build.
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentInterfaceTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentInterfaceTest.php
index 1070107..00a4291 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentInterfaceTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentInterfaceTest.php
@@ -37,7 +37,6 @@ function testCommentInterface() {
     $this->drupalLogin($this->web_user);
     $comment_text = $this->randomName();
     $comment = $this->postComment($this->node, $comment_text);
-    $comment_loaded = comment_load($comment->id);
     $this->assertTrue($this->commentExists($comment), 'Comment found.');
 
     // Set comments to have subject and preview to required.
@@ -52,11 +51,10 @@ function testCommentInterface() {
     $subject_text = $this->randomName();
     $comment_text = $this->randomName();
     $comment = $this->postComment($this->node, $comment_text, $subject_text, TRUE);
-    $comment_loaded = comment_load($comment->id);
     $this->assertTrue($this->commentExists($comment), 'Comment found.');
 
     // Check comment display.
-    $this->drupalGet('node/' . $this->node->nid . '/' . $comment->id);
+    $this->drupalGet('node/' . $this->node->nid . '/' . $comment->id());
     $this->assertText($subject_text, 'Individual comment subject found.');
     $this->assertText($comment_text, 'Individual comment body found.');
 
@@ -67,49 +65,49 @@ function testCommentInterface() {
     $this->setCommentPreview(DRUPAL_OPTIONAL);
 
     // Test changing the comment author to "Anonymous".
-    $this->drupalGet('comment/' . $comment->id . '/edit');
-    $comment = $this->postComment(NULL, $comment->comment, $comment->subject, array('name' => ''));
-    $comment_loaded = comment_load($comment->id);
-    $this->assertTrue(empty($comment_loaded->name) && $comment_loaded->uid == 0, 'Comment author successfully changed to anonymous.');
+    $this->drupalGet('comment/' . $comment->id() . '/edit');
+    $comment = $this->postComment(NULL, $comment->comment_body->value, $comment->subject->value, array('name' => ''));
+    $comment_loaded = comment_load($comment->id());
+    $this->assertTrue(empty($comment_loaded->name->value) && $comment_loaded->uid->value == 0, 'Comment author successfully changed to anonymous.');
 
     // Test changing the comment author to an unverified user.
     $random_name = $this->randomName();
-    $this->drupalGet('comment/' . $comment->id . '/edit');
-    $comment = $this->postComment(NULL, $comment->comment, $comment->subject, array('name' => $random_name));
+    $this->drupalGet('comment/' . $comment->id() . '/edit');
+    $comment = $this->postComment(NULL, $comment->comment_body->value, $comment->subject->value, array('name' => $random_name));
     $this->drupalGet('node/' . $this->node->nid);
     $this->assertText($random_name . ' (' . t('not verified') . ')', 'Comment author successfully changed to an unverified user.');
 
     // Test changing the comment author to a verified user.
-    $this->drupalGet('comment/' . $comment->id . '/edit');
-    $comment = $this->postComment(NULL, $comment->comment, $comment->subject, array('name' => $this->web_user->name));
-    $comment_loaded = comment_load($comment->id);
-    $this->assertTrue($comment_loaded->name == $this->web_user->name && $comment_loaded->uid == $this->web_user->uid, 'Comment author successfully changed to a registered user.');
+    $this->drupalGet('comment/' . $comment->id() . '/edit');
+    $comment = $this->postComment(NULL, $comment->comment_body->value, $comment->subject->value, array('name' => $this->web_user->name));
+    $comment_loaded = comment_load($comment->id());
+    $this->assertTrue($comment_loaded->name->value == $this->web_user->name && $comment_loaded->uid->value == $this->web_user->uid, 'Comment author successfully changed to a registered user.');
 
     $this->drupalLogout();
 
     // Reply to comment #2 creating comment #3 with optional preview and no
     // subject though field enabled.
     $this->drupalLogin($this->web_user);
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id());
     $this->assertText($subject_text, 'Individual comment-reply subject found.');
     $this->assertText($comment_text, 'Individual comment-reply body found.');
     $reply = $this->postComment(NULL, $this->randomName(), '', TRUE);
-    $reply_loaded = comment_load($reply->id);
+    $reply_loaded = comment_load($reply->id());
     $this->assertTrue($this->commentExists($reply, TRUE), 'Reply found.');
-    $this->assertEqual($comment->id, $reply_loaded->pid, 'Pid of a reply to a comment is set correctly.');
-    $this->assertEqual(rtrim($comment_loaded->thread, '/') . '.00/', $reply_loaded->thread, 'Thread of reply grows correctly.');
+    $this->assertEqual($comment->id(), $reply_loaded->pid->value, 'Pid of a reply to a comment is set correctly.');
+    $this->assertEqual(rtrim($comment_loaded->thread->value, '/') . '.00/', $reply_loaded->thread->value, 'Thread of reply grows correctly.');
 
     // Second reply to comment #3 creating comment #4.
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id());
     $this->assertText($subject_text, 'Individual comment-reply subject found.');
     $this->assertText($comment_text, 'Individual comment-reply body found.');
     $reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
-    $reply_loaded = comment_load($reply->id);
+    $reply_loaded = comment_load($reply->id());
     $this->assertTrue($this->commentExists($reply, TRUE), 'Second reply found.');
-    $this->assertEqual(rtrim($comment_loaded->thread, '/') . '.01/', $reply_loaded->thread, 'Thread of second reply grows correctly.');
+    $this->assertEqual(rtrim($comment_loaded->thread->value, '/') . '.01/', $reply_loaded->thread->value, 'Thread of second reply grows correctly.');
 
     // Edit reply.
-    $this->drupalGet('comment/' . $reply->id . '/edit');
+    $this->drupalGet('comment/' . $reply->id() . '/edit');
     $reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
     $this->assertTrue($this->commentExists($reply, TRUE), 'Modified reply found.');
 
@@ -126,9 +124,9 @@ function testCommentInterface() {
     $this->setCommentsPerPage(50);
 
     // Attempt to reply to an unpublished comment.
-    $reply_loaded->status = COMMENT_NOT_PUBLISHED;
+    $reply_loaded->status->value = COMMENT_NOT_PUBLISHED;
     $reply_loaded->save();
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $reply_loaded->cid);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $reply_loaded->cid->value);
     $this->assertText(t('The comment you are replying to does not exist.'), 'Replying to an unpublished comment');
 
     // Attempt to post to node with comments disabled.
@@ -178,5 +176,4 @@ function testCommentInterface() {
     $this->drupalLogin($this->admin_user);
     $this->setCommentForm(FALSE);
   }
-
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index 474ae69..9597cf7 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -127,9 +127,9 @@ function testCommentLanguage() {
           ->execute()
           ->fetchField();
         $comment = comment_load($cid);
-        $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode, '%langcode' => $langcode);
-        $this->assertEqual($comment->langcode, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
-        $this->assertEqual($comment->comment_body[$langcode][0]['value'], $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.');
+        $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode->value, '%langcode' => $langcode);
+        $this->assertEqual($comment->langcode->value, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
+        $this->assertEqual($comment->comment_body->value, $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.');
       }
     }
 
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeAccessTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeAccessTest.php
index 37809c6..7910196 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeAccessTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeAccessTest.php
@@ -52,7 +52,6 @@ function setUp() {
    * Test that threaded comments can be viewed.
    */
   function testThreadedCommentView() {
-    $langcode = LANGUAGE_NOT_SPECIFIED;
     // Set comments to have subject required and preview disabled.
     $this->drupalLogin($this->admin_user);
     $this->setCommentPreview(DRUPAL_DISABLED);
@@ -66,20 +65,18 @@ function testThreadedCommentView() {
     $comment_text = $this->randomName();
     $comment_subject = $this->randomName();
     $comment = $this->postComment($this->node, $comment_text, $comment_subject);
-    $comment_loaded = comment_load($comment->id);
     $this->assertTrue($this->commentExists($comment), 'Comment found.');
 
     // Check comment display.
-    $this->drupalGet('node/' . $this->node->nid . '/' . $comment->id);
+    $this->drupalGet('node/' . $this->node->nid . '/' . $comment->id());
     $this->assertText($comment_subject, 'Individual comment subject found.');
     $this->assertText($comment_text, 'Individual comment body found.');
 
     // Reply to comment, creating second comment.
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment->id());
     $reply_text = $this->randomName();
     $reply_subject = $this->randomName();
     $reply = $this->postComment(NULL, $reply_text, $reply_subject, TRUE);
-    $reply_loaded = comment_load($reply->id);
     $this->assertTrue($this->commentExists($reply, TRUE), 'Reply found.');
 
     // Go to the node page and verify comment and reply are visible.
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeChangesTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeChangesTest.php
index b517d04..b27412c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeChangesTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentNodeChangesTest.php
@@ -26,8 +26,8 @@ public static function getInfo() {
   function testNodeDeletion() {
     $this->drupalLogin($this->web_user);
     $comment = $this->postComment($this->node, $this->randomName(), $this->randomName());
-    $this->assertTrue(comment_load($comment->id), 'The comment could be loaded.');
+    $this->assertTrue(comment_load($comment->id()), 'The comment could be loaded.');
     node_delete($this->node->nid);
-    $this->assertFalse(comment_load($comment->id), 'The comment could not be loaded after the node was deleted.');
+    $this->assertFalse(comment_load($comment->id()), 'The comment could not be loaded after the node was deleted.');
   }
 }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentPagerTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentPagerTest.php
index 52f1e32..644001c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentPagerTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentPagerTest.php
@@ -66,7 +66,7 @@ function testCommentPaging() {
     // Post a reply to the oldest comment and test again.
     $replies = array();
     $oldest_comment = reset($comments);
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $oldest_comment->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $oldest_comment->id());
     $reply = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     $this->setCommentsPerPage(2);
@@ -114,19 +114,19 @@ function testCommentOrderingThreading() {
     $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the second comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[1]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[1]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the first comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[0]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[0]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the last comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[2]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[2]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the second comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[3]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[3]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // At this point, the comment tree is:
@@ -180,7 +180,7 @@ function assertCommentOrder(array $comments, array $expected_order) {
 
     // First, rekey the expected order by cid.
     foreach ($expected_order as $key) {
-      $expected_cids[] = $comments[$key]->id;
+      $expected_cids[] = $comments[$key]->id();
     }
 
     $comment_anchors = $this->xpath('//a[starts-with(@id,"comment-")]');
@@ -188,8 +188,7 @@ function assertCommentOrder(array $comments, array $expected_order) {
     foreach ($comment_anchors as $anchor) {
       $result_order[] = substr($anchor['id'], 8);
     }
-
-    return $this->assertIdentical($expected_cids, $result_order, format_string('Comment order: expected @expected, returned @returned.', array('@expected' => implode(',', $expected_cids), '@returned' => implode(',', $result_order))));
+    return $this->assertEqual($expected_cids, $result_order, format_string('Comment order: expected @expected, returned @returned.', array('@expected' => implode(',', $expected_cids), '@returned' => implode(',', $result_order))));
   }
 
   /**
@@ -215,15 +214,15 @@ function testCommentNewPageIndicator() {
     $comments[] = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the second comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[1]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[1]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the first comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[0]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[0]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the last comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[2]->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $comments[2]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // At this point, the comment tree is:
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
index 0738076..c790606 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentPreviewTest.php
@@ -94,7 +94,7 @@ function testCommentEditPreviewSave() {
     $expected_text_date = format_date($raw_date);
     $expected_form_date = format_date($raw_date, 'custom', 'Y-m-d H:i O');
     $comment = $this->postComment($this->node, $edit['subject'], $edit['comment_body[' . $langcode . '][0][value]'], TRUE);
-    $this->drupalPost('comment/' . $comment->id . '/edit', $edit, t('Preview'));
+    $this->drupalPost('comment/' . $comment->id() . '/edit', $edit, t('Preview'));
 
     // Check that the preview is displaying the subject, comment, author and date correctly.
     $this->assertTitle(t('Preview comment | Drupal'), 'Page title is "Preview comment".');
@@ -110,11 +110,11 @@ function testCommentEditPreviewSave() {
     $this->assertFieldByName('date', $edit['date'], 'Date field displayed.');
 
     // Check that saving a comment produces a success message.
-    $this->drupalPost('comment/' . $comment->id . '/edit', $edit, t('Save'));
+    $this->drupalPost('comment/' . $comment->id() . '/edit', $edit, t('Save'));
     $this->assertText(t('Your comment has been posted.'), 'Comment posted.');
 
     // Check that the comment fields are correct after loading the saved comment.
-    $this->drupalGet('comment/' . $comment->id . '/edit');
+    $this->drupalGet('comment/' . $comment->id() . '/edit');
     $this->assertFieldByName('subject', $edit['subject'], 'Subject field displayed.');
     $this->assertFieldByName('comment_body[' . $langcode . '][0][value]', $edit['comment_body[' . $langcode . '][0][value]'], 'Comment field displayed.');
     $this->assertFieldByName('name', $edit['name'], 'Author field displayed.');
@@ -126,14 +126,14 @@ function testCommentEditPreviewSave() {
     $displayed['comment_body[' . $langcode . '][0][value]'] = (string) current($this->xpath("//textarea[@id='edit-comment-body-" . $langcode . "-0-value']"));
     $displayed['name'] = (string) current($this->xpath("//input[@id='edit-name']/@value"));
     $displayed['date'] = (string) current($this->xpath("//input[@id='edit-date']/@value"));
-    $this->drupalPost('comment/' . $comment->id . '/edit', $displayed, t('Save'));
+    $this->drupalPost('comment/' . $comment->id() . '/edit', $displayed, t('Save'));
 
     // Check that the saved comment is still correct.
-    $comment_loaded = comment_load($comment->id);
-    $this->assertEqual($comment_loaded->subject, $edit['subject'], 'Subject loaded.');
-    $this->assertEqual($comment_loaded->comment_body[$langcode][0]['value'], $edit['comment_body[' . $langcode . '][0][value]'], 'Comment body loaded.');
-    $this->assertEqual($comment_loaded->name, $edit['name'], 'Name loaded.');
-    $this->assertEqual($comment_loaded->created, $raw_date, 'Date loaded.');
+    $comment_loaded = comment_load($comment->id());
+    $this->assertEqual($comment_loaded->subject->value, $edit['subject'], 'Subject loaded.');
+    $this->assertEqual($comment_loaded->comment_body->value, $edit['comment_body[' . $langcode . '][0][value]'], 'Comment body loaded.');
+    $this->assertEqual($comment_loaded->name->value, $edit['name'], 'Name loaded.');
+    $this->assertEqual($comment_loaded->created->value->getTimestamp(), $raw_date, 'Date loaded.');
 
   }
 
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentStatisticsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentStatisticsTest.php
index afdcba4..1bb8d48 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentStatisticsTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentStatisticsTest.php
@@ -39,7 +39,6 @@ function setUp() {
    * Tests the node comment statistics.
    */
   function testCommentNodeCommentStatistics() {
-    $langcode = LANGUAGE_NOT_SPECIFIED;
     // Set comments to have subject and preview disabled.
     $this->drupalLogin($this->admin_user);
     $this->setCommentPreview(DRUPAL_DISABLED);
@@ -58,8 +57,7 @@ function testCommentNodeCommentStatistics() {
     // Post comment #1 as web_user2.
     $this->drupalLogin($this->web_user2);
     $comment_text = $this->randomName();
-    $comment = $this->postComment($this->node, $comment_text);
-    $comment_loaded = comment_load($comment->id);
+    $this->postComment($this->node, $comment_text);
 
     // Checks the new values of node comment statistics with comment #1.
     // The node needs to be reloaded with a node_load_multiple cache reset.
@@ -82,8 +80,7 @@ function testCommentNodeCommentStatistics() {
 
     // Post comment #2 as anonymous (comment approval enabled).
     $this->drupalGet('comment/reply/' . $this->node->nid);
-    $anonymous_comment = $this->postComment($this->node, $this->randomName(), '', TRUE);
-    $comment_unpublished_loaded = comment_load($anonymous_comment->id);
+    $this->postComment($this->node, $this->randomName(), '', TRUE);
 
     // Checks the new values of node comment statistics with comment #2 and
     // ensure they haven't changed since the comment has not been moderated.
@@ -104,13 +101,12 @@ function testCommentNodeCommentStatistics() {
 
     // Post comment #3 as anonymous.
     $this->drupalGet('comment/reply/' . $this->node->nid);
-    $anonymous_comment = $this->postComment($this->node, $this->randomName(), '', array('name' => $this->randomName()));
-    $comment_loaded = comment_load($anonymous_comment->id);
+    $comment_loaded = $this->postComment($this->node, $this->randomName(), '', array('name' => $this->randomName()));
 
     // Checks the new values of node comment statistics with comment #3.
     // The node needs to be reloaded with a node_load_multiple cache reset.
     $node = node_load($this->node->nid, TRUE);
-    $this->assertEqual($node->last_comment_name, $comment_loaded->name, 'The value of node last_comment_name is the name of the anonymous user.');
+    $this->assertEqual($node->last_comment_name, $comment_loaded->name->value, 'The value of node last_comment_name is the name of the anonymous user.');
     $this->assertEqual($node->last_comment_uid, 0, 'The value of node last_comment_uid is zero.');
     $this->assertEqual($node->comment_count, 2, 'The value of node comment_count is 2.');
   }
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
index 902dbdd..569efa6 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTestBase.php
@@ -145,7 +145,10 @@ function postComment($node, $comment, $subject = '', $contact = NULL) {
     }
 
     if (isset($match[1])) {
-      return entity_create('comment', array('id' => $match[1], 'subject' => $subject, 'comment' => $comment));
+      $entity = comment_load($match[1]);
+      $entity->subject->value = $subject;
+      $entity->comment_body->value = $comment;
+      return $entity;
     }
   }
 
@@ -163,9 +166,9 @@ function postComment($node, $comment, $subject = '', $contact = NULL) {
   function commentExists(Comment $comment = NULL, $reply = FALSE) {
     if ($comment) {
       $regex = '/' . ($reply ? '<div class="indented">(.*?)' : '');
-      $regex .= '<a id="comment-' . $comment->id . '"(.*?)'; // Comment anchor.
-      $regex .= $comment->subject . '(.*?)'; // Match subject.
-      $regex .= $comment->comment . '(.*?)'; // Match comment.
+      $regex .= '<a id="comment-' . $comment->id() . '"(.*?)'; // Comment anchor.
+      $regex .= $comment->subject->value . '(.*?)'; // Match subject.
+      $regex .= $comment->comment_body->value . '(.*?)'; // Match comment.
       $regex .= '/s';
 
       return (boolean)preg_match($regex, $this->drupalGetContent());
@@ -182,7 +185,7 @@ function commentExists(Comment $comment = NULL, $reply = FALSE) {
    *   Comment to delete.
    */
   function deleteComment(Comment $comment) {
-    $this->drupalPost('comment/' . $comment->id . '/delete', array(), t('Delete'));
+    $this->drupalPost('comment/' . $comment->id() . '/delete', array(), t('Delete'));
     $this->assertText(t('The comment and all its replies have been deleted.'), 'Comment deleted.');
   }
 
@@ -292,7 +295,7 @@ function commentContactInfoAvailable() {
   function performCommentOperation($comment, $operation, $approval = FALSE) {
     $edit = array();
     $edit['operation'] = $operation;
-    $edit['comments[' . $comment->id . ']'] = TRUE;
+    $edit['comments[' . $comment->id() . ']'] = TRUE;
     $this->drupalPost('admin/content/comment' . ($approval ? '/approval' : ''), $edit, t('Update'));
 
     if ($operation == 'delete') {
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentThreadingTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentThreadingTest.php
index ba03951..682cf51 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentThreadingTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentThreadingTest.php
@@ -42,43 +42,43 @@ function testCommentThreading() {
     $comment_text = $this->randomName();
     $comment1 = $this->postComment($this->node, $comment_text, $subject_text, TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment1_loaded = comment_load($comment1->id);
+    $comment1_loaded = comment_load($comment1->id());
     $this->assertTrue($this->commentExists($comment1), 'Comment #1. Comment found.');
-    $this->assertEqual($comment1_loaded->thread, '01/');
+    $this->assertEqual($comment1_loaded->thread->value, '01/');
     // Confirm that there is no reference to a parent comment.
-    $this->assertNoParentLink($comment1->id);
+    $this->assertNoParentLink($comment1->id());
 
     // Reply to comment #1 creating comment #2.
     $this->drupalLogin($this->web_user);
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment1->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment1->id());
     $comment2 = $this->postComment(NULL, $this->randomName(), '', TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment2_loaded = comment_load($comment2->id);
+    $comment2_loaded = comment_load($comment2->id());
     $this->assertTrue($this->commentExists($comment2, TRUE), 'Comment #2. Reply found.');
-    $this->assertEqual($comment2_loaded->thread, '01.00/');
+    $this->assertEqual($comment2_loaded->thread->value, '01.00/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment2->id, $comment1->id);
+    $this->assertParentLink($comment2->id(), $comment1->id());
 
     // Reply to comment #2 creating comment #3.
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment2->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment2->id());
     $comment3 = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment3_loaded = comment_load($comment3->id);
+    $comment3_loaded = comment_load($comment3->id());
     $this->assertTrue($this->commentExists($comment3, TRUE), 'Comment #3. Second reply found.');
-    $this->assertEqual($comment3_loaded->thread, '01.00.00/');
+    $this->assertEqual($comment3_loaded->thread->value, '01.00.00/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment3->id, $comment2->id);
+    $this->assertParentLink($comment3->id(), $comment2->id());
 
     // Reply to comment #1 creating comment #4.
     $this->drupalLogin($this->web_user);
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment1->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment1->id());
     $comment4 = $this->postComment(NULL, $this->randomName(), '', TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment4_loaded = comment_load($comment4->id);
+    $comment4_loaded = comment_load($comment4->id());
     $this->assertTrue($this->commentExists($comment4), 'Comment #4. Third reply found.');
-    $this->assertEqual($comment4_loaded->thread, '01.01/');
+    $this->assertEqual($comment4_loaded->thread->value, '01.01/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment4->id, $comment1->id);
+    $this->assertParentLink($comment4->id(), $comment1->id());
 
     // Post comment #2 overall comment #5.
     $this->drupalLogin($this->web_user);
@@ -86,43 +86,43 @@ function testCommentThreading() {
     $comment_text = $this->randomName();
     $comment5 = $this->postComment($this->node, $comment_text, $subject_text, TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment5_loaded = comment_load($comment5->id);
+    $comment5_loaded = comment_load($comment5->id());
     $this->assertTrue($this->commentExists($comment5), 'Comment #5. Second comment found.');
-    $this->assertEqual($comment5_loaded->thread, '02/');
+    $this->assertEqual($comment5_loaded->thread->value, '02/');
     // Confirm that there is no link to a parent comment.
-    $this->assertNoParentLink($comment5->id);
+    $this->assertNoParentLink($comment5->id());
 
     // Reply to comment #5 creating comment #6.
     $this->drupalLogin($this->web_user);
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment5->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment5->id());
     $comment6 = $this->postComment(NULL, $this->randomName(), '', TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment6_loaded = comment_load($comment6->id);
+    $comment6_loaded = comment_load($comment6->id());
     $this->assertTrue($this->commentExists($comment6, TRUE), 'Comment #6. Reply found.');
-    $this->assertEqual($comment6_loaded->thread, '02.00/');
+    $this->assertEqual($comment6_loaded->thread->value, '02.00/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment6->id, $comment5->id);
+    $this->assertParentLink($comment6->id(), $comment5->id());
 
     // Reply to comment #6 creating comment #7.
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment6->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment6->id());
     $comment7 = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment7_loaded = comment_load($comment7->id);
+    $comment7_loaded = comment_load($comment7->id());
     $this->assertTrue($this->commentExists($comment7, TRUE), 'Comment #7. Second reply found.');
-    $this->assertEqual($comment7_loaded->thread, '02.00.00/');
+    $this->assertEqual($comment7_loaded->thread->value, '02.00.00/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment7->id, $comment6->id);
+    $this->assertParentLink($comment7->id(), $comment6->id());
 
     // Reply to comment #5 creating comment #8.
     $this->drupalLogin($this->web_user);
-    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment5->id);
+    $this->drupalGet('comment/reply/' . $this->node->nid . '/' . $comment5->id());
     $comment8 = $this->postComment(NULL, $this->randomName(), '', TRUE);
     // Confirm that the comment was created and has the correct threading.
-    $comment8_loaded = comment_load($comment8->id);
+    $comment8_loaded = comment_load($comment8->id());
     $this->assertTrue($this->commentExists($comment8), 'Comment #8. Third reply found.');
-    $this->assertEqual($comment8_loaded->thread, '02.01/');
+    $this->assertEqual($comment8_loaded->thread->value, '02.01/');
     // Confirm that there is a link to the parent comment.
-    $this->assertParentLink($comment8->id, $comment5->id);
+    $this->assertParentLink($comment8->id(), $comment5->id());
   }
 
   /**
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTokenReplaceTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTokenReplaceTest.php
index 34674e8..85e21a4 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTokenReplaceTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTokenReplaceTest.php
@@ -39,33 +39,33 @@ function testCommentTokenReplacement() {
     $parent_comment = $this->postComment($node, $this->randomName(), $this->randomName(), TRUE);
 
     // Post a reply to the comment.
-    $this->drupalGet('comment/reply/' . $node->nid . '/' . $parent_comment->id);
+    $this->drupalGet('comment/reply/' . $node->nid . '/' . $parent_comment->id());
     $child_comment = $this->postComment(NULL, $this->randomName(), $this->randomName());
-    $comment = comment_load($child_comment->id);
-    $comment->homepage = 'http://example.org/';
+    $comment = comment_load($child_comment->id());
+    $comment->homepage->value = 'http://example.org/';
 
     // Add HTML to ensure that sanitation of some fields tested directly.
-    $comment->subject = '<blink>Blinking Comment</blink>';
+    $comment->subject->value = '<blink>Blinking Comment</blink>';
     $instance = field_info_instance('comment', 'body', 'comment_body');
 
     // Generate and test sanitized tokens.
     $tests = array();
-    $tests['[comment:cid]'] = $comment->cid;
-    $tests['[comment:hostname]'] = check_plain($comment->hostname);
-    $tests['[comment:name]'] = filter_xss($comment->name);
+    $tests['[comment:cid]'] = $comment->cid->value;
+    $tests['[comment:hostname]'] = check_plain($comment->hostname->value);
+    $tests['[comment:name]'] = filter_xss($comment->name->value);
     $tests['[comment:mail]'] = check_plain($this->admin_user->mail);
-    $tests['[comment:homepage]'] = check_url($comment->homepage);
-    $tests['[comment:title]'] = filter_xss($comment->subject);
-    $tests['[comment:body]'] = _text_sanitize($instance, LANGUAGE_NOT_SPECIFIED, $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0], 'value');
-    $tests['[comment:url]'] = url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid));
-    $tests['[comment:edit-url]'] = url('comment/' . $comment->cid . '/edit', $url_options);
-    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $language_interface->langcode);
-    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed, 2, $language_interface->langcode);
-    $tests['[comment:parent:cid]'] = $comment->pid;
-    $tests['[comment:parent:title]'] = check_plain($parent_comment->subject);
-    $tests['[comment:node:nid]'] = $comment->nid;
+    $tests['[comment:homepage]'] = check_url($comment->homepage->value);
+    $tests['[comment:title]'] = filter_xss($comment->subject->value);
+    $tests['[comment:body]'] = $comment->comment_body->processed;
+    $tests['[comment:url]'] = url('comment/' . $comment->cid->value, $url_options + array('fragment' => 'comment-' . $comment->cid->value));
+    $tests['[comment:edit-url]'] = url('comment/' . $comment->cid->value . '/edit', $url_options);
+    $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created->value->getTimestamp(), 2, $language_interface->langcode);
+    $tests['[comment:changed:since]'] = format_interval(REQUEST_TIME - $comment->changed->value->getTimestamp(), 2, $language_interface->langcode);
+    $tests['[comment:parent:cid]'] = $comment->pid->value;
+    $tests['[comment:parent:title]'] = check_plain($parent_comment->subject->value);
+    $tests['[comment:node:nid]'] = $comment->nid->value;
     $tests['[comment:node:title]'] = check_plain($node->title);
-    $tests['[comment:author:uid]'] = $comment->uid;
+    $tests['[comment:author:uid]'] = $comment->uid->value;
     $tests['[comment:author:name]'] = check_plain($this->admin_user->name);
 
     // Test to make sure that we generated something for each token.
@@ -77,13 +77,13 @@ function testCommentTokenReplacement() {
     }
 
     // Generate and test unsanitized tokens.
-    $tests['[comment:hostname]'] = $comment->hostname;
-    $tests['[comment:name]'] = $comment->name;
+    $tests['[comment:hostname]'] = $comment->hostname->value;
+    $tests['[comment:name]'] = $comment->name->value;
     $tests['[comment:mail]'] = $this->admin_user->mail;
-    $tests['[comment:homepage]'] = $comment->homepage;
-    $tests['[comment:title]'] = $comment->subject;
-    $tests['[comment:body]'] = $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0]['value'];
-    $tests['[comment:parent:title]'] = $parent_comment->subject;
+    $tests['[comment:homepage]'] = $comment->homepage->value;
+    $tests['[comment:title]'] = $comment->subject->value;
+    $tests['[comment:body]'] = $comment->comment_body->value;
+    $tests['[comment:parent:title]'] = $parent_comment->subject->value;
     $tests['[comment:node:title]'] = $node->title;
     $tests['[comment:author:name]'] = $this->admin_user->name;
 
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
index 7dfb02e..60cb474 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
@@ -99,6 +99,7 @@ protected function getNewEntityValues($langcode) {
    * Tests translate link on comment content admin page.
    */
   function testTranslateLinkCommentAdminPage() {
+    $this->drupalCreateContentType(array('type' => 'page', 'name' => 'page'));
     $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer comments', 'translate any entity'));
     $this->drupalLogin($this->admin_user);
 
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 29ebd3b..34d3967 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -7,6 +7,8 @@
 
 use Drupal\field\FieldValidationException;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityNG;
+use Drupal\Core\Entity\EntityBCDecorator;
 
 /**
  * @defgroup field_storage Field Storage API
@@ -1384,6 +1386,9 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
       // Add this entity to the items to be prepared.
       $prepare[$id] = $entity;
 
+      // Enable BC if necessary.
+      $entity = $entity->getBCEntity();
+
       // Determine the actual language code to display for each field, given the
       // language codes available in the field data.
       $options['langcode'][$id] = field_language($entity_type, $entity, NULL, $langcode);
@@ -1449,6 +1454,8 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
  *   A renderable array for the field values.
  */
 function field_attach_view($entity_type, EntityInterface $entity, $view_mode, $langcode = NULL, array $options = array()) {
+  // Enable BC if necessary.
+  $entity = $entity->getBCEntity();
   // Determine the actual language code to display for each field, given the
   // language codes available in the field data.
   $options['langcode'] = field_language($entity_type, $entity, NULL, $langcode);
@@ -1457,6 +1464,9 @@ function field_attach_view($entity_type, EntityInterface $entity, $view_mode, $l
   $null = NULL;
   $output = field_invoke_method('view', _field_invoke_formatter_target($view_mode), $entity, $view_mode, $null, $options);
 
+  // Remove the BC layer now.
+  $entity = $entity->getOriginalEntity();
+
   // Add custom weight handling.
   $output['#pre_render'][] = '_field_extra_fields_pre_render';
   $output['#entity_type'] = $entity_type;
diff --git a/core/modules/field/modules/text/lib/Drupal/text/TextProcessed.php b/core/modules/field/modules/text/lib/Drupal/text/TextProcessed.php
index 9284c2d..c73af6f 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/TextProcessed.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/TextProcessed.php
@@ -8,7 +8,7 @@
 namespace Drupal\text;
 
 use Drupal\Core\TypedData\ContextAwareInterface;
-use Drupal\Core\TypedData\Type\String;
+use Drupal\Core\TypedData\ContextAwareTypedData;
 use Drupal\Core\TypedData\ReadOnlyException;
 use InvalidArgumentException;
 
@@ -18,7 +18,7 @@
  * Required settings (below the definition's 'settings' key) are:
  *  - text source: The text property containing the to be processed text.
  */
-class TextProcessed extends String implements ContextAwareInterface {
+class TextProcessed extends ContextAwareTypedData {
 
   /**
    * The text property.
@@ -35,24 +35,10 @@ class TextProcessed extends String implements ContextAwareInterface {
   protected $format;
 
   /**
-   * The name.
-   *
-   * @var string
+   * Overrides ContextAwareTypedData::__construct().
    */
-  protected $name;
-
-  /**
-   * The parent data structure.
-   *
-   * @var \Drupal\Core\Entity\Field\FieldItemInterface
-   */
-  protected $parent;
-
-  /**
-   * Implements TypedDataInterface::__construct().
-   */
-  public function __construct(array $definition) {
-    $this->definition = $definition;
+  public function __construct(array $definition, $name = NULL, ContextAwareInterface $parent = NULL) {
+    parent::__construct($definition, $name, $parent);
 
     if (!isset($definition['settings']['text source'])) {
       throw new InvalidArgumentException("The definition's 'source' key has to specify the name of the text property to be processed.");
@@ -60,39 +46,18 @@ public function __construct(array $definition) {
   }
 
   /**
-   * Implements ContextAwareInterface::getName().
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setName().
-   */
-  public function setName($name) {
-    $this->name = $name;
-  }
-
-  /**
-   * Implements ContextAwareInterface::getParent().
-   *
-   * @return \Drupal\Core\Entity\Field\FieldItemInterface
+   * Overrides ContextAwareTypedData::setContext().
    */
-  public function getParent() {
-    return $this->parent;
-  }
-
-  /**
-   * Implements ContextAwareInterface::setParent().
-   */
-  public function setParent($parent) {
-    $this->parent = $parent;
-    $this->text = $parent->get($this->definition['settings']['text source']);
-    $this->format = $parent->get('format');
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    parent::setContext($name, $parent);
+    if (isset($parent)) {
+      $this->text = $parent->get($this->definition['settings']['text source']);
+      $this->format = $parent->get('format');
+    }
   }
 
   /**
-   * Implements TypedDataInterface::getValue().
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::getValue().
    */
   public function getValue($langcode = NULL) {
 
@@ -104,21 +69,28 @@ public function getValue($langcode = NULL) {
     $entity = $field->getParent();
     $instance = field_info_instance($entity->entityType(), $field->getName(), $entity->bundle());
 
-    if (!empty($instance['settings']['text_processing']) && $this->format->value) {
-      return check_markup($this->text->value, $this->format->value, $entity->language()->langcode);
+    if (!empty($instance['settings']['text_processing']) && $this->format->getValue()) {
+      return check_markup($this->text->getValue(), $this->format->getValue(), $entity->language()->langcode);
     }
     else {
       // If no format is available, still make sure to sanitize the text.
-      return check_plain($this->text->value);
+      return check_plain($this->text->getValue());
     }
   }
 
   /**
-   * Implements TypedDataInterface::setValue().
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::setValue().
    */
   public function setValue($value) {
     if (isset($value)) {
       throw new ReadOnlyException('Unable to set a computed property.');
     }
   }
+
+  /**
+   * Implements \Drupal\Core\TypedData\TypedDataInterface::validate().
+   */
+  public function validate() {
+    // @todo: Implement.
+  }
 }
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Type/TextItem.php b/core/modules/field/modules/text/lib/Drupal/text/Type/TextItem.php
index 07ae7f9..2077b42 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Type/TextItem.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Type/TextItem.php
@@ -10,12 +10,12 @@
 use Drupal\Core\Entity\Field\FieldItemBase;
 
 /**
- * Defines the 'text_item' and 'text_long_item' entity field items.
+ * Defines the 'text_field' and 'text_long_field' entity field items.
  */
 class TextItem extends FieldItemBase {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/modules/field/modules/text/lib/Drupal/text/Type/TextSummaryItem.php b/core/modules/field/modules/text/lib/Drupal/text/Type/TextSummaryItem.php
index b6438ff..67624c4 100644
--- a/core/modules/field/modules/text/lib/Drupal/text/Type/TextSummaryItem.php
+++ b/core/modules/field/modules/text/lib/Drupal/text/Type/TextSummaryItem.php
@@ -8,12 +8,12 @@
 namespace Drupal\text\Type;
 
 /**
- * Defines the 'text_with_summary' entity field item.
+ * Defines the 'text_with_summary_field' entity field item.
  */
 class TextSummaryItem extends TextItem {
 
   /**
-   * Field definitions of the contained properties.
+   * Definitions of the contained properties.
    *
    * @see self::getPropertyDefinitions()
    *
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index 9ec3ec6..3a9b149 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -491,7 +491,7 @@ function forum_taxonomy_term_delete(Term $term) {
  * comment_save() calls hook_comment_publish() for all published comments.
  */
 function forum_comment_publish($comment) {
-  _forum_update_forum_index($comment->nid);
+  _forum_update_forum_index($comment->nid->value);
 }
 
 /**
@@ -503,8 +503,8 @@ function forum_comment_publish($comment) {
 function forum_comment_update($comment) {
   // comment_save() calls hook_comment_publish() for all published comments,
   // so we need to handle all other values here.
-  if (!$comment->status) {
-    _forum_update_forum_index($comment->nid);
+  if (!$comment->status->value) {
+    _forum_update_forum_index($comment->nid->value);
   }
 }
 
@@ -512,14 +512,14 @@ function forum_comment_update($comment) {
  * Implements hook_comment_unpublish().
  */
 function forum_comment_unpublish($comment) {
-  _forum_update_forum_index($comment->nid);
+  _forum_update_forum_index($comment->nid->value);
 }
 
 /**
  * Implements hook_comment_delete().
  */
 function forum_comment_delete($comment) {
-  _forum_update_forum_index($comment->nid);
+  _forum_update_forum_index($comment->nid->value);
 }
 
 /**
diff --git a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
index e121cb8..c75512e 100644
--- a/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
+++ b/core/modules/forum/lib/Drupal/forum/Tests/ForumBlockTest.php
@@ -111,7 +111,7 @@ function testActiveForumTopicsBlock() {
       $comment = entity_create('comment', array(
         'nid' => $node->nid,
         'subject' => $this->randomString(20),
-        'comment_body' => array(LANGUAGE_NOT_SPECIFIED => $this->randomString(256)),
+        'comment_body' => $this->randomString(256),
         'created' => $timestamp + $index,
       ));
       comment_save($comment);
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php
index bf8f845..3dc24ed 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessPagerTest.php
@@ -49,9 +49,7 @@ public function testCommentPager() {
         'nid' => $node->nid,
         'subject' => $this->randomName(),
         'comment_body' => array(
-          LANGUAGE_NOT_SPECIFIED => array(
-            array('value' => $this->randomName()),
-          ),
+          array('value' => $this->randomName()),
         ),
       ));
       $comment->save();
diff --git a/core/modules/node/node.api.php b/core/modules/node/node.api.php
index c139952..da078e4 100644
--- a/core/modules/node/node.api.php
+++ b/core/modules/node/node.api.php
@@ -754,7 +754,7 @@ function hook_node_update_index(Drupal\node\Node $node, $langcode) {
   $text = '';
   $comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED));
   foreach ($comments as $comment) {
-    $text .= '<h2>' . check_plain($comment->subject) . '</h2>' . check_markup($comment->comment, $comment->format, '', TRUE);
+    $text .= '<h2>' . check_plain($comment->subject->value) . '</h2>' . $comment->comment_body->processed;
   }
   return $text;
 }
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
index fc883cb..992e94e 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/CommentAttributesTest.php
@@ -156,7 +156,7 @@ public function testCommentReplyOfRdfaMarkup() {
     $this->assertFalse($result, 'No RDFa markup referring to the comment itself is present.');
 
     // Posts a reply to the first comment.
-    $this->drupalGet('comment/reply/' . $this->node1->nid . '/' . $comments[0]->id);
+    $this->drupalGet('comment/reply/' . $this->node1->nid . '/' . $comments[0]->id());
     $comments[] = $this->postComment(NULL, $this->randomName(), $this->randomName(), TRUE);
 
     // Tests the reply_of relationship of a second level comment.
@@ -181,7 +181,7 @@ function _testBasicCommentRdfaMarkup($comment, $account = array()) {
     $comment_container = $this->xpath('//div[contains(@class, "comment") and contains(@typeof, "sioct:Comment")]');
     $this->assertTrue(!empty($comment_container), 'Comment RDF type for comment found.');
     $comment_title = $this->xpath('//div[contains(@class, "comment") and contains(@typeof, "sioct:Comment")]//h3[@property="dc:title"]');
-    $this->assertEqual((string) $comment_title[0]->a, $comment->subject, 'RDFa markup for the comment title found.');
+    $this->assertEqual((string) $comment_title[0]->a, $comment->subject->value, 'RDFa markup for the comment title found.');
     $comment_date = $this->xpath('//div[contains(@class, "comment") and contains(@typeof, "sioct:Comment")]//*[contains(@property, "dc:date") and contains(@property, "dc:created")]');
     $this->assertTrue(!empty($comment_date), 'RDFa markup for the date of the comment found.');
     // The author tag can be either a or span
@@ -189,6 +189,6 @@ function _testBasicCommentRdfaMarkup($comment, $account = array()) {
     $name = empty($account["name"]) ? $this->web_user->name : $account["name"] . " (not verified)";
     $this->assertEqual((string) $comment_author[0], $name, 'RDFa markup for the comment author found.');
     $comment_body = $this->xpath('//div[contains(@class, "comment") and contains(@typeof, "sioct:Comment")]//div[@class="content"]//div[contains(@class, "comment-body")]//div[@property="content:encoded"]');
-    $this->assertEqual((string) $comment_body[0]->p, $comment->comment, 'RDFa markup for the comment body found.');
+    $this->assertEqual((string) $comment_body[0]->p, $comment->comment_body->value, 'RDFa markup for the comment body found.');
   }
 }
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index 2e04773..182d63c 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -428,10 +428,10 @@ function rdf_comment_load($comments) {
     // Pages with many comments can show poor performance. This information
     // isn't needed until rdf_preprocess_comment() is called, but set it here
     // to optimize performance for websites that implement an entity cache.
-    $comment->rdf_data['date'] = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created);
-    $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid);
-    if ($comment->pid) {
-      $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid, array('fragment' => 'comment-' . $comment->pid));
+    $comment->rdf_data['date'] = rdf_rdfa_attributes($comment->rdf_mapping['created'], $comment->created->value->getTimestamp());
+    $comment->rdf_data['nid_uri'] = url('node/' . $comment->nid->value);
+    if ($comment->pid->value) {
+      $comment->rdf_data['pid_uri'] = url('comment/' . $comment->pid->value, array('fragment' => 'comment-' . $comment->pid->value));
     }
   }
 }
@@ -627,28 +627,30 @@ function rdf_preprocess_user_profile(&$variables) {
     $variables['attributes']['typeof'] = $account->rdf_mapping['rdftype'];
     $variables['attributes']['about'] = url($uri['path'], $uri['options']);
   }
-  // Adds the relationship between the sioc:UserAccount and the foaf:Person who
-  // holds the account.
-  $account_holder_meta = array(
-    '#tag' => 'meta',
-    '#attributes' => array(
-      'about' => url($uri['path'], array_merge($uri['options'], array('fragment' => 'me'))),
-      'typeof' => array('foaf:Person'),
-      'rel' => array('foaf:account'),
-      'resource' => url($uri['path'], $uri['options']),
-    ),
-  );
-  // Adds the markup for username.
-  $username_meta = array(
-    '#tag' => 'meta',
-    '#attributes' => array(
-      'about' => url($uri['path'], $uri['options']),
-      'property' => $account->rdf_mapping['name']['predicates'],
-      'content' => $account->name,
-    )
-  );
-  drupal_add_html_head($account_holder_meta, 'rdf_user_account_holder');
-  drupal_add_html_head($username_meta, 'rdf_user_username');
+  // If we are on the user account page, add the relationship between the
+  // sioc:UserAccount and the foaf:Person who holds the account.
+  if (current_path() == $uri['path']) {
+    $account_holder_meta = array(
+      '#tag' => 'meta',
+      '#attributes' => array(
+        'about' => url($uri['path'], array_merge($uri['options'], array('fragment' => 'me'))),
+        'typeof' => array('foaf:Person'),
+        'rel' => array('foaf:account'),
+        'resource' => url($uri['path'], $uri['options']),
+      ),
+    );
+    // Adds the markup for username.
+    $username_meta = array(
+      '#tag' => 'meta',
+      '#attributes' => array(
+        'about' => url($uri['path'], $uri['options']),
+        'property' => $account->rdf_mapping['name']['predicates'],
+        'content' => $account->name,
+      )
+    );
+    drupal_add_html_head($account_holder_meta, 'rdf_user_account_holder');
+    drupal_add_html_head($username_meta, 'rdf_user_username');
+  }
 }
 
 /**
@@ -754,7 +756,7 @@ function rdf_preprocess_comment(&$variables) {
     $variables['rdf_metadata_attributes'][] = $parent_node_attributes;
 
     // Adds the relation to parent comment, if it exists.
-    if ($comment->pid != 0) {
+    if ($comment->pid->value != 0) {
       $parent_comment_attributes['rel'] = $comment->rdf_mapping['pid']['predicates'];
       // The parent comment URI is precomputed as part of the rdf_data so that
       // it can be cached as part of the entity.
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 866536e..d9919eb 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -832,7 +832,7 @@ function search_node_update(Node $node) {
  */
 function search_comment_insert($comment) {
   // Reindex the node when comments are added.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -840,7 +840,7 @@ function search_comment_insert($comment) {
  */
 function search_comment_update($comment) {
   // Reindex the node when comments are changed.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -848,7 +848,7 @@ function search_comment_update($comment) {
  */
 function search_comment_delete($comment) {
   // Reindex the node when comments are deleted.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -856,7 +856,7 @@ function search_comment_delete($comment) {
  */
 function search_comment_publish($comment) {
   // Reindex the node when comments are published.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
@@ -864,7 +864,7 @@ function search_comment_publish($comment) {
  */
 function search_comment_unpublish($comment) {
   // Reindex the node when comments are unpublished.
-  search_touch_node($comment->nid);
+  search_touch_node($comment->nid->value);
 }
 
 /**
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 001e521..a6ff91f 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -2074,9 +2074,9 @@ protected function createTypedData($definition, $value = NULL, $context = array(
     // Assert the definition of the wrapper.
     $this->assertTrue($data instanceof \Drupal\Core\TypedData\TypedDataInterface, 'Typed data object is an instance of the typed data interface.');
     $definition = $data->getDefinition();
-    $this->assertTrue(!empty($definition['label']), $definition['label'] . ' data definition was returned.');
+    $this->assertTrue(!empty($definition['type']), $definition['type'] . ' data definition was returned.');
     // Assert that the correct type was constructed.
-    $this->assertEqual($data->getType(), $type, $definition['label'] . ' object returned type.');
+    $this->assertEqual($data->getType(), $type, $definition['type'] . ' object returned type.');
     return $data;
   }
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php
index 4afb574..38e35bc 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityCrudHookTest.php
@@ -107,7 +107,7 @@ public function testCommentHooks() {
     ));
 
     $_SESSION['entity_crud_hook_test'] = array();
-    $comment = comment_load($comment->cid);
+    $comment = comment_load($comment->cid->value);
 
     $this->assertHookMessageOrder(array(
       'entity_crud_hook_test_entity_load called for type comment',
@@ -115,7 +115,7 @@ public function testCommentHooks() {
     ));
 
     $_SESSION['entity_crud_hook_test'] = array();
-    $comment->subject = 'New subject';
+    $comment->subject->value = 'New subject';
     comment_save($comment);
 
     $this->assertHookMessageOrder(array(
@@ -126,7 +126,7 @@ public function testCommentHooks() {
     ));
 
     $_SESSION['entity_crud_hook_test'] = array();
-    comment_delete($comment->cid);
+    comment_delete($comment->cid->value);
 
     $this->assertHookMessageOrder(array(
       'entity_crud_hook_test_comment_predelete called',
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 a152bfa..6f27146 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityFieldTest.php
@@ -215,6 +215,8 @@ public function testReadWrite() {
     $this->assertIdentical(count($entity->name), 0, 'Name field contains no items.');
     $this->assertIdentical($entity->name->getValue(), array(), 'Name field value is an empty array.');
 
+    $entity->name->value = 'foo';
+    $this->assertTrue($entity->name->value, 'foo', 'Name field set.');
     // Test removing all list items by setting it to NULL.
     $entity->name = NULL;
     $this->assertIdentical(count($entity->name), 0, 'Name field contains no items.');
@@ -313,6 +315,30 @@ public function testIntrospection() {
 
     // @todo: Once the user entity has definitions, continue testing getting
     // them from the $userref_values['entity'] property.
+
+    // Make sure provided contextual information is right.
+    $this->assertEqual($entity->getNamespace(), 'Drupal.core.entity.entity_test');
+    $this->assertEqual($entity->getPropertyPath(), '');
+    $this->assertEqual($entity->getName(), '');
+    $this->assertEqual($entity->getParent(), NULL);
+
+    $field = $entity->user_id;
+    $this->assertEqual($field->getNamespace(), 'Drupal.core.entity.entity_test');
+    $this->assertEqual($field->getPropertyPath(), 'user_id');
+    $this->assertEqual($field->getName(), 'user_id');
+    $this->assertIdentical($field->getParent(), $entity, 'Parent object matches.');
+
+    $field_item = $field[0];
+    $this->assertEqual($field_item->getNamespace(), 'Drupal.core.entity.entity_test');
+    $this->assertEqual($field_item->getPropertyPath(), 'user_id.0');
+    $this->assertEqual($field_item->getName(), '0');
+    $this->assertIdentical($field_item->getParent(), $field, 'Parent object matches.');
+
+    $item_value = $field_item->get('entity');
+    $this->assertEqual($item_value->getNamespace(), 'Drupal.core.entity.entity_test');
+    $this->assertEqual($item_value->getPropertyPath(), 'user_id.0.entity');
+    $this->assertEqual($item_value->getName(), 'entity');
+    $this->assertIdentical($item_value->getParent(), $field_item, 'Parent object matches.');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index 370622d..b41bf3d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -131,7 +131,7 @@ function testEntityLanguageMethods() {
       $this->pass('A translation for an invalid language is NULL.');
     }
 
-    // Try to get an untranslatable value from a translation in strict mode.
+    // Try to get an unstranslatable value from a translation in strict mode.
     try {
       $field_name = 'field_test_text';
       $value = $entity->getTranslation($this->langcodes[1])->get($field_name);
@@ -141,7 +141,7 @@ function testEntityLanguageMethods() {
       $this->pass('Getting an untranslatable value from a translation in strict mode throws an exception.');
     }
 
-    // Try to get an untranslatable value from a translation in non-strict
+    // Try to get an unstranslatable value from a translation in non-strict
     // mode.
     $entity->set($field_name, array(0 => array('value' => 'default value')));
     $value = $entity->getTranslation($this->langcodes[1], FALSE)->get($field_name)->value;
@@ -156,7 +156,7 @@ function testEntityLanguageMethods() {
       $this->pass("Setting a translation for an invalid language throws an exception.");
     }
 
-    // Try to set an untranslatable value into a translation in strict mode.
+    // Try to set an unstranslatable value into a translation in strict mode.
     try {
       $entity->getTranslation($this->langcodes[1])->set($field_name, NULL);
       $this->fail("Setting an untranslatable value into a translation in strict mode throws an exception.");
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
index e3b45fa..d24ade8 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestStorageController.php
@@ -48,30 +48,38 @@ protected function buildPropertyQuery(QueryInterface $entity_query, array $value
    *   An array of entity objects implementing the EntityInterface.
    */
   protected function mapFromStorageRecords(array $records, $load_revision = FALSE) {
-    $records = parent::mapFromStorageRecords($records, $load_revision);
+    $property_values = $this->getPropertyValues($records, $load_revision);
 
-    // Load data of translatable properties.
-    $this->attachPropertyData($records, $load_revision);
+    foreach ($records as $id => $record) {
+      $values = isset($property_values[$id]) ? $property_values[$id] : array();
+
+      foreach ($record as $name => $value) {
+        $values[$name][LANGUAGE_DEFAULT][0]['value'] = $value;
+      }
+      $entity = new $this->entityClass($values, $this->entityType);
+      $records[$id] = $entity;
+    }
     return $records;
   }
 
   /**
    * Attaches property data in all languages for translatable properties.
    */
-  protected function attachPropertyData(&$queried_entities, $load_revision = FALSE) {
+  protected function getPropertyValues($records, $load_revision = FALSE) {
     $query = db_select('entity_test_property_data', 'data', array('fetch' => PDO::FETCH_ASSOC))
       ->fields('data')
-      ->condition('id', array_keys($queried_entities))
+      ->condition('id', array_keys($records))
       ->orderBy('data.id');
     if ($load_revision) {
       // Get revision id's.
       $revision_ids = array();
-      foreach ($queried_entities as $id => $entity) {
-        $revision_ids[] = $entity->get('revision_id')->value;
+      foreach ($records as $record) {
+        $revision_ids[] = $record->revision_id;
       }
       $query->condition('revision_id', $revision_ids);
     }
     $data = $query->execute();
+    $property_values = array();
 
     foreach ($data as $values) {
       $id = $values['id'];
@@ -79,9 +87,10 @@ protected function attachPropertyData(&$queried_entities, $load_revision = FALSE
       // LANGUAGE_DEFAULT as key.
       $langcode = empty($values['default_langcode']) ? $values['langcode'] : LANGUAGE_DEFAULT;
 
-      $queried_entities[$id]->name[$langcode][0]['value'] = $values['name'];
-      $queried_entities[$id]->user_id[$langcode][0]['value'] = $values['user_id'];
+      $property_values[$id]['name'][$langcode][0]['value'] = $values['name'];
+      $property_values[$id]['user_id'][$langcode][0]['value'] = $values['user_id'];
     }
+    return $property_values;
   }
 
   /**
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestTranslationController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestTranslationController.php
deleted file mode 100644
index e7ca050..0000000
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestTranslationController.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\translation_entity\EntityTranslationController.
- */
-
-namespace Drupal\entity_test;
-
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\translation_entity\EntityTranslationController;
-
-/**
- * Test entity translation controller.
- */
-class EntityTestTranslationController extends EntityTranslationController {
-
-  /**
-   * Overrides EntityTranslationControllerInterface::removeTranslation().
-   */
-  public function removeTranslation(EntityInterface $entity, $langcode) {
-    $translation = $entity->getTranslation($langcode);
-    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
-      $translation->$property_name = array();
-    }
-  }
-
-}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
index 9b4d368..22ac2ec 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
@@ -23,7 +23,7 @@
  *   form_controller_class = {
  *     "default" = "Drupal\entity_test\EntityTestFormController"
  *   },
- *   translation_controller_class = "Drupal\entity_test\EntityTestTranslationController",
+ *   translation_controller_class = "Drupal\translation_entity\EntityTranslationControllerNG",
  *   base_table = "entity_test",
  *   data_table = "entity_test_property_data",
  *   revision_table = "entity_test_property_revision",
@@ -74,14 +74,12 @@ class EntityTest extends EntityNG {
   public $user_id;
 
   /**
-   * Overrides Entity::__construct().
+   * Initialize the object. Invoked upon construction and wake up.
    */
-  public function __construct(array $values, $entity_type) {
-    parent::__construct($values, $entity_type);
-
+  protected function init() {
+    parent::init();
     // We unset all defined properties, so magic getters apply.
     unset($this->id);
-    unset($this->langcode);
     unset($this->uuid);
     unset($this->revision_id);
     unset($this->name);
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 92f5701..e014834 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -228,8 +228,8 @@ function tracker_node_predelete(Node $node, $arg = 0) {
 function tracker_comment_update($comment) {
   // comment_save() calls hook_comment_publish() for all published comments
   // so we need to handle all other values here.
-  if ($comment->status != COMMENT_PUBLISHED) {
-    _tracker_remove($comment->nid, $comment->uid, $comment->changed);
+  if ($comment->status->value != COMMENT_PUBLISHED) {
+    _tracker_remove($comment->nid->value, $comment->uid->value, $comment->changed->value->getTimestamp());
   }
 }
 
@@ -240,21 +240,21 @@ function tracker_comment_update($comment) {
  * comment_save() calls hook_comment_publish() for all published comments.
  */
 function tracker_comment_publish($comment) {
-  _tracker_add($comment->nid, $comment->uid, $comment->changed);
+  _tracker_add($comment->nid->value, $comment->uid->value, $comment->changed->value->getTimestamp());
 }
 
 /**
  * Implements hook_comment_unpublish().
  */
 function tracker_comment_unpublish($comment) {
-  _tracker_remove($comment->nid, $comment->uid, $comment->changed);
+  _tracker_remove($comment->nid->value, $comment->uid->value, $comment->changed->value->getTimestamp());
 }
 
 /**
  * Implements hook_comment_delete().
  */
 function tracker_comment_delete($comment) {
-  _tracker_remove($comment->nid, $comment->uid, $comment->changed);
+  _tracker_remove($comment->nid->value, $comment->uid->value, $comment->changed->value->getTimestamp());
 }
 
 /**
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php
new file mode 100644
index 0000000..8dfc538
--- /dev/null
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerNG.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\translation_entity\EntityTranslationControllerNG.
+ */
+
+namespace Drupal\translation_entity;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Test entity translation controller.
+ */
+class EntityTranslationControllerNG extends EntityTranslationController {
+
+  /**
+   * Overrides EntityTranslationController::removeTranslation().
+   */
+  public function removeTranslation(EntityInterface $entity, $langcode) {
+    $translation = $entity->getTranslation($langcode);
+    foreach ($translation->getPropertyDefinitions() as $property_name => $langcode) {
+      $translation->$property_name = array();
+    }
+  }
+}
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 eac0b7f..6ad80a3 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
@@ -292,7 +292,7 @@ protected function getEditValues($values, $langcode, $new = FALSE) {
    *   The translation object to act on.
    */
   protected function getTranslation(EntityInterface $entity, $langcode) {
-    return $entity instanceof EntityNG ? $entity->getTranslation($langcode) : $entity;
+    return $entity instanceof EntityNG ? $entity->getTranslation($langcode, FALSE) : $entity;
   }
 
   /**
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
index e9f3f6c..8741790 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserCancelTest.php
@@ -297,7 +297,7 @@ function testUserDelete() {
     $this->assertText(t('Your comment has been posted.'));
     $comments = entity_load_multiple_by_properties('comment', array('subject' => $edit['subject']));
     $comment = reset($comments);
-    $this->assertTrue($comment->cid, 'Comment found.');
+    $this->assertTrue($comment->cid->value, 'Comment found.');
 
     // Create a node with two revisions, the initial one belonging to the
     // cancelling user.
@@ -327,7 +327,7 @@ function testUserDelete() {
     $this->assertFalse(node_load($node->nid, TRUE), 'Node of the user has been deleted.');
     $this->assertFalse(node_revision_load($revision), 'Node revision of the user has been deleted.');
     $this->assertTrue(node_load($revision_node->nid, TRUE), "Current revision of the user's node was not deleted.");
-    $this->assertFalse(comment_load($comment->cid), 'Comment of the user has been deleted.');
+    $this->assertFalse(comment_load($comment->cid->value), 'Comment of the user has been deleted.');
 
     // Confirm that user is logged out.
     $this->assertNoText($account->name, 'Logged out.');
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 7dabb6d..b8a2f99 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -2620,22 +2620,6 @@ function user_build_filter_query(SelectInterface $query) {
 }
 
 /**
- * Implements hook_comment_view().
- */
-function user_comment_view($comment) {
-  if (config('user.settings')->get('signatures') && !empty($comment->signature)) {
-    // @todo This alters and replaces the original object value, so a
-    //   hypothetical process of loading, viewing, and saving will hijack the
-    //   stored data. Consider renaming to $comment->signature_safe or similar
-    //   here and elsewhere in Drupal 8.
-    $comment->signature = check_markup($comment->signature, $comment->signature_format, '', TRUE);
-  }
-  else {
-    $comment->signature = '';
-  }
-}
-
-/**
  * Returns HTML for a user signature.
  *
  * @param $variables
diff --git a/core/modules/views/lib/Drupal/views/Tests/Comment/DefaultViewRecentComments.php b/core/modules/views/lib/Drupal/views/Tests/Comment/DefaultViewRecentComments.php
index 73ddfe0..debd1a8 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Comment/DefaultViewRecentComments.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Comment/DefaultViewRecentComments.php
@@ -77,13 +77,12 @@ public function setUp() {
 
     // Create some comments and attach them to the created node.
     for ($i = 0; $i < $this->masterDisplayResults; $i++) {
-      $comment = entity_create('comment', array());
-      $comment->uid = 0;
-      $comment->nid = $this->node->nid;
-      $comment->subject = 'Test comment ' . $i;
-      $comment->node_type = 'comment_node_' . $this->node->type;
-      $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0]['value'] = 'Test body ' . $i;
-      $comment->comment_body[LANGUAGE_NOT_SPECIFIED][0]['format'] = 'full_html';
+      $comment = entity_create('comment', array('node_type' => 'comment_node_' . $this->node->type));
+      $comment->uid->value = 0;
+      $comment->nid->value = $this->node->nid;
+      $comment->subject->value = 'Test comment ' . $i;
+      $comment->comment_body->value = 'Test body ' . $i;
+      $comment->comment_body->format = 'full_html';
 
       comment_save($comment);
     }
@@ -108,10 +107,10 @@ public function testBlockDisplay() {
     );
     $expected_result = array();
     foreach (array_values($this->commentsCreated) as $key => $comment) {
-      $expected_result[$key]['nid'] = $comment->nid;
-      $expected_result[$key]['subject'] = $comment->subject;
-      $expected_result[$key]['cid'] = $comment->cid;
-      $expected_result[$key]['changed'] = $comment->changed;
+      $expected_result[$key]['nid'] = $comment->nid->value;
+      $expected_result[$key]['subject'] = $comment->subject->value;
+      $expected_result[$key]['cid'] = $comment->cid->value;
+      $expected_result[$key]['changed'] = $comment->changed->value->getTimestamp();
     }
     $this->assertIdenticalResultset($view, $expected_result, $map);
 
@@ -140,11 +139,11 @@ public function testPageDisplay() {
     );
     $expected_result = array();
     foreach (array_values($this->commentsCreated) as $key => $comment) {
-      $expected_result[$key]['nid'] = $comment->nid;
-      $expected_result[$key]['subject'] = $comment->subject;
-      $expected_result[$key]['changed'] = $comment->changed;
-      $expected_result[$key]['created'] = $comment->created;
-      $expected_result[$key]['cid'] = $comment->cid;
+      $expected_result[$key]['nid'] = $comment->nid->value;
+      $expected_result[$key]['subject'] = $comment->subject->value;
+      $expected_result[$key]['changed'] = $comment->changed->value->getTimestamp();
+      $expected_result[$key]['created'] = $comment->created->value->getTimestamp();
+      $expected_result[$key]['cid'] = $comment->cid->value;
     }
     $this->assertIdenticalResultset($view, $expected_result, $map);
 
diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
index fa8fc58..fc9b884 100644
--- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -9,6 +9,7 @@
 
 use Drupal\views\ViewExecutable;
 use Drupal\Core\Database\Database;
+use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\views\Plugin\views\query\Sql;
 use Drupal\views\Plugin\Core\Entity\View;
 use Drupal\views\ViewStorageInterface;
@@ -1073,4 +1074,52 @@ public function setOriginalID($id) {
     return $this->__call(__FUNCTION__, func_get_args());
   }
 
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getBCEntity().
+   */
+  public function getBCEntity() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::getOriginalEntity().
+   */
+  public function getOriginalEntity() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getName().
+   */
+  public function getName() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getNamespace().
+   */
+  public function getNamespace() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getPropertyPath().
+   */
+  public function getPropertyPath() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::getParent().
+   */
+  public function getParent() {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * Implements \Drupal\Core\TypedData\ContextAwareInterface::setContext().
+   */
+  public function setContext($name = NULL, ContextAwareInterface $parent = NULL) {
+    return $this->__call(__FUNCTION__, func_get_args());
+  }
 }
