189 votes

Définition d'une contrainte unique avec l'API fluente ?

J'essaye de construire une entité EF avec Code First, et une EntityTypeConfiguration La création de clés primaires est facile, mais ce n'est pas le cas avec une contrainte unique. J'ai vu d'anciens messages qui suggéraient d'exécuter des commandes SQL natives pour cela, mais cela semble aller à l'encontre du but recherché. Est-ce possible avec EF6 ?

281voto

Yorro Points 1889

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

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

Exemple

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

Sur EF6.1 à partir de cette date, vous pouvez utiliser IndexAnnotation() pour ajouter des index pour la migration dans votre API fluide.

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

Vous devez ajouter la référence à :

using System.Data.Entity.Infrastructure.Annotations;

Exemple de base

Voici une utilisation simple, l'ajout d'un index sur le fichier User.FirstName proprié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 indice unique sur plusieurs propriétés : User.FirstName y 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 }));

4 votes

Ceci est nécessaire pour nommer l'annotation de la colonne comme "Index" ! J'ai écrit un autre nom et ça n'a pas marché ! J'ai passé des heures avant d'essayer de la renommer en "Index" original comme dans votre post et j'ai compris que c'était important :( Il doit y avoir une constante pour cela dans le framework pour ne pas coder en dur la chaîne.

0 votes

Lien utile pour le problème ! stackoverflow.com/questions/18889218/

10 votes

@AlexanderVasilyev La constante est définie comme suit . IndexAnnotation.AnnotationName

135voto

coni2k Points 855

En complément de la réponse de Yorro, il est également possible d'utiliser des attributs.

Echantillon pour int une combinaison de touches 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 entonces MaxLength 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 entre le domaine et le modèle de stockage, l'utilisation de l'option Metadatatype attribut/classe peut être une option : https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f=255&MSPPError=-2147217396


Un exemple rapide d'application de console :

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

46 votes

Pas si vous voulez que votre modèle de domaine soit complètement séparé des questions de stockage.

4 votes

Vous devez également vous assurer que vous avez une référence à EntityFramework

2 votes

Ce serait bien si l'attribut Index était séparé d'Entity Framework pour 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 choses comme les noms d'utilisateur et les noms de rôle.

18voto

Bartho Bernsmann Points 1139

Voici une méthode d'extension permettant de définir des index uniques de manière plus fluide :

public static class MappingExtensions
{
    public static PrimitivePropertyConfiguration IsUnique(this PrimitivePropertyConfiguration configuration)
    {
        return configuration.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute { IsUnique = true }));
    }
}

Utilisation :

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 Entity Framework 6.1 fluent API

16voto

Arijoon Points 1025

La réponse de @coni2k est correcte mais vous devez ajouter [StringLength] pour que cela fonctionne, sinon vous obtiendrez une exception de clé invalide (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; }

10voto

Kenneth Points 13780

Malheureusement, cela n'est pas pris en charge par Entity Framework. Il était sur la feuille de route pour EF 6, mais il a été repoussé : Chantier 299 : Contraintes uniques (index uniques)

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