diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php
new file mode 100644
index 0000000..e4c3dee
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldConstraint.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint.
+ */
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+
+/**
+ * Checks if an entity field has a unique value.
+ *
+ * @Plugin(
+ *   id = "UniqueField",
+ *   label = @Translation("Unique field constraint", context = "Validation"),
+ * )
+ */
+class UniqueFieldConstraint extends Constraint {
+
+  public $message = 'The %entity_type_id with the %field_name %value already exists.';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validatedBy() {
+    return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
index d6c3910..76cfd20 100644
--- a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/UniqueFieldValueValidator.php
@@ -37,7 +37,11 @@ public function validate($items, Constraint $constraint) {
       ->execute();
 
     if ($value_taken) {
-      $this->context->addViolation($constraint->message, array("%value" => $item->value));
+      $this->context->addViolation($constraint->message, [
+        '%value' => $item->value,
+        '%entity_type_id' => $entity_type_id,
+        '%field_name' => $field_name,
+      ]);
     }
   }
 }
diff --git a/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedTitleConstraint.php b/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedTitleConstraint.php
index 47d16ac..774d4e3 100644
--- a/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedTitleConstraint.php
+++ b/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedTitleConstraint.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\aggregator\Plugin\Validation\Constraint;
 
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
 
 /**
  * Supports validating feed titles.
@@ -17,15 +17,8 @@
  *   label = @Translation("Feed title", context = "Validation")
  * )
  */
-class FeedTitleConstraint extends Constraint {
+class FeedTitleConstraint extends UniqueFieldConstraint {
 
   public $message = 'A feed named %value already exists. Enter a unique title.';
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
-  }
-
 }
diff --git a/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedUrlConstraint.php b/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedUrlConstraint.php
index 0e2ce1d..ef2fec5 100644
--- a/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedUrlConstraint.php
+++ b/core/modules/aggregator/src/Plugin/Validation/Constraint/FeedUrlConstraint.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\aggregator\Plugin\Validation\Constraint;
 
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
 
 /**
  * Supports validating feed URLs.
@@ -17,15 +17,8 @@
  *   label = @Translation("Feed URL", context = "Validation")
  * )
  */
-class FeedUrlConstraint extends Constraint {
+class FeedUrlConstraint extends UniqueFieldConstraint {
 
   public $message = 'A feed with this URL %value already exists. Enter a unique URL.';
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
-  }
-
 }
diff --git a/core/modules/block_content/src/BlockContentForm.php b/core/modules/block_content/src/BlockContentForm.php
index 379e576..088c4d7 100644
--- a/core/modules/block_content/src/BlockContentForm.php
+++ b/core/modules/block_content/src/BlockContentForm.php
@@ -220,18 +220,4 @@ public function save(array $form, FormStateInterface $form_state) {
     }
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validateForm(array &$form, FormStateInterface $form_state) {
-    if ($this->entity->isNew()) {
-      $exists = $this->blockContentStorage->loadByProperties(array('info' => $form_state->getValue(['info', 0, 'value'])));
-      if (!empty($exists)) {
-        $form_state->setErrorByName('info', $this->t('A block with description %name already exists.', array(
-          '%name' => $form_state->getValue(array('info', 0, 'value')),
-        )));
-      }
-    }
-  }
-
 }
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
index 046640c..f82c2d5 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -187,7 +187,8 @@ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
         'type' => 'string_textfield',
         'weight' => -5,
       ))
-      ->setDisplayConfigurable('form', TRUE);
+      ->setDisplayConfigurable('form', TRUE)
+      ->addConstraint('BlockContentDescription', []);
 
     $fields['type'] = BaseFieldDefinition::create('entity_reference')
       ->setLabel(t('Block type'))
