Solución:
Con Entity Framework 6.1, ahora puede hacer esto:
[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }
[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }
El segundo parámetro del atributo es donde puede especificar el orden de las columnas en el índice.
Más información: MSDN
Encontré tres formas de resolver el problema.
Índices únicos en EntityFramework Core:
Primer enfoque:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>()
.HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}
El segundo enfoque para crear restricciones únicas con EF Core mediante el uso de claves alternativas.
Ejemplos de
Una columna:
modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");
Varias columnas:
modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");
EF 6 y menos:
Primer enfoque:
dbContext.Database.ExecuteSqlCommand(string.Format(
@"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})",
"Entitys", "FirstColumn, SecondColumn"));
Este enfoque es muy rápido y útil, pero el problema principal es que Entity Framework no sabe nada sobre esos cambios.
Segundo enfoque:
Lo encontré en esta publicación pero no lo probé solo.
CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
true, "IX_Entitys");
El problema de este enfoque es el siguiente: necesita DbMigration, entonces, ¿qué haces si no lo tienes?
Tercer enfoque:
Creo que este es el mejor, pero requiere algo de tiempo para hacerlo. Solo le mostraré la idea detrás de esto: en este enlace http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a puede encontrar el código para la anotación de datos clave única:
[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
El problema de este enfoque; ¿Cómo puedo combinarlos? Tengo una idea para extender esta implementación de Microsoft, por ejemplo:
[UniqueKey, 1] // Unique Key
public int FirstColumn { get; set;}
[UniqueKey ,1] // Unique Key
public int SecondColumn { get; set;}
Más adelante, en IDatabaseInitializer, como se describe en el ejemplo de Microsoft, puede combinar las claves de acuerdo con el entero dado. Sin embargo, debe tenerse en cuenta una cosa: si la propiedad única es de tipo cadena, entonces debe establecer MaxLength.
Si está usando Code-First, puede implementar una extensión personalizada 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);
}
}
Entonces úsalo así:
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);
Lo que resultará en esta migración:
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");
}
Y eventualmente terminará en la base de datos como:
CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
[Email] ASC,
[ApplicationId] ASC
)