diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index 53f5fe4..bae5f61 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -127,44 +127,6 @@ function entity_info_cache_clear() {
 }
 
 /**
- * Extracts ID, revision ID, and bundle name from an entity.
- *
- * @param $entity_type
- *   The entity type; e.g. 'node' or 'user'.
- * @param $entity
- *   The entity from which to extract values.
- *
- * @return
- *   A numerically indexed array (not a hash table) containing these
- *   elements:
- *   - 0: Primary ID of the entity.
- *   - 1: Revision ID of the entity, or NULL if $entity_type is not versioned.
- *   - 2: Bundle name of the entity.
- */
-function entity_extract_ids($entity_type, $entity) {
-  $info = entity_get_info($entity_type);
-
-  // Objects being created might not have id/vid yet.
-  $id = isset($entity->{$info['entity keys']['id']}) ? $entity->{$info['entity keys']['id']} : NULL;
-  $vid = ($info['entity keys']['revision'] && isset($entity->{$info['entity keys']['revision']})) ? $entity->{$info['entity keys']['revision']} : NULL;
-
-  if (!empty($info['entity keys']['bundle'])) {
-    // Explicitly fail for malformed entities missing the bundle property.
-    if (!isset($entity->{$info['entity keys']['bundle']}) || $entity->{$info['entity keys']['bundle']} === '') {
-      throw new EntityMalformedException(t('Missing bundle property on entity of type @entity_type.', array('@entity_type' => $entity_type)));
-    }
-    $bundle = $entity->{$info['entity keys']['bundle']};
-  }
-  else {
-    // The entity type provides no bundle key: assume a single bundle, named
-    // after the entity type.
-    $bundle = $entity_type;
-  }
-
-  return array($id, $vid, $bundle);
-}
-
-/**
  * Loads an entity from the database.
  *
  * @param string $entity_type
@@ -409,12 +371,11 @@ function entity_prepare_view($entity_type, $entities) {
  */
 function entity_uri($entity_type, $entity) {
   $info = entity_get_info($entity_type);
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
 
   // A bundle-specific callback takes precedence over the generic one for the
   // entity type.
-  if (isset($info['bundles'][$bundle]['uri callback'])) {
-    $uri_callback = $info['bundles'][$bundle]['uri callback'];
+  if (isset($info['bundles'][$entity->bundle()]['uri callback'])) {
+    $uri_callback = $info['bundles'][$entity->bundle()]['uri callback'];
   }
   elseif (isset($info['uri callback'])) {
     $uri_callback = $info['uri callback'];
@@ -465,7 +426,7 @@ function entity_form_field_validate($entity_type, $form, &$form_state) {
   // prior to processing the current form submission, and we must not update it
   // until we have fully validated the submitted input. Therefore, for
   // validation, act on a pseudo entity created out of the form values.
-  $pseudo_entity = (object) $form_state['values'];
+  $pseudo_entity = entity_create($entity_type, $form_state['values']);
   field_attach_form_validate($entity_type, $pseudo_entity, $form, $form_state);
 }
 
@@ -492,12 +453,11 @@ function entity_form_field_validate($entity_type, $form, &$form_state) {
  */
 function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
   $info = entity_get_info($entity_type);
-  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
 
   // Copy top-level form values that are not for fields to entity properties,
   // without changing existing entity properties that are not being edited by
   // this form. Copying field values must be done using field_attach_submit().
-  $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $bundle)) : $form_state['values'];
+  $values_excluding_fields = $info['fieldable'] ? array_diff_key($form_state['values'], field_info_instances($entity_type, $entity->bundle())) : $form_state['values'];
   foreach ($values_excluding_fields as $key => $value) {
     $entity->$key = $value;
   }
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php
index fd609d8..7749a49 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityFieldQueryTest.php
@@ -108,24 +108,25 @@ class EntityFieldQueryTest extends WebTestBase {
 
     // Create entities which have a 'bundle key' defined.
     for ($i = 1; $i < 7; $i++) {
-      $entity = new stdClass();
+      $entity = entity_create('test_entity_bundle_key', array());
       $entity->ftid = $i;
+      $entity->ftvid = $i;
       $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
 
       $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i;
-      drupal_write_record('test_entity_bundle_key', $entity);
-      field_attach_insert('test_entity_bundle_key', $entity);
+      $entity->enforceIsNew();
+      $entity->save();
     }
+    debug(1);
 
-    $entity = new stdClass();
-    $entity->ftid = 5;
-    $entity->fttype = 'test_entity_bundle';
+    $entity = entity_create('test_entity_bundle', array('ftid' => 5));
     $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][0]['shape'] = 'square';
     $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][0]['color'] = 'red';
     $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][1]['shape'] = 'circle';
     $entity->{$this->field_names[1]}[LANGUAGE_NOT_SPECIFIED][1]['color'] = 'blue';
-    drupal_write_record('test_entity_bundle', $entity);
-    field_attach_insert('test_entity_bundle', $entity);
+    $entity->enforceIsNew();
+    $entity->save();
+    debug(2);
 
     $instances[2] = $instance;
     $instances[2]['bundle'] = 'test_bundle';
@@ -135,23 +136,20 @@ class EntityFieldQueryTest extends WebTestBase {
 
     // Create entities with support for revisions.
     for ($i = 1; $i < 5; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
+      $entity = field_test_create_entity($i, $i);
       $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i;
-
-      drupal_write_record('test_entity', $entity);
-      field_attach_insert('test_entity', $entity);
-      drupal_write_record('test_entity_revision', $entity);
+      $entity->enforceIsNew();
+      debug(db_query('SELECT * FROM {test_entity}')->fetchAll());
+        debug($entity);
+      $entity->save();
     }
+    debug(3);
+
 
     // Add two revisions to an entity.
     for ($i = 100; $i < 102; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = 4;
+      $entity = field_test_create_entity(4);
       $entity->ftvid = $i;
-      $entity->fttype = 'test_bundle';
       $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i;
 
       drupal_write_record('test_entity', $entity, 'ftid');
@@ -986,12 +984,10 @@ class EntityFieldQueryTest extends WebTestBase {
     ), t('Test offset on a field.'), TRUE);
 
     for ($i = 6; $i < 10; $i++) {
-      $entity = new stdClass();
-      $entity->ftid = $i;
-      $entity->fttype = 'test_entity_bundle';
+      $entity = entity_create('test_entity_bundle', array('ftid' => $i));
       $entity->{$this->field_names[0]}[LANGUAGE_NOT_SPECIFIED][0]['value'] = $i - 5;
-      drupal_write_record('test_entity_bundle', $entity);
-      field_attach_insert('test_entity_bundle', $entity);
+      $entity->enforceIsNew();
+      $entity->save();
     }
 
     $query = new EntityFieldQuery();
@@ -1068,10 +1064,7 @@ class EntityFieldQueryTest extends WebTestBase {
     field_test_entity_info_translatable('test_entity', TRUE);
 
     // Create more items with different languages.
-    $entity = new stdClass();
-    $entity->ftid = 1;
-    $entity->ftvid = 1;
-    $entity->fttype = 'test_bundle';
+    $entity = entity_load('test_entity', 1);
 
     // Set fields in two languages with one field value.
     foreach (array(LANGUAGE_NOT_SPECIFIED, 'en') as $langcode) {
@@ -1105,10 +1098,7 @@ class EntityFieldQueryTest extends WebTestBase {
     field_test_entity_info_translatable('test_entity', TRUE);
 
     // Create more items with different languages.
-    $entity = new stdClass();
-    $entity->ftid = 1;
-    $entity->ftvid = 1;
-    $entity->fttype = 'test_bundle';
+    $entity = entity_load('test_entity', 1);
     $j = 0;
 
     foreach (array(LANGUAGE_NOT_SPECIFIED, 'en') as $langcode) {
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index be98e3a..f770e06 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -584,15 +584,14 @@ function hook_field_storage_update_field($field, $prior_field, $has_data) {
  * @see hook_field_update()
  */
 function hook_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   foreach ($items as $delta => $item) {
     // For hook_file_references(), remember that this is being deleted.
     $item['file_field_name'] = $field['field_name'];
     // Pass in the ID of the object that is being removed so all references can
     // be counted in hook_file_references().
     $item['file_field_type'] = $entity_type;
-    $item['file_field_id'] = $id;
-    file_field_delete_file($item, $field, $entity_type, $id);
+    $item['file_field_id'] = $entity->id();
+    file_field_delete_file($item, $field, $entity_type, $entity->id());
   }
 }
 
@@ -617,11 +616,10 @@ function hook_field_delete($entity_type, $entity, $field, $instance, $langcode,
  *   $entity->{$field['field_name']}[$langcode], or an empty array if unset.
  */
 function hook_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   foreach ($items as $delta => $item) {
     // For hook_file_references, remember that this file is being deleted.
     $item['file_field_name'] = $field['field_name'];
-    if (file_field_delete_file($item, $field, $entity_type, $id)) {
+    if (file_field_delete_file($item, $field, $entity_type, $entity->id())) {
       $items[$delta] = NULL;
     }
   }
@@ -1794,7 +1792,9 @@ function hook_field_storage_load($entity_type, $entities, $age, $fields, $option
  *   array are field IDs.
  */
 function hook_field_storage_write($entity_type, $entity, $op, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $id = $entity->id();
+  $vid = $entity->getRevisionId();
+  $bundle = $entity->bundle();
   if (!isset($vid)) {
     $vid = $id;
   }
@@ -1888,9 +1888,7 @@ function hook_field_storage_write($entity_type, $entity, $op, $fields) {
  *   array are field IDs.
  */
 function hook_field_storage_delete($entity_type, $entity, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     if (isset($fields[$instance['field_id']])) {
       $field = field_info_field_by_id($instance['field_id']);
       field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance);
@@ -1918,15 +1916,14 @@ function hook_field_storage_delete($entity_type, $entity, $fields) {
  *   array are field IDs.
  */
 function hook_field_storage_delete_revision($entity_type, $entity, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
+  $vid = $entity->getRevisionId();
   if (isset($vid)) {
     foreach ($fields as $field_id) {
       $field = field_info_field_by_id($field_id);
       $revision_name = _field_sql_storage_revision_tablename($field);
       db_delete($revision_name)
         ->condition('entity_type', $entity_type)
-        ->condition('entity_id', $id)
+        ->condition('entity_id', $entity->id())
         ->condition('revision_id', $vid)
         ->execute();
     }
@@ -2631,17 +2628,15 @@ function hook_field_storage_purge_field_instance($instance) {
  *   The deleted field instance whose data is being purged.
  */
 function hook_field_storage_purge($entity_type, $entity, $field, $instance) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   $table_name = _field_sql_storage_tablename($field);
   $revision_name = _field_sql_storage_revision_tablename($field);
   db_delete($table_name)
     ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $id)
+    ->condition('entity_id', $entity->id())
     ->execute();
   db_delete($revision_name)
     ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $id)
+    ->condition('entity_id', $entity->id())
     ->execute();
 }
 
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index e4b28a9..a683965 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\field\FieldValidationException;
+use Drupal\entity\EntityInterface;
 
 /**
  * @defgroup field_storage Field Storage API
@@ -122,7 +123,7 @@ const FIELD_STORAGE_INSERT = 'insert';
  *   - prepare translation
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The fully formed $entity_type entity.
  * @param $a
  *   - The $form in the 'form' operation.
@@ -156,7 +157,7 @@ const FIELD_STORAGE_INSERT = 'insert';
  *    name. It will be used to narrow down to a single value the available
  *    languages to act on.
  */
-function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $options = array()) {
+function _field_invoke($op, $entity_type, EntityInterface $entity, &$a = NULL, &$b = NULL, $options = array()) {
   // Merge default options.
   $default_options = array(
     'default' => FALSE,
@@ -166,8 +167,11 @@ function _field_invoke($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $opti
   $options += $default_options;
 
   // Determine the list of instances to iterate on.
-  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-  $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+  if (!($entity instanceof EntityInterface)) {
+    debug($entity);
+    debug(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
+  }
+  $instances = _field_invoke_get_instances($entity_type, $entity->bundle(), $options);
 
   // Iterate through the instances and collect results.
   $return = array();
@@ -279,8 +283,8 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
   // $entity->$field_name property.
   foreach ($entities as $entity) {
     // Determine the list of instances to iterate on.
-    list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-    $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+    $instances = _field_invoke_get_instances($entity_type, $entity->bundle(), $options);
+    $id = $entity->id();
 
     foreach ($instances as $instance) {
       $field_id = $instance['field_id'];
@@ -359,7 +363,7 @@ function _field_invoke_multiple($op, $entity_type, $entities, &$a = NULL, &$b =
  *
  * @see _field_invoke()
  */
-function _field_invoke_default($op, $entity_type, $entity, &$a = NULL, &$b = NULL, $options = array()) {
+function _field_invoke_default($op, $entity_type, EntityInterface $entity, &$a = NULL, &$b = NULL, $options = array()) {
   $options['default'] = TRUE;
   return _field_invoke($op, $entity_type, $entity, $a, $b, $options);
 }
@@ -519,7 +523,7 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity for which to load form elements, used to initialize
  *   default form values.
  * @param $form
@@ -537,7 +541,7 @@ function _field_invoke_get_instances($entity_type, $bundle, $options) {
  * @see field_form_get_state()
  * @see field_form_set_state()
  */
-function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode = NULL) {
+function field_attach_form($entity_type, EntityInterface $entity, &$form, &$form_state, $langcode = NULL) {
   // Set #parents to 'top-level' by default.
   $form += array('#parents' => array());
 
@@ -546,10 +550,9 @@ function field_attach_form($entity_type, $entity, &$form, &$form_state, $langcod
   $form += (array) _field_invoke_default('form', $entity_type, $entity, $form, $form_state, $options);
 
   // Add custom weight handling.
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   $form['#pre_render'][] = '_field_extra_fields_pre_render';
   $form['#entity_type'] = $entity_type;
-  $form['#bundle'] = $bundle;
+  $form['#bundle'] = $entity->bundle();
 
   // Let other modules make changes to the form.
   // Avoid module_invoke_all() to let parameters be taken by reference.
@@ -658,9 +661,13 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
     // Collect the storage backends used by the remaining fields in the entities.
     $storages = array();
     foreach ($queried_entities as $entity) {
-      list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-      $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
-
+      if (!($entity instanceof EntityInterface)) {
+        debug(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS));
+        debug($entity);
+      }
+      $instances = _field_invoke_get_instances($entity_type, $entity->bundle(), $options);
+      $id = $entity->id();
+      $vid = $entity->getRevisionId();
       foreach ($instances as $instance) {
         $field_name = $instance['field_name'];
         $field_id = $instance['field_id'];
@@ -694,8 +701,7 @@ function field_attach_load($entity_type, $entities, $age = FIELD_LOAD_CURRENT, $
     if ($cache_write) {
       foreach ($queried_entities as $id => $entity) {
         $data = array();
-        list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-        $instances = field_info_instances($entity_type, $bundle);
+        $instances = field_info_instances($entity_type, $entity->bundle());
         foreach ($instances as $instance) {
           $data[$instance['field_name']] = $queried_entities[$id]->{$instance['field_name']};
         }
@@ -741,7 +747,7 @@ function field_attach_load_revision($entity_type, $entities, $options = array())
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to validate.
  * @throws Drupal\field\FieldValidationException
  *   If validation errors are found, a FieldValidationException is thrown. The
@@ -786,7 +792,7 @@ function field_attach_validate($entity_type, $entity) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity being submitted. The 'bundle', 'id' and (if applicable)
  *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
@@ -796,7 +802,7 @@ function field_attach_validate($entity_type, $entity) {
  * @param $form_state
  *   An associative array containing the current state of the form.
  */
-function field_attach_form_validate($entity_type, $entity, $form, &$form_state) {
+function field_attach_form_validate($entity_type, EntityInterface $entity, $form, &$form_state) {
   // Extract field values from submitted values.
   _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
 
@@ -826,7 +832,7 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity being submitted. The 'bundle', 'id' and (if applicable)
  *   'revision' keys should be present. The actual field values will be read
  *   from $form_state['values'].
@@ -836,7 +842,7 @@ function field_attach_form_validate($entity_type, $entity, $form, &$form_state)
  * @param $form_state
  *   An associative array containing the current state of the form.
  */
-function field_attach_submit($entity_type, $entity, $form, &$form_state) {
+function field_attach_submit($entity_type, EntityInterface $entity, $form, &$form_state) {
   // Extract field values from submitted values.
   _field_invoke_default('extract_form_values', $entity_type, $entity, $form, $form_state);
 
@@ -878,18 +884,16 @@ function field_attach_presave($entity_type, $entity) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to save.
  * @return
  *   Default values (if any) will be added to the $entity parameter for fields
  *   it leaves unspecified.
  */
-function field_attach_insert($entity_type, $entity) {
+function field_attach_insert($entity_type, EntityInterface $entity) {
   _field_invoke_default('insert', $entity_type, $entity);
   _field_invoke('insert', $entity_type, $entity);
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Let any module insert field data before the storage engine, accumulating
   // saved fields along the way.
   $skip_fields = array();
@@ -900,7 +904,7 @@ function field_attach_insert($entity_type, $entity) {
 
   // Collect the storage backends used by the remaining fields in the entities.
   $storages = array();
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     $field = field_info_field_by_id($instance['field_id']);
     $field_id = $field['id'];
     $field_name = $field['field_name'];
@@ -929,14 +933,12 @@ function field_attach_insert($entity_type, $entity) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to save.
  */
-function field_attach_update($entity_type, $entity) {
+function field_attach_update($entity_type, EntityInterface $entity) {
   _field_invoke('update', $entity_type, $entity);
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Let any module update field data before the storage engine, accumulating
   // saved fields along the way.
   $skip_fields = array();
@@ -947,7 +949,7 @@ function field_attach_update($entity_type, $entity) {
 
   // Collect the storage backends used by the remaining fields in the entities.
   $storages = array();
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     $field = field_info_field_by_id($instance['field_id']);
     $field_id = $field['id'];
     $field_name = $field['field_name'];
@@ -974,7 +976,7 @@ function field_attach_update($entity_type, $entity) {
 
   $entity_info = entity_get_info($entity_type);
   if ($entity_info['field cache']) {
-    cache('field')->delete("field:$entity_type:$id");
+    cache('field')->delete("field:$entity_type:" . $entity->id());
   }
 }
 
@@ -984,17 +986,15 @@ function field_attach_update($entity_type, $entity) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity whose field data to delete.
  */
 function field_attach_delete($entity_type, $entity) {
   _field_invoke('delete', $entity_type, $entity);
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Collect the storage backends used by the fields in the entities.
   $storages = array();
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     $field = field_info_field_by_id($instance['field_id']);
     $field_id = $field['id'];
     $storages[$field['storage']['type']][$field_id] = $field_id;
@@ -1011,7 +1011,7 @@ function field_attach_delete($entity_type, $entity) {
 
   $entity_info = entity_get_info($entity_type);
   if ($entity_info['field cache']) {
-    cache('field')->delete("field:$entity_type:$id");
+    cache('field')->delete("field:$entity_type:" . $entity->id());
   }
 }
 
@@ -1021,17 +1021,15 @@ function field_attach_delete($entity_type, $entity) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to save.
  */
 function field_attach_delete_revision($entity_type, $entity) {
   _field_invoke('delete_revision', $entity_type, $entity);
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Collect the storage backends used by the fields in the entities.
   $storages = array();
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     $field = field_info_field_by_id($instance['field_id']);
     $field_id = $field['id'];
     $storages[$field['storage']['type']][$field_id] = $field_id;
@@ -1138,7 +1136,7 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to render.
  * @param $view_mode
  *   View mode, e.g. 'full', 'teaser'...
@@ -1148,7 +1146,7 @@ function field_attach_prepare_view($entity_type, $entities, $view_mode, $langcod
  * @return
  *   A renderable array for the field values.
  */
-function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL) {
+function field_attach_view($entity_type, EntityInterface $entity, $view_mode, $langcode = NULL) {
   // Determine the actual language code to display for each field, given the
   // language codes available in the field data.
   $display_langcode = field_language($entity_type, $entity, NULL, $langcode);
@@ -1159,10 +1157,9 @@ function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL)
   $output = _field_invoke_default('view', $entity_type, $entity, $view_mode, $null, $options);
 
   // Add custom weight handling.
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   $output['#pre_render'][] = '_field_extra_fields_pre_render';
   $output['#entity_type'] = $entity_type;
-  $output['#bundle'] = $bundle;
+  $output['#bundle'] = $entity->bundle();
 
   // Let other modules alter the renderable array.
   $context = array(
@@ -1192,7 +1189,7 @@ function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL)
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity with fields to render.
  * @param $element
  *   The structured array containing the values ready for rendering.
@@ -1200,10 +1197,8 @@ function field_attach_view($entity_type, $entity, $view_mode, $langcode = NULL)
  *   The variables array is passed by reference and will be populated with field
  *   values.
  */
-function field_attach_preprocess($entity_type, $entity, $element, &$variables) {
-  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
-
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+function field_attach_preprocess($entity_type, EntityInterface $entity, $element, &$variables) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     $field_name = $instance['field_name'];
     if (isset($element[$field_name]['#language'])) {
       $langcode = $element[$field_name]['#language'];
@@ -1234,7 +1229,7 @@ function field_attach_preprocess($entity_type, $entity, $element, &$variables) {
  *
  * @param $entity_type
  *   The type of $entity; e.g. 'node' or 'user'.
- * @param $entity
+ * @param Drupal\entity\EntityInterface $entity
  *   The entity to be prepared for translation.
  * @param $langcode
  *   The language the entity has to be translated in.
@@ -1243,7 +1238,7 @@ function field_attach_preprocess($entity_type, $entity, $element, &$variables) {
  * @param $source_langcode
  *   The source language from which translate.
  */
-function field_attach_prepare_translation($entity_type, $entity, $langcode, $source_entity, $source_langcode) {
+function field_attach_prepare_translation($entity_type, EntityInterface $entity, $langcode, $source_entity, $source_langcode) {
   $options = array('langcode' => $langcode);
   // Copy source field values into the entity to be prepared.
   _field_invoke_default('prepare_translation', $entity_type, $entity, $source_entity, $source_langcode, $options);
diff --git a/core/modules/field/field.default.inc b/core/modules/field/field.default.inc
index 1e1675c..b4a6f50 100644
--- a/core/modules/field/field.default.inc
+++ b/core/modules/field/field.default.inc
@@ -189,8 +189,6 @@ function field_default_prepare_view($entity_type, $entities, $field, $instances,
  *     of $instance definitions.
  */
 function field_default_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   $addition = array();
 
   // Prepare incoming display specifications.
@@ -222,7 +220,7 @@ function field_default_view($entity_type, $entity, $field, $instance, $langcode,
           '#field_type' => $field['type'],
           '#field_translatable' => $field['translatable'],
           '#entity_type' => $entity_type,
-          '#bundle' => $bundle,
+          '#bundle' => $entity->bundle(),
           '#object' => $entity,
           '#items' => $items,
           '#formatter' => $display['type']
diff --git a/core/modules/field/field.form.inc b/core/modules/field/field.form.inc
index d9a0342..bd81c7c 100644
--- a/core/modules/field/field.form.inc
+++ b/core/modules/field/field.form.inc
@@ -30,7 +30,7 @@
  *   or an empty array to use default values.
  * @param $form
  *   An array representing the form that the editing element will be attached
- *   to. 
+ *   to.
  * @param $form_state
  *   An array containing the current state of the form.
  * @param $get_delta
@@ -43,7 +43,7 @@ function field_default_form($entity_type, $entity, $field, $instance, $langcode,
   // This could be called with no entity, as when a UI module creates a
   // dummy form to set default values.
   if ($entity) {
-    list($id, , ) = entity_extract_ids($entity_type, $entity);
+    $id = $entity->id();
   }
 
   $parents = $form['#parents'];
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index d42e5ac..8157971 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -910,7 +910,7 @@ function field_view_field($entity_type, $entity, $field_name, $display = array()
 
     // Invoke prepare_view steps if needed.
     if (empty($entity->_field_view_prepared)) {
-      list($id) = entity_extract_ids($entity_type, $entity);
+      $id = $entity->id();
 
       // First let the field types do their preparation.
       _field_invoke_multiple('prepare_view', $entity_type, array($id => $entity), $display, $null, $options);
diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc
index 533c881..e5e81ce 100644
--- a/core/modules/field/field.multilingual.inc
+++ b/core/modules/field/field.multilingual.inc
@@ -295,7 +295,8 @@ function field_valid_language($langcode, $default = TRUE) {
  */
 function field_language($entity_type, $entity, $field_name = NULL, $langcode = NULL) {
   $display_langcodes = &drupal_static(__FUNCTION__, array());
-  list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
+  $id = $entity->bundle();
+  $bundle = $entity->bundle();
   $langcode = field_valid_language($langcode, FALSE);
 
   if (!isset($display_langcodes[$entity_type][$id][$langcode])) {
diff --git a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
index 4405000..58d5593 100644
--- a/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/TranslationTest.php
@@ -343,7 +343,7 @@ class TranslationTest extends FieldTestBase {
     $field_name = $this->field['field_name'];
 
     // Store the field translations.
-    $entity->is_new = TRUE;
+    $entity->enforceIsNew();
     foreach ($available_langcodes as $langcode => $value) {
       $entity->{$field_name}[$langcode][0]['value'] = $value + 1;
     }
diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.module b/core/modules/field/modules/field_sql_storage/field_sql_storage.module
index fd2f52c..69e8bda 100644
--- a/core/modules/field/modules/field_sql_storage/field_sql_storage.module
+++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.module
@@ -378,7 +378,9 @@ function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fi
  * Implements hook_field_storage_write().
  */
 function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $vid = $entity->getRevisionId();
+  $id = $entity->id();
+  $bundle = $entity->bundle();
   if (!isset($vid)) {
     $vid = $id;
   }
@@ -463,9 +465,7 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel
  * This function deletes data for all fields for an entity from the database.
  */
 function field_sql_storage_field_storage_delete($entity_type, $entity, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
-  foreach (field_info_instances($entity_type, $bundle) as $instance) {
+  foreach (field_info_instances($entity_type, $entity->bundle()) as $instance) {
     if (isset($fields[$instance['field_id']])) {
       $field = field_info_field_by_id($instance['field_id']);
       field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance);
@@ -480,17 +480,15 @@ function field_sql_storage_field_storage_delete($entity_type, $entity, $fields)
  * an entity.
  */
 function field_sql_storage_field_storage_purge($entity_type, $entity, $field, $instance) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   $table_name = _field_sql_storage_tablename($field);
   $revision_name = _field_sql_storage_revision_tablename($field);
   db_delete($table_name)
     ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $id)
+    ->condition('entity_id', $entity->id())
     ->execute();
   db_delete($revision_name)
     ->condition('entity_type', $entity_type)
-    ->condition('entity_id', $id)
+    ->condition('entity_id', $entity->id())
     ->execute();
 }
 
@@ -655,15 +653,14 @@ function _field_sql_storage_query_field_conditions(EntityFieldQuery $query, Sele
  * This function actually deletes the data from the database.
  */
 function field_sql_storage_field_storage_delete_revision($entity_type, $entity, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
+  $vid = $entity->getRevisionId();
   if (isset($vid)) {
     foreach ($fields as $field_id) {
       $field = field_info_field_by_id($field_id);
       $revision_name = _field_sql_storage_revision_tablename($field);
       db_delete($revision_name)
         ->condition('entity_type', $entity_type)
-        ->condition('entity_id', $id)
+        ->condition('entity_id', $entity->id())
         ->condition('revision_id', $vid)
         ->execute();
     }
diff --git a/core/modules/field/modules/options/tests/options_test.module b/core/modules/field/modules/options/tests/options_test.module
index e30b1ef..604205b 100644
--- a/core/modules/field/modules/options/tests/options_test.module
+++ b/core/modules/field/modules/options/tests/options_test.module
@@ -28,5 +28,5 @@ function options_test_allowed_values_callback($field, $instance, $entity_type, $
 function options_test_dynamic_values_callback($field, $instance, $entity_type, $entity, &$cacheable) {
   $cacheable = FALSE;
   // We need the values of the entity as keys.
-  return drupal_map_assoc(array_merge(array($entity->ftlabel), entity_extract_ids($entity_type, $entity)));
+  return drupal_map_assoc(array_merge(array($entity->ftlabel), array($entity->id(), $entity->getRevisionId(), $entity->bundle())));
 }
diff --git a/core/modules/field/tests/modules/field_test/field_test.entity.inc b/core/modules/field/tests/modules/field_test/field_test.entity.inc
index ae5cdf3..e6a47af 100644
--- a/core/modules/field/tests/modules/field_test/field_test.entity.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.entity.inc
@@ -24,6 +24,8 @@ function field_test_entity_info() {
   return array(
     'test_entity' => array(
       'name' => t('Test Entity'),
+      'entity class' => 'Drupal\field_test\TestEntity',
+      'controller class' => 'Drupal\field_test\TestEntityController',
       'fieldable' => TRUE,
       'field cache' => FALSE,
       'base table' => 'test_entity',
@@ -39,6 +41,8 @@ function field_test_entity_info() {
     // This entity type doesn't get form handling for now...
     'test_cacheable_entity' => array(
       'name' => t('Test Entity, cacheable'),
+      'entity class' => 'Drupal\field_test\TestEntity',
+      'controller class' => 'Drupal\field_test\TestEntityController',
       'fieldable' => TRUE,
       'field cache' => TRUE,
       'entity keys' => array(
@@ -50,6 +54,8 @@ function field_test_entity_info() {
       'view modes' => $test_entity_modes,
     ),
     'test_entity_bundle_key' => array(
+      'entity class' => 'Drupal\field_test\TestEntity',
+      'controller class' => 'Drupal\field_test\TestEntityController',
       'name' => t('Test Entity with a bundle key.'),
       'base table' => 'test_entity_bundle_key',
       'fieldable' => TRUE,
@@ -64,6 +70,7 @@ function field_test_entity_info() {
     // In this case, the bundle key is not stored in the database.
     'test_entity_bundle' => array(
       'name' => t('Test Entity with a specified bundle.'),
+      'entity class' => 'Drupal\field_test\TestEntity',
       'base table' => 'test_entity_bundle',
       'fieldable' => TRUE,
       'controller class' => 'Drupal\field_test\TestEntityBundleController',
@@ -78,6 +85,8 @@ function field_test_entity_info() {
     // @see EntityPropertiesTestCase::testEntityLabel()
     'test_entity_no_label' => array(
       'name' => t('Test entity without label'),
+      'entity class' => 'Drupal\field_test\TestEntity',
+      'controller class' => 'Drupal\field_test\TestEntityController',
       'fieldable' => TRUE,
       'field cache' => FALSE,
       'base table' => 'test_entity',
@@ -92,6 +101,8 @@ function field_test_entity_info() {
     ),
     'test_entity_label' => array(
       'name' => t('Test entity label'),
+      'entity class' => 'Drupal\field_test\TestEntity',
+      'controller class' => 'Drupal\field_test\TestEntityController',
       'fieldable' => TRUE,
       'field cache' => FALSE,
       'base table' => 'test_entity',
@@ -107,6 +118,7 @@ function field_test_entity_info() {
     ),
     'test_entity_label_callback' => array(
       'name' => t('Test entity label callback'),
+      'entity class' => 'Drupal\field_test\TestEntity',
       'fieldable' => TRUE,
       'field cache' => FALSE,
       'base table' => 'test_entity',
@@ -239,82 +251,27 @@ function field_test_create_entity($id = 1, $vid = 1, $bundle = 'test_bundle', $l
  *   The loaded entity.
  */
 function field_test_entity_test_load($ftid, $ftvid = NULL) {
-  // Load basic strucure.
-  $query = db_select('test_entity', 'fte', array())
-    ->condition('fte.ftid', $ftid);
-
-  if ($ftvid) {
-    $query->join('test_entity_revision', 'fter', 'fte.ftid = fter.ftid');
-    $query->addField('fte', 'ftid');
-    $query->addField('fte', 'fttype');
-    $query->addField('fter', 'ftvid');
-    $query->condition('fter.ftvid', $ftvid);
-  }
-  else {
-    $query->fields('fte');
-  }
-
-  $entities = $query->execute()->fetchAllAssoc('ftid');
-
-  // Attach fields.
-  if ($ftvid) {
-    field_attach_load_revision('test_entity', $entities);
-  }
-  else {
-    field_attach_load('test_entity', $entities);
-  }
-
-  return $entities[$ftid];
+  $ids = (isset($ftid) ? array($ftid) : array());
+  $conditions = (isset($ftvid) ? array('ftvid' => $ftvid) : array());
+  $test_entity = entity_load_multiple('test_entity', $ids, $conditions);
+  return $test_entity ? reset($test_entity) : FALSE;
 }
 
 /**
  * Saves a test_entity.
  *
- * A new entity is created if $entity->ftid and $entity->is_new are both empty.
- * A new revision is created if $entity->revision is not empty.
- *
  * @param $entity
  *   The entity to save.
  */
-function field_test_entity_save(&$entity) {
-  field_attach_presave('test_entity', $entity);
-
-  if (!isset($entity->is_new)) {
-    $entity->is_new = empty($entity->ftid);
-  }
-
-  if (!$entity->is_new && !empty($entity->revision)) {
-    $entity->old_ftvid = $entity->ftvid;
-    unset($entity->ftvid);
-  }
+function field_test_entity_save(\Drupal\entity\EntityInterface $entity) {
+  $is_new = $entity->isNew();
 
-  $update_entity = TRUE;
-  if ($entity->is_new) {
-    drupal_write_record('test_entity', $entity);
-    drupal_write_record('test_entity_revision', $entity);
-    $op = 'insert';
-  }
-  else {
-    drupal_write_record('test_entity', $entity, 'ftid');
-    if (!empty($entity->revision)) {
-      drupal_write_record('test_entity_revision', $entity);
-    }
-    else {
-      drupal_write_record('test_entity_revision', $entity, 'ftvid');
-      $update_entity = FALSE;
-    }
-    $op = 'update';
-  }
-  if ($update_entity) {
-    db_update('test_entity')
-      ->fields(array('ftvid' => $entity->ftvid))
-      ->condition('ftid', $entity->ftid)
-      ->execute();
-  }
 
-  // Save fields.
-  $function = "field_attach_$op";
-  $function('test_entity', $entity);
+  $entity->save();
+  // @todo: Move this to the proper place
+  debug($entity);
+  debug(db_query('SELECT * from {test_entity}')->fetchAll());
+  debug(db_query('SELECT * from {test_entity_revision}')->fetchAll());
 }
 
 /**
diff --git a/core/modules/field/tests/modules/field_test/field_test.storage.inc b/core/modules/field/tests/modules/field_test/field_test.storage.inc
index b4b98ea..ffc919e 100644
--- a/core/modules/field/tests/modules/field_test/field_test.storage.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.storage.inc
@@ -119,7 +119,9 @@ function field_test_field_storage_load($entity_type, $entities, $age, $fields, $
 function field_test_field_storage_write($entity_type, $entity, $op, $fields) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
+  $id = $entity->id();
+  $vid = $entity->getRevisionId();
+  $bundle = $entity->bundle();
 
   foreach ($fields as $field_id) {
     $field = field_info_field_by_id($field_id);
@@ -187,11 +189,9 @@ function field_test_field_storage_write($entity_type, $entity, $op, $fields) {
  * Implements hook_field_storage_delete().
  */
 function field_test_field_storage_delete($entity_type, $entity, $fields) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Note: reusing field_test_storage_purge(), like field_sql_storage.module
   // does, is highly inefficient in our case...
-  foreach (field_info_instances($bundle) as $instance) {
+  foreach (field_info_instances($entity->bundle()) as $instance) {
     if (isset($fields[$instance['field_id']])) {
       $field = field_info_field_by_id($instance['field_id']);
       field_test_field_storage_purge($entity_type, $entity, $field, $instance);
@@ -205,12 +205,10 @@ function field_test_field_storage_delete($entity_type, $entity, $fields) {
 function field_test_field_storage_purge($entity_type, $entity, $field, $instance) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   $field_data = &$data[$field['id']];
   foreach (array('current', 'revisions') as $sub_table) {
     foreach ($field_data[$sub_table] as $key => $row) {
-      if ($row->type == $entity_type && $row->entity_id == $id) {
+      if ($row->type == $entity_type && $row->entity_id == $entity->id()) {
         unset($field_data[$sub_table][$key]);
       }
     }
@@ -225,12 +223,11 @@ function field_test_field_storage_purge($entity_type, $entity, $field, $instance
 function field_test_field_storage_delete_revision($entity_type, $entity, $fields) {
   $data = _field_test_storage_data();
 
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   foreach ($fields as $field_id) {
     $field_data = &$data[$field_id];
     foreach (array('current', 'revisions') as $sub_table) {
       foreach ($field_data[$sub_table] as $key => $row) {
-        if ($row->type == $entity_type && $row->entity_id == $id && $row->revision_id == $vid) {
+        if ($row->type == $entity_type && $row->entity_id == $entity->id() && $row->revision_id == $entity->getRevisionId()) {
           unset($field_data[$sub_table][$key]);
         }
       }
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
new file mode 100644
index 0000000..fb1335b
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntity.php
@@ -0,0 +1,59 @@
+<?php
+
+/*
+ * @file
+ * Definition of Drupal\field_test\TestEntity.
+ */
+
+namespace Drupal\field_test;
+
+use Drupal\entity\Entity;
+
+/**
+ * Test entity class.
+ */
+class TestEntity extends Entity {
+
+  /**
+   * Primary key.
+   *
+   * @var integer
+   */
+  public $ftid;
+
+  /**
+   * Revision key.
+   *
+   * @var integer
+   */
+  public $ftvid;
+
+  /**
+   * Bundle
+   *
+   * @var string
+   */
+  public $fttype;
+
+  /**
+   * Overrides Drupal\entity\Entity::id().
+   */
+  public function id() {
+    return $this->ftid;
+  }
+
+  /**
+   * Overrides Drupal\entity\Entity::getRevisionId().
+   */
+  public function getRevisionId() {
+    return $this->ftvid;
+  }
+
+  /**
+   * Overrides Drupal\entity\Entity::bundle().
+   */
+  public function bundle() {
+    return !empty($this->fttype) ? $this->fttype : $this->entityType();
+  }
+}
+
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
new file mode 100644
index 0000000..54ab4f1
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/TestEntityController.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_test\TestEntityController.
+ */
+
+namespace Drupal\field_test;
+
+use Drupal\entity\DatabaseStorageController;
+use Drupal\entity\EntityInterface;
+
+/**
+ * Controller class for the test_entity_bundle entity type.
+ */
+class TestEntityController extends DatabaseStorageController {
+
+  public function preSave(EntityInterface $entity) {
+    if (!$entity->isNew() && !empty($entity->revision)) {
+      $entity->old_ftvid = $entity->ftvid;
+      $entity->ftvid = NULL;
+    }
+  }
+
+  public function postSave(EntityInterface $entity, $update) {
+    if ($entity->entityType() == 'test_entity') {
+      $update_entity = TRUE;
+      if (!$update || !empty($entity->revision)) {
+        drupal_write_record('test_entity_revision', $entity);
+      }
+      else {
+        drupal_write_record('test_entity_revision', $entity, 'ftvid');
+        $update_entity = FALSE;
+      }
+
+      if ($update_entity) {
+        db_update('test_entity')
+          ->fields(array('ftvid' => $entity->ftvid))
+          ->condition('ftid', $entity->ftid)
+          ->execute();
+      }
+    }
+  }
+
+  public function create(array $values) {
+    $entity = parent::create($values);
+    $entity->enforceIsNew();
+    return $entity;
+  }
+}
diff --git a/core/modules/file/file.field.inc b/core/modules/file/file.field.inc
index b331e4b..70314db 100644
--- a/core/modules/file/file.field.inc
+++ b/core/modules/file/file.field.inc
@@ -217,11 +217,9 @@ function file_field_prepare_view($entity_type, $entities, $field, $instances, $l
  * Implements hook_field_insert().
  */
 function file_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Add a new usage of each uploaded file.
   foreach ($items as $item) {
-    file_usage_add(file_load($item['fid']), 'file', $entity_type, $id);
+    file_usage_add(file_load($item['fid']), 'file', $entity_type, $entity->id());
   }
 }
 
@@ -231,13 +229,11 @@ function file_field_insert($entity_type, $entity, $field, $instance, $langcode,
  * Checks for files that have been removed from the object.
  */
 function file_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // On new revisions, all files are considered to be a new usage and no
   // deletion of previous file usages are necessary.
   if (!empty($entity->revision)) {
     foreach ($items as $item) {
-      file_usage_add(file_load($item['fid']), 'file', $entity_type, $id);
+      file_usage_add(file_load($item['fid']), 'file', $entity_type, $entity->id());
     }
     return;
   }
@@ -256,7 +252,7 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode,
       $original_fids[] = $original_item['fid'];
       if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
         // Decrement the file usage count by 1.
-        file_usage_delete(file_load($original_item['fid']), 'file', $entity_type, $id);
+        file_usage_delete(file_load($original_item['fid']), 'file', $entity_type, $entity->id());
       }
     }
   }
@@ -264,7 +260,7 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode,
   // Add new usage entries for newly added files.
   foreach ($items as $item) {
     if (!in_array($item['fid'], $original_fids)) {
-      file_usage_add(file_load($item['fid']), 'file', $entity_type, $id);
+      file_usage_add(file_load($item['fid']), 'file', $entity_type, $entity->id());
     }
   }
 }
@@ -273,11 +269,9 @@ function file_field_update($entity_type, $entity, $field, $instance, $langcode,
  * Implements hook_field_delete().
  */
 function file_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
-
   // Delete all file usages within this entity.
   foreach ($items as $delta => $item) {
-    file_usage_delete(file_load($item['fid']), 'file', $entity_type, $id, 0);
+    file_usage_delete(file_load($item['fid']), 'file', $entity_type, $entity->id(), 0);
   }
 }
 
@@ -285,10 +279,9 @@ function file_field_delete($entity_type, $entity, $field, $instance, $langcode,
  * Implements hook_field_delete_revision().
  */
 function file_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
-  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
   foreach ($items as $delta => $item) {
     // Decrement the file usage count by 1.
-    file_usage_delete(file_load($item['fid']), 'file', $entity_type, $id);
+    file_usage_delete(file_load($item['fid']), 'file', $entity_type, $entity->id());
   }
 }
 
diff --git a/core/modules/file/tests/file_module_test.module b/core/modules/file/tests/file_module_test.module
index 490ef42..216b10f 100644
--- a/core/modules/file/tests/file_module_test.module
+++ b/core/modules/file/tests/file_module_test.module
@@ -73,8 +73,7 @@ function file_module_test_form_submit($form, &$form_state) {
  * Implements hook_file_download_access().
  */
 function file_module_test_file_download_access($field, $entity_type, $entity) {
-  list(,, $bundle) = entity_extract_ids($entity_type, $entity);
-  $instance = field_info_instance($entity_type, $field['field_name'], $bundle);
+  $instance = field_info_instance($entity_type, $field['field_name'], $entity->bundle());
   // Allow the file to be downloaded only if the given arguments are correct.
   // If any are wrong, $instance will be NULL.
   if (empty($instance)) {
diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module
index e3f76d1..f829e47 100644
--- a/core/modules/rdf/rdf.module
+++ b/core/modules/rdf/rdf.module
@@ -395,8 +395,7 @@ function rdf_entity_info_alter(&$entity_info) {
 function rdf_entity_load($entities, $type) {
   foreach ($entities as $entity) {
     // Extracts the bundle of the entity being loaded.
-    list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
-    $entity->rdf_mapping = rdf_mapping_load($type, $bundle);
+    $entity->rdf_mapping = rdf_mapping_load($type, $entity->bundle());
   }
 }
 