diff --git a/core/modules/block_content/src/Plugin/Validation/Constraint/BlockContentDescriptionConstraint.php b/core/modules/block_content/src/Plugin/Validation/Constraint/BlockContentDescriptionConstraint.php
new file mode 100644
index 0000000..322bda6
--- /dev/null
+++ b/core/modules/block_content/src/Plugin/Validation/Constraint/BlockContentDescriptionConstraint.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Plugin\Validation\Constraint\BlockContentDescriptionConstraint.
+ */
+
+namespace Drupal\block_content\Plugin\Validation\Constraint;
+
+use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
+
+/**
+ * Supports validating uniqueness of block descriptions.
+ *
+ * @Plugin(
+ *   id = "BlockContentDescription",
+ *   label = @Translation("Block description unique", context = "Validation")
+ * )
+ */
+class BlockContentDescriptionConstraint extends UniqueFieldConstraint {
+
+  public $message = 'A block with description %value already exists.';
+
+}
diff --git a/core/modules/block_content/src/Tests/BlockContentCreationTest.php b/core/modules/block_content/src/Tests/BlockContentCreationTest.php
index 6bf250c..d2bef6e 100644
--- a/core/modules/block_content/src/Tests/BlockContentCreationTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCreationTest.php
@@ -124,8 +124,8 @@ public function testBlockContentCreationMultipleViewModes() {
     $this->drupalPostForm('block/add/basic', $edit, t('Save'));
 
     // Check that the Basic block has been created.
-    $this->assertRaw(format_string('A block with description %name already exists.', array(
-      '%name' => $edit['info[0][value]']
+    $this->assertRaw(format_string('A block with description %value already exists.', array(
+      '%value' => $edit['info[0][value]']
     )));
     $this->assertResponse(200);
   }
diff --git a/core/modules/block_content/src/Tests/BlockContentTestBase.php b/core/modules/block_content/src/Tests/BlockContentTestBase.php
index 82139c0..9cff6c0 100644
--- a/core/modules/block_content/src/Tests/BlockContentTestBase.php
+++ b/core/modules/block_content/src/Tests/BlockContentTestBase.php
@@ -70,17 +70,20 @@ protected function setUp() {
    *   Defaults to FALSE.
    * @param string $bundle
    *   (optional) Bundle name. Defaults to 'basic'.
+   * @param bool $save
+   *   (optional) Whether to save the block. Defaults to TRUE.
    *
    * @return \Drupal\block_content\Entity\BlockContent
    *   Created custom block.
    */
-  protected function createBlockContent($title = FALSE, $bundle = 'basic') {
+  protected function createBlockContent($title = FALSE, $bundle = 'basic', $save = TRUE) {
     $title = ($title ? : $this->randomMachineName());
-    if ($block_content = entity_create('block_content', array(
+    $block_content = entity_create('block_content', array(
       'info' => $title,
       'type' => $bundle,
       'langcode' => 'en'
-    ))) {
+    ));
+    if ($block_content && $save === TRUE) {
       $block_content->save();
     }
     return $block_content;
diff --git a/core/modules/block_content/src/Tests/BlockContentValidationTest.php b/core/modules/block_content/src/Tests/BlockContentValidationTest.php
new file mode 100644
index 0000000..85bf6e9
--- /dev/null
+++ b/core/modules/block_content/src/Tests/BlockContentValidationTest.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\BlockContentValidationTest.
+ */
+
+namespace Drupal\block_content\Tests;
+
+/**
+ * Tests block content validation constraints.
+ *
+ * @group block_content
+ */
+class BlockContentValidationTest extends BlockContentTestBase {
+
+  /**
+   * Tests the block content validation constraints.
+   */
+  public function testValidation() {
+    // Add a block.
+    $description = $this->randomMachineName();
+    $block = $this->createBlockContent($description, 'basic', FALSE);
+    // Validate the block.
+    $violations = $block->validate();
+    // Make sure we have no violations.
+    $this->assertEqual(count($violations), 0);
+    // Save the block.
+    $block->save();
+
+    // Add another block with the same description.
+    $block = $this->createBlockContent($description, 'basic', FALSE);
+    // Validate this block.
+    $violations = $block->validate();
+    // Make sure we have 1 violation.
+    $this->assertEqual(count($violations), 1);
+    // Make sure the violation is on the info property
+    $this->assertEqual($violations[0]->getPropertyPath(), 'info');
+    // Make sure the message is correct.
+    $this->assertEqual($violations[0]->getMessage(), format_string('A block with description %value already exists.', [
+      '%value' => $block->label(),
+    ]));
+  }
+
+}
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserMailUnique.php b/core/modules/user/src/Plugin/Validation/Constraint/UserMailUnique.php
index e541518..b72e058 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserMailUnique.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserMailUnique.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\user\Plugin\Validation\Constraint;
 
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
 
 /**
  * Checks if a user's email address is unique on the site.
@@ -17,14 +17,8 @@
  *   label = @Translation("User email unique", context = "Validation")
  * )
  */
-class UserMailUnique extends Constraint {
+class UserMailUnique extends UniqueFieldConstraint {
 
   public $message = 'The email address %value is already taken.';
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
-  }
 }
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserNameUnique.php b/core/modules/user/src/Plugin/Validation/Constraint/UserNameUnique.php
index 0a65d32..6dbe431 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserNameUnique.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserNameUnique.php
@@ -7,7 +7,7 @@
 
 namespace Drupal\user\Plugin\Validation\Constraint;
 
-use Symfony\Component\Validator\Constraint;
+use Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldConstraint;
 
 /**
  * Checks if a user name is unique on the site.
@@ -17,14 +17,8 @@
  *   label = @Translation("User name unique", context = "Validation"),
  * )
  */
-class UserNameUnique extends Constraint {
+class UserNameUnique extends UniqueFieldConstraint {
 
   public $message = 'The username %value is already taken.';
 
-  /**
-   * {@inheritdoc}
-   */
-  public function validatedBy() {
-    return '\Drupal\Core\Validation\Plugin\Validation\Constraint\UniqueFieldValueValidator';
-  }
 }
