Définition d'une contrainte unique avec une API fluide?

185

J'essaie de créer une entité EF avec Code First et une EntityTypeConfigurationAPI fluide. la création de clés primaires est facile, mais pas avec une contrainte unique. Je voyais d'anciens articles suggérant d'exécuter des commandes SQL natives pour cela, mais cela semble aller à l'encontre de l'objectif. est-ce possible avec EF6?

kob490
la source

Réponses:

275

Sur EF6.2 , vous pouvez utiliser HasIndex()pour ajouter des index pour la migration via l'API fluent.

https://github.com/aspnet/EntityFramework6/issues/274

Exemple

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

À partir d' EF6.1 , vous pouvez utiliser IndexAnnotation()pour ajouter des index pour la migration dans votre API fluent.

http://msdn.microsoft.com/en-us/data/jj591617.aspx#PropertyIndex

Vous devez ajouter une référence à:

using System.Data.Entity.Infrastructure.Annotations;

Exemple de base

Voici une utilisation simple, en ajoutant un index sur la User.FirstNamepropriété

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));

Exemple pratique:

Voici un exemple plus réaliste. Il ajoute un index unique sur plusieurs propriétés: User.FirstNameet User.LastName, avec un nom d'index "IX_FirstNameLastName"

modelBuilder 
    .Entity<User>() 
    .Property(t => t.FirstName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 1) { IsUnique = true }));

modelBuilder 
    .Entity<User>() 
    .Property(t => t.LastName) 
    .IsRequired()
    .HasMaxLength(60)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(
            new IndexAttribute("IX_FirstNameLastName", 2) { IsUnique = true }));
Yorro
la source
4
Ceci est nécessaire pour nommer l'annotation de colonne comme "Index"! J'ai écrit un autre nom et ça n'a pas marché! J'ai passé des heures avant d'essayer de le renommer en "Index" d'origine comme dans votre message et j'ai compris que c'était important. :( Il doit y avoir une constante dans le cadre pour ne pas coder en dur la chaîne.
Alexander Vasilyev
10
@AlexanderVasilyev La constante est définie commeIndexAnnotation.AnnotationName
Nathan
3
@Nathan Merci! C'est tout! L'exemple de cet article doit être corrigé en utilisant cette constante.
Alexander Vasilyev
2
Je n'arrive pas à le trouver dans EF7 - DNX
Shimmy Weitzhandler
2
Je crois que IsUnique doit être défini sur true lors de la création d'IndexAttribute dans le premier exemple. Comme ceci: new IndexAttribute() { IsUnique = true }. Sinon, il crée juste un index régulier (non unique).
jakubka
134

En plus de la réponse de Yorro, cela peut également être fait en utilisant des attributs.

Exemple de intcombinaison de touches de type unique:

[Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
public int UniqueKeyIntPart1 { get; set; }

[Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
public int UniqueKeyIntPart2 { get; set; }

Si le type de données est string, l' MaxLengthattribut doit être ajouté:

[Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
[MaxLength(50)]
public string UniqueKeyStringPart1 { get; set; }

[Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
[MaxLength(50)]
public string UniqueKeyStringPart2 { get; set; }

S'il y a un problème de séparation de domaine / modèle de stockage, l'utilisation d' Metadatatypeattribut / classe peut être une option: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2147217396


Un exemple d'application de console rapide:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;

namespace EFIndexTest
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var context = new AppDbContext())
            {
                var newUser = new User { UniqueKeyIntPart1 = 1, UniqueKeyIntPart2 = 1, UniqueKeyStringPart1 = "A", UniqueKeyStringPart2 = "A" };
                context.UserSet.Add(newUser);
                context.SaveChanges();
            }
        }
    }

    [MetadataType(typeof(UserMetadata))]
    public class User
    {
        public int Id { get; set; }
        public int UniqueKeyIntPart1 { get; set; }
        public int UniqueKeyIntPart2 { get; set; }
        public string UniqueKeyStringPart1 { get; set; }
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class UserMetadata
    {
        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 1)]
        public int UniqueKeyIntPart1 { get; set; }

        [Index("IX_UniqueKeyInt", IsUnique = true, Order = 2)]
        public int UniqueKeyIntPart2 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 1)]
        [MaxLength(50)]
        public string UniqueKeyStringPart1 { get; set; }

        [Index("IX_UniqueKeyString", IsUnique = true, Order = 2)]
        [MaxLength(50)]
        public string UniqueKeyStringPart2 { get; set; }
    }

    public class AppDbContext : DbContext
    {
        public virtual DbSet<User> UserSet { get; set; }
    }
}
coni2k
la source
45
Pas si vous voulez garder votre modèle de domaine complètement séparé des problèmes de stockage.
Rickard Liljeberg
4
Vous devez également vous assurer d'avoir une référence à EntityFramework
Michael Tranchida
2
Soyez gentil si l'attribut Index était séparé de Entity Framework afin que je puisse l'inclure dans mon projet Models. Je comprends que c'est un problème de stockage, mais la principale raison pour laquelle je l'utilise est de mettre des contraintes uniques sur des éléments tels que les noms d'utilisateur et les noms de rôle.
Sam le
2
Je n'arrive pas à le trouver dans EF7 - DNX
Shimmy Weitzhandler
4
Cela ne fonctionne que si vous limitez également la longueur de la chaîne, car SQL n'autorise pas l'utilisation de nvarchar (max) comme clé.
JMK le
17

Voici une méthode d'extension pour définir plus couramment 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 des migrations telles 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" });
    }
}

Src: Création d'un index unique avec l'API Fluent Entity Framework 6.1

Bartho Bernsmann
la source
16

La réponse de @ coni2k est correcte mais vous devez ajouter un [StringLength]attribut pour que cela fonctionne sinon vous obtiendrez une exception de clé non valide (exemple ci-dessous).

[StringLength(65)]
[Index("IX_FirstNameLastName", 1, IsUnique = true)]
public string FirstName { get; set; }

[StringLength(65)]
[Index("IX_FirstNameLastName", 2, IsUnique = true)]
public string LastName { get; set; }
Arijoon
la source
0
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);
Rousonur Jaman
la source