Unique Key constraints for multiple columns in Entity Framework
Solution 1:
With Entity Framework 6.1, you can now do this:
[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }
[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }
The second parameter in the attribute is where you can specify the order of the columns in the index.
More information: MSDN
Solution 2:
I found three ways to solve the problem.
Unique indexes in EntityFramework Core:
First approach:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}
The second approach to create Unique Constraints with EF Core by using Alternate Keys.
Examples
One column:
modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");
Multiple columns:
modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");
EF 6 and below:
First approach:
dbContext.Database.ExecuteSqlCommand(string.Format(
@"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})",
"Entitys", "FirstColumn, SecondColumn"));
This approach is very fast and useful but the main problem is that Entity Framework doesn't know anything about those changes!
Second approach:
I found it in this post but I did not tried by myself.
CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
true, "IX_Entitys");
The problem of this approach is the following: It needs DbMigration so what do you do if you don't have it?
Third approach:
I think this is the best one but it requires some time to do it. I will just show you the idea behind it:
In this link http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a
you can find the code for unique key data annotation:
[UniqueKey] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey] // Unique Key
public int SecondColumn { get; set;}
// The problem hier
1, 1 = OK
1 ,2 = NO OK 1 IS UNIQUE
The problem for this approach; How can I combine them? I have an idea to extend this Microsoft implementation for example:
[UniqueKey, 1] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey ,1] // Unique Key
public int SecondColumn { get; set;}
Later in the IDatabaseInitializer as described in the Microsoft example you can combine the keys according to the given integer. One thing has to be noted though: If the unique property is of type string then you have to set the MaxLength.
Solution 3:
If you're using Code-First, you can implement a custom extension HasUniqueIndexAnnotation
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Infrastructure.Annotations;
using System.Data.Entity.ModelConfiguration.Configuration;
internal static class TypeConfigurationExtensions
{
public static PrimitivePropertyConfiguration HasUniqueIndexAnnotation(
this PrimitivePropertyConfiguration property,
string indexName,
int columnOrder)
{
var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = true };
var indexAnnotation = new IndexAnnotation(indexAttribute);
return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
}
}
Then use it like so:
this.Property(t => t.Email)
.HasColumnName("Email")
.HasMaxLength(250)
.IsRequired()
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 0);
this.Property(t => t.ApplicationId)
.HasColumnName("ApplicationId")
.HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 1);
Which will result in this migration:
public override void Up()
{
CreateIndex("dbo.User", new[] { "Email", "ApplicationId" }, unique: true, name: "UQ_User_EmailPerApplication");
}
public override void Down()
{
DropIndex("dbo.User", "UQ_User_EmailPerApplication");
}
And eventually end up in database as:
CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
[Email] ASC,
[ApplicationId] ASC
)
Solution 4:
The answer from niaher stating that to use the fluent API you need a custom extension may have been correct at the time of writing. You can now (EF core 2.1) use the fluent API as follows:
modelBuilder.Entity<ClassName>()
.HasIndex(a => new { a.Column1, a.Column2}).IsUnique();
Solution 5:
You need to define a composite key.
With data annotations it looks like this:
public class Entity
{
public string EntityId { get; set;}
[Key]
[Column(Order=0)]
public int FirstColumn { get; set;}
[Key]
[Column(Order=1)]
public int SecondColumn { get; set;}
}
You can also do this with modelBuilder when overriding OnModelCreating by specifying:
modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });