I'm trying to build an EF Entity with Code First, and an EntityTypeConfiguration using fluent API. creating primary keys is easy but not so with a Unique Constraint. I was seeing old posts that suggested executing native SQL commands for this, but that seem to defeat the purpose. is this possible with EF6?
 
    
    - 98,240
- 88
- 296
- 433
 
    
    - 3,177
- 4
- 25
- 40
6 Answers
On EF6.2, you can use HasIndex() to add indexes for migration through fluent API.
https://github.com/aspnet/EntityFramework6/issues/274
Example
modelBuilder
    .Entity<User>()
    .HasIndex(u => u.Email)
        .IsUnique();
On EF6.1 onwards, you can use IndexAnnotation() to add indexes for migration in your fluent API.
http://msdn.microsoft.com/en-us/data/jj591617.aspx#PropertyIndex
You must add reference to:
using System.Data.Entity.Infrastructure.Annotations;
Basic Example
Here is a simple usage, adding an index on the User.FirstName property
modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
Practical Example:
Here is a more realistic example. It adds a unique index on multiple properties: User.FirstName and User.LastName, with an index name "IX_FirstNameLastName"
modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 1) { IsUnique = true }));
modelBuilder 
    .Entity<User>() 
    .Property(t => t.LastName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 2) { IsUnique = true }));
- 
                    4This is required to name column annotation as "Index"! I wrote another name and it didn't work! I spent hours before I try rename it to original "Index" as in your post and understood that this important. :( There must be a constant for it in the framework to not hard code the string. – Alexander Vasilyev Jul 10 '14 at 07:09
- 
                    useful link for the problem! http://stackoverflow.com/questions/18889218/unique-key-constraints-for-multiple-columns-in-entity-framework – Bassam Alugili Jul 15 '14 at 09:23
- 
                    10@AlexanderVasilyev The constant is defined as `IndexAnnotation.AnnotationName` – Nathan Jul 25 '14 at 21:21
- 
                    3@Nathan Thank you! That's it! The example in this post must be corrected using this constant. – Alexander Vasilyev Aug 07 '14 at 13:45
- 
                    It's also pretty easy to create your own extension methods so you can write things like `Property(x => x.FirstName).Unique("IX_FirstName")` or `new[] { Property(x => x.FirstName), Property(x => x.LastName) }.Unique("IX_FirstNameLastName")` – Phil Mar 26 '15 at 16:36
- 
                    2Can't seem to find it in EF7 - DNX – Shimmy Weitzhandler Jul 21 '15 at 02:52
- 
                    Yeah, and how do you add unique constraints on navigation properties? Lets say if one of the `User` properties has a type of `Role`? how do you make a unique constraint off that? – Tanuki Jul 28 '15 at 12:56
- 
                    That's a lot of typing just to an Unique constraint. Jeez – wickd Dec 21 '15 at 11:17
- 
                    2I believe that IsUnique need to be set to true when creating IndexAttribute in the first example. Like this: `new IndexAttribute() { IsUnique = true }`. Otherwise it creates just regular (non-unique) index. – jakubka Feb 05 '16 at 12:50
- 
                    How would you get this to work with nullable fields? – Eitan K Mar 18 '16 at 13:39
- 
                    1I have tried this and can compile however when I run Update-Database, the index is not added. Anyone knows why and how to solve? – bman Aug 02 '16 at 04:11
- 
                    @bman, check your migration scripts. I tried this on a `string` variable not realizing I needed to set a `HasMaxLength` but after I fixed this, running the migration again did not re-scaffold properly. You should have something like this: `AlterColumn("dbo.GiftCards", "CardNumber", c => c.String(nullable: false, maxLength: 50)); CreateIndex("dbo.GiftCards", "CardNumber", unique: true);` – justanotherdev Nov 24 '16 at 22:16
- 
                    @Yorro, my ConsoleApplication needed `using System.ComponentModel.DataAnnotations.Schema;`. I can't run my application right now to see if it's right, but if this is OK, better add it below the other `using` statment for newbies like me ^^' – Henryk Budzinski Mar 07 '18 at 22:05
