web-dev-qa-db-fra.com

Création d'un index unique avec une API fluide Entity Framework 6.1

J'ai une colonne "Nom" qui doit être unqiue. Aucune clé étrangère ou quelque chose comme ça.

EF 6.1 prend enfin en charge la création de tels index via Annotations. Cela a déjà été discuté sur SO. Mais il semble que cela ne peut se faire que par des annotations dans les classes. Comment est-ce que je fais cela en utilisant seulement l'API Fluent?

Quelque chose comme ça:

public class PersonConfiguration : EntityTypeConfiguration<Person>
{
    public PersonConfiguration()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        //not possible?
        Index(p => p.Name).IsUnique();  //???
    }
}
38
John

NOTE: Pertinent pour EF 6

Vous pouvez utiliser IndexAttribute comme mentionné mais avec Fluent API au lieu de DataAnnotations qui fera l'affaire:

modelBuilder 
    .Entity<Person>() 
    .Property(t => t.Name) 
    .HasColumnAnnotation( 
        "Index",  
        new IndexAnnotation(new IndexAttribute("IX_Name") { IsUnique = true }));

Malheureusement, il n'y a pas d'autre moyen de créer des index uniques en utilisant Fluent API. Il existe un problème en suspens concernant cette fonctionnalité: Contraintes uniques (index uniques)


UPDATE: Entity Framework Core

Dans la dernière version d'EF Core, vous pouvez compter sur Fluent API pour spécifier des index sans astuces supplémentaires.
HasIndex permet de le définir:

modelBuilder 
    .Entity<Person>()
    .HasIndex(x => x.Name);

Par conséquent, il reprend l'objet IndexBuilder, vous pouvez l'utiliser pour d'autres configurations d'index (par exemple, l'unicité):

modelBuilder 
    .Entity<Person>()
    .HasIndex(x => x.Name)
    .IsUnique();
58
Anatolii Gabuza

Basé sur la réponse d'Anatolii, voici une méthode d'extension pour définir plus facilement des index uniques:

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

Générera une migration telle que:

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" });
    }
}
36
Geir Sagberg

Cette réponse ne contient que des informations supplémentaires car il existe déjà d'excellentes réponses.

Si vous voulez avoir plusieurs champs uniques dans votre index, vous pouvez le réaliser en ajoutant la propriété Order. Vous devez également vous assurer que vous utilisez le même nom d'index dans toutes vos propriétés (voir uniqueIndex ci-dessous).

string uniqueIndex = "UN_TableName";

this.Property(t => t.PropertyOne)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(
            new IndexAttribute(uniqueIndex)
            {
                IsUnique = true,
                Order = 1
            }
        )
    );

this.Property(t => t.PropertyTwo)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(
            new IndexAttribute(uniqueIndex)
            {
                IsUnique = true,
                Order = 2
            }

        )
    );

Supposons maintenant que vous souhaitiez également un index sur PropertyTwo.

La méthode mal consiste à démarrer une ligne nouvelle _ avec this.Property(t => t.PropertyTwo), comme ANNULER votre index unique.

Tous vos index (index et unique) doivent être définis en même temps. Ce serait la bonne façon de le faire:

this.Property(t => t.PropertyTwo)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new[] 
            {
                new IndexAttribute(), // This is the index
                new IndexAttribute(uniqueIndex) // This is the Unique made earlier
                {
                    IsUnique = true,
                    Order = 2
                }
            }
        )
    );

Enfin, si vous souhaitez également modifier l'ordre de tri de votre index/unique, vous pouvez voir: 

Comment ajouter un index sur plusieurs colonnes avec le tri ASC/DESC à l'aide de l'API Fluent? .

28
Maxime

Dans la nouvelle version de EF, 6.2, vous pouvez utiliser la méthode HasIndex:

 modelBuilder.Entity<User>().HasIndex(user => user.Email).IsUnique(true);
 modelBuilder.Entity<User>().HasIndex(user => user.UserName).IsUnique(true);

Mise à jour pour EF Core:

 modelBuilder.Entity<User>()
        .HasIndex(b => b.Email)
        .IsUnique();

 modelBuilder.Entity<User>()
        .HasIndex(b => b.UserName)
        .IsUnique();

Microsoft Docs - Index de base EF

8
Soheil Alizadeh

Vous pouvez créer un index unique dans votre fichier de migration. 

Dans le gestionnaire de paquets:

  1. Activer les migrations
  2. Add-Migration InitialMigration

Cela créera un nouveau fichier horodaté dans votre dossier Migrations. La classe aura une méthode Up and Down que vous pouvez utiliser pour créer l'index:

public override void Up()
{
    // ... 
    CreateIndex("tableName", "columnName", unique: true, name: "myIndex");
}

public override void Down()
{
    // ...
    DropIndex("tableName", "myIndex");
}

Pour exécuter la migration, tapez Update-Database dans le gestionnaire de packages.

Vous pouvez également ajouter l'index dans le cadre de la migration qui crée la table:

CreateTable(
    "dbo.Products",
    c => new
        {
            ProductId = c.Int(nullable: false, identity: true),
            ProductName = c.String(nullable: false, maxLength: 256),
        })
    .Index(c => c.ProductName, name: "IX_Products_ProductName", unique: true)
    .PrimaryKey(t => t.ProductId);
4
Dismissile

Pour moi, en utilisant MVC 5 et EF 6, le point clé était de spécifier une longueur sur la propriété de chaîne où je voulais placer l'index. 

protected override void OnModelCreating(DbModelBuilder modelBuilder){
    //More stuff
    modelBuilder.Entity<Device>().Property(c => c.Name).HasColumnType("nvarchar").HasMaxLength(50);
    modelBuilder.Entity<Device>().HasIndex(c => c.Name).IsUnique(true); 
}
0
Oscar