diff --git includes/common.inc includes/common.inc
index 747c586..7884569 100644
--- includes/common.inc
+++ includes/common.inc
@@ -2680,6 +2680,8 @@ function drupal_attributes(array $attributes = array()) {
  *       escaped HTML.
  *     - 'alias' (default FALSE)
  *       Whether the given path is an alias already.
+ *     - 'url' (default FALSE)
+ *       Whether the given path has been passed to url() already.
  * @return
  *   an HTML string containing a link to the given path.
  */
@@ -2691,6 +2693,7 @@ function l($text, $path, array $options = array()) {
   $options += array(
       'attributes' => array(),
       'html' => FALSE,
+      'url' => FALSE,
     );
 
   // Append active class.
@@ -2736,7 +2739,7 @@ function l($text, $path, array $options = array()) {
   }
   // The result of url() is a plain-text URL. Because we are using it here
   // in an HTML argument context, we need to encode it properly.
-  return '<a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
+  return '<a href="' . check_plain(($options['url'] ? $path : url($path, $options))) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
 }
 
 /**
diff --git includes/entity.inc includes/entity.inc
index 26db7f5..30df923 100644
--- includes/entity.inc
+++ includes/entity.inc
@@ -232,6 +232,30 @@ class DrupalDefaultEntityController implements DrupalEntityControllerInterface {
    * See NodeController::attachLoad() for an example.
    */
   protected function attachLoad(&$queried_entities) {
+    // Add the URI of each entity if available.
+    foreach ($queried_entities as $queried_entity) {
+      if ($url_structure = entity_uri($this->entityType, $queried_entity)) {
+        // Prepare variables to be passed to url().
+        $path = $url_structure['path'];
+        $options = isset($url_structure['options']) ? $url_structure['options'] : array();
+        // Implementations of hook_url_outbound_alter() might try to load the
+        // same entity, so there is a risk of infinite recursion when calling
+        // url(), see forum.module for example. To avoid this recursion, we only
+        // call url() the first time an entity is being loaded. An empty uri is
+        // assigned on eventual subsequent recursive loads.
+        // @todo remove when http://drupal.org/node/712076 is fixed.
+        static $loaded_entities;
+        if (empty($loaded_entities[$path])) {
+          $loaded_entities[$path] = TRUE;
+          $queried_entity->uri = url($path, $options);
+          unset($loaded_entities[$path]);
+        }
+        else {
+          $queried_entity->uri = NULL;
+        }
+      }
+    }
+
     // Attach fields.
     if ($this->entityInfo['fieldable']) {
       if ($this->revisionId) {
diff --git modules/comment/comment.module modules/comment/comment.module
index b8c7cca..02f3f43 100644
--- modules/comment/comment.module
+++ modules/comment/comment.module
@@ -2154,8 +2154,9 @@ function template_preprocess_comment(&$variables) {
   $variables['new']       = !empty($comment->new) ? t('new') : '';
   $variables['picture']   = theme_get_setting('toggle_comment_user_picture') ? theme('user_picture', array('account' => $comment)) : '';
   $variables['signature'] = $comment->signature;
-  $variables['title']     = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
-  $variables['permalink'] = l('#', 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
+  $comment->uri = empty($comment->uri) ? NULL : $comment->uri;
+  $variables['title']     = l($comment->subject, $comment->uri, array('url' => TRUE));
+  $variables['permalink'] = l('#', $comment->uri, array('url' => TRUE));
 
   // Preprocess fields.
   field_attach_preprocess('comment', $comment, $variables['elements'], $variables);
diff --git modules/node/node.module modules/node/node.module
index 07ae759..1717cfd 100644
--- modules/node/node.module
+++ modules/node/node.module
@@ -1365,7 +1365,7 @@ function template_preprocess_node(&$variables) {
 
   $variables['date']      = format_date($node->created);
   $variables['name']      = theme('username', array('account' => $node));
-  $variables['node_url']  = url('node/' . $node->nid);
+  $variables['node_url']  = empty($node->uri) ? NULL : $node->uri;
   $variables['node_title'] = check_plain($node->title);
   $variables['page']      = node_is_page($node);
 
@@ -2416,7 +2416,7 @@ function node_page_default() {
 function node_page_view($node) {
   drupal_set_title($node->title);
   // Set the node path as the canonical URL to prevent duplicate content.
-  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid)), TRUE);
+  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => $node->uri), TRUE);
   // Set the non-aliased path as a default shortlink.
   drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url('node/' . $node->nid, array('alias' => TRUE))), TRUE);
   return node_show($node);
diff --git modules/rdf/rdf.module modules/rdf/rdf.module
index 3aabd5c..fbc1fa8 100644
--- modules/rdf/rdf.module
+++ modules/rdf/rdf.module
@@ -479,7 +479,7 @@ function rdf_preprocess_user_profile(&$variables) {
   $account = $variables['elements']['#account'];
   if (!empty($account->rdf_mapping['rdftype'])) {
     $variables['attributes_array']['typeof'] = $account->rdf_mapping['rdftype'];
-    $variables['attributes_array']['about'] = url('user/' . $account->uid);
+    $variables['attributes_array']['about'] = $account->uri;
   }
 }
 
@@ -549,7 +549,7 @@ function rdf_preprocess_comment(&$variables) {
     // Adds RDFa markup to the comment container. The about attribute specifies
     // the URI of the resource described within the HTML element, while the
     // typeof attribute indicates its RDF type (e.g. sioc:Post, etc.).
-    $variables['attributes_array']['about'] = url('comment/' . $comment->cid, array('fragment' => 'comment-' . $comment->cid));
+    $variables['attributes_array']['about'] = $comment->uri;
     $variables['attributes_array']['typeof'] = $comment->rdf_mapping['rdftype'];
   }
 
diff --git modules/user/user.module modules/user/user.module
index e07c91a..6835bb3 100644
--- modules/user/user.module
+++ modules/user/user.module
@@ -3360,7 +3360,7 @@ function user_register_submit($form, &$form_state) {
 
   // New administrative account without notification.
   if ($admin && !$notify) {
-    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
+    drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => $account->uri, '%name' => $account->name)));
   }
   // No e-mail verification required; log in user immediately.
   elseif (!$admin && !variable_get('user_email_verification', TRUE) && $account->status) {
@@ -3375,7 +3375,7 @@ function user_register_submit($form, &$form_state) {
     $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
     _user_mail_notify($op, $account);
     if ($notify) {
-      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
+      drupal_set_message(t('A welcome message with further instructions has been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => $account->uri, '%name' => $account->name)));
     }
     else {
       drupal_set_message(t('A welcome message with further instructions has been sent to your e-mail address.'));