As an addition to Yorro's answer, it can also be done by using attributes.
Sample for int type unique key combination:
[Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
public int UniqueKeyIntPart1 { get; set; }
[Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
public int UniqueKeyIntPart2 { get; set; }
If the data type is string, then MaxLength attribute must be added:
[Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
[MaxLength(50)]
public string UniqueKeyStringPart1 { get; set; }
[Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
[MaxLength(50)]
public string UniqueKeyStringPart2 { get; set; }
If there is a domain/storage model separation concern, using Metadatatype attribute/class can be an option: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f=255&MSPPError=-2147217396
A quick console app example:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace EFIndexTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new AppDbContext())
            {
                var newUser = new User { UniqueKeyIntPart1 = 1, UniqueKeyIntPart2 = 1, UniqueKeyStringPart1 = "A", UniqueKeyStringPart2 = "A" };
                context.UserSet.Add(newUser);
                context.SaveChanges();
            }
        }
    }
    [MetadataType(typeof(UserMetadata))]
    public class User
    {
        public int Id { get; set; }
        public int UniqueKeyIntPart1 { get; set; }
        public int UniqueKeyIntPart2 { get; set; }
        public string UniqueKeyStringPart1 { get; set; }
        public string UniqueKeyStringPart2 { get; set; }
    }
    public class UserMetadata
    {
        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
        public int UniqueKeyIntPart1 { get; set; }
        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
        public int UniqueKeyIntPart2 { get; set; }
        [Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
        [MaxLength(50)]
        public string UniqueKeyStringPart1 { get; set; }
        [Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
        [MaxLength(50)]
        public string UniqueKeyStringPart2 { get; set; }
    }
    public class AppDbContext : DbContext
    {
        public virtual DbSet<User> UserSet { get; set; }
    }
}
 
    
    - 2,535
- 2
- 20
- 21
- 
                    47Not if you want to keep your domain model completely separate from storage concerns. – Rickard Liljeberg Oct 26 '14 at 20:40
- 
                    4
- 
                    2Be nice if the Index attribute was separated from Entity Framework so I could include it in my Models project. I understand that it is a storage concern, but the main reason I use it is to put unique constraints on things like UserNames and Role Names. – Sam Feb 20 '15 at 02:24
- 
                    2
- 
                    1This must be out of date. It does not work at all. You just get an exception: Column 'FieldName' in table 'dbo.TableName' is of a type that is invalid for use as a key column in an index. – Cheesus Toast Oct 02 '15 at 15:19
- 
                    1You get an exception if you do not set the string length. I have edited the answer. It think it may normally be 70 characters for the whole name but it is whatever you feel like. – Cheesus Toast Oct 02 '15 at 15:42
- 
                    2The code does not work and my edit to fix it got rejected. Is this some kind of joke? – Cheesus Toast Oct 02 '15 at 18:55
- 
                    1@CheesusToast - post your edit as a separate answer then. Point out the differences and the problems it solves. – H H Oct 28 '15 at 08:28
- 
                    4This only works if you also restrict the length of the string, as SQL doesn't allow *nvarchar(max)* to be used as a key. – JMK Sep 17 '16 at 16:54
- 
                    
Here is an extension method for setting unique indexes more fluently:
public static class MappingExtensions
{
    public static PrimitivePropertyConfiguration IsUnique(this PrimitivePropertyConfiguration configuration)
    {
        return configuration.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsUnique = true }));
    }
}
Usage:
modelBuilder 
    .Entity<Person>() 
    .Property(t => t.Name)
    .IsUnique();
Will generate migration such as:
public partial class Add_unique_index : DbMigration
{
    public override void Up()
    {
        CreateIndex("dbo.Person", "Name", unique: true);
    }
    public override void Down()
    {
        DropIndex("dbo.Person", new[] { "Name" });
    }
}
Src: Creating Unique Index with Entity Framework 6.1 fluent API
 
    
    - 1
- 1
 
    
    - 2,393
- 1
- 25
- 34
@coni2k 's answer is correct however you must add [StringLength] attribute for it to work otherwise you will get an invalid key exception (Example bellow).
[StringLength(65)]
[Index("IX_FirstNameLastName", 1, IsUnique = true)]
public string FirstName { get; set; }
[StringLength(65)]
[Index("IX_FirstNameLastName", 2, IsUnique = true)]
public string LastName { get; set; }
 
    
    - 2,184
- 3
- 24
- 32
Unfortunately this is not supported in Entity Framework. It was on the roadmap for EF 6, but it got pushed back: Workitem 299: Unique Constraints (Unique Indexes)
 
    
    - 232,247
- 41
- 429
- 459
 
    
    - 28,294
- 6
- 61
- 84
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);
 
    
    - 1,183
- 14
- 19
 
    