web-dev-qa-db-fra.com

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

J'essaie de créer une entité EF avec Code First et une EntityTypeConfiguration en utilisant une API fluide. La création de clés primaires est facile, mais pas avec une contrainte unique. Je voyais d'anciens articles suggérant l'exécution de commandes SQL natives pour cela, mais cela semble aller à l'encontre du but recherché. est-ce possible avec EF6?

176
kob490

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

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

Exemple

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

À partir de à partir de EF6.1 , vous pouvez utiliser IndexAnnotation() pour ajouter des index de migration dans votre API courante.

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, ajouter un index sur la propriété User.FirstName

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.FirstName et 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 }));
261
Yorro

En complément de la réponse de Yorro, vous pouvez également utiliser des attributs.

Exemple pour int combinaison de clé 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'attribut 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 existe un problème de séparation de domaine/modèle de stockage, l'utilisation de 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 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; }
    }
}
133
coni2k

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

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

17
Bartho Bernsmann

La réponse de @ coni2k est correcte, mais vous devez ajouter un attribut [StringLength] 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; }
16
Arijoon

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

10
Kenneth
0
Shimmy