329 votes

Contraintes de clé unique pour plusieurs colonnes dans Entity Framework

J'utilise Entity Framework 5.0 Code First ;

public class Entity
 {
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public string EntityId { get; set;}
   public int FirstColumn  { get; set;}
   public int SecondColumn  { get; set;}
 }

Je veux faire la combinaison entre FirstColumn y SecondColumn comme unique.

Exemple :

Id  FirstColumn  SecondColumn 
1       1              1       = OK
2       2              1       = OK
3       3              3       = OK
5       3              1       = THIS OK 
4       3              3       = GRRRRR! HERE ERROR

Existe-t-il un moyen de le faire ?

417voto

chuck Points 450

Avec Entity Framework 6.1, vous pouvez désormais le faire :

[Index("IX_FirstAndSecond", 1, IsUnique = true)]
public int FirstColumn { get; set; }

[Index("IX_FirstAndSecond", 2, IsUnique = true)]
public int SecondColumn { get; set; }

Le deuxième paramètre de l'attribut permet de spécifier l'ordre des colonnes dans l'index.
Plus d'informations : MSDN

247voto

Bassam Alugili Points 1958

J'ai trouvé trois façons de résoudre le problème.

Index uniques dans EntityFramework Core :

Première approche :

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
   modelBuilder.Entity<Entity>()
   .HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique();
}

La deuxième approche pour créer des contraintes uniques avec EF Core en utilisant des clés alternatives.

Exemples

Une colonne :

modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");

Colonnes multiples :

modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns");

EF 6 et moins :


Première approche :

dbContext.Database.ExecuteSqlCommand(string.Format(
                        @"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})", 
                                 "Entitys", "FirstColumn, SecondColumn"));

Cette approche est très rapide et utile mais le problème principal est qu'Entity Framework ne sait rien de ces changements !


Deuxième approche :
Je l'ai trouvé dans ce billet mais je n'ai pas essayé par moi-même.

CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
              true, "IX_Entitys");

Le problème de cette approche est le suivant : Elle nécessite DbMigration, alors que faire si vous ne l'avez pas ?


Troisième approche :
Je pense que c'est la meilleure, mais elle demande un peu de temps. Je me contenterai de vous montrer l'idée sous-jacente : Dans ce lien http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a vous pouvez trouver le code pour l'annotation des données de la clé unique :

[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

Le problème de cette approche : comment les combiner ? J'ai l'idée d'étendre cette implémentation Microsoft par exemple :

[UniqueKey, 1] // Unique Key 
public int FirstColumn  { get; set;}
[UniqueKey ,1] // Unique Key 
public int SecondColumn  { get; set;}

Plus tard, dans l'IDatabaseInitializer, comme décrit dans l'exemple de Microsoft, vous pouvez combiner les clés en fonction de l'entier donné. Il convient toutefois de noter une chose : si la propriété unique est de type chaîne de caractères, vous devez définir la longueur maximale (MaxLength).

78voto

niaher Points 2708

Si vous utilisez la méthode Code-First vous pouvez mettre en œuvre une extension personnalisée 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);
    }
}

Utilisez-le ensuite comme suit :

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);

Ce qui aboutira à cette 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");
}

Et finissent par se retrouver dans une base de données en tant que :

CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
    [Email] ASC,
    [ApplicationId] ASC
)

43voto

GilShalit Points 1538

La réponse de niaher indiquant que pour utiliser l'API fluent, il faut une extension personnalisée était peut-être correcte au moment de la rédaction. Vous pouvez maintenant (EF core 2.1) utiliser l'API fluent comme suit :

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2}).IsUnique();

28voto

fullStackChris Points 43

Pour ceux qui recherchent une solution 2021, la version de travail de la réponse acceptée devrait maintenant ressembler à ceci :

[Index(nameof(FirstColumn), nameof(SecondColumn), IsUnique = true)]
public class Entity
 {
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public string EntityId { get; set;}
   public int FirstColumn  { get; set;}
   public int SecondColumn  { get; set;}
 }

Ainsi, l'annotation doit se trouver sur le modèle et non sur les colonnes individuelles. Notez également que l'élément nameof() syntaxe.

Cette réponse est tirée de la documentation officielle : https://learn.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X