Saltar al contenido

Restricciones de clave única para múltiples columnas en Entity Framework

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
)
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *