Impostare un vincolo univoco con API fluente?


185

Sto cercando di creare un'entità EF con Code First e EntityTypeConfigurationun'API fluente utilizzando. la creazione di chiavi primarie è semplice ma non così con un vincolo univoco. Stavo vedendo vecchi post che suggerivano di eseguire comandi SQL nativi per questo, ma che sembrano vanificare lo scopo. è possibile con EF6?

Risposte:


275

Su EF6.2 , è possibile utilizzare HasIndex()per aggiungere indici per la migrazione tramite API fluenti.

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

Esempio

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

A partire da EF6.1 , è possibile utilizzare IndexAnnotation()per aggiungere indici per la migrazione nell'API fluente.

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

Devi aggiungere un riferimento a:

using System.Data.Entity.Infrastructure.Annotations;

Esempio di base

Ecco un semplice utilizzo, aggiungendo un indice sulla User.FirstNameproprietà

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

Esempio pratico:

Ecco un esempio più realistico. Aggiunge un indice univoco su più proprietà: User.FirstNamee User.LastName, con un nome indice "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
Questo è necessario per nominare l'annotazione della colonna come "Indice"! Ho scritto un altro nome e non ha funzionato! Ho trascorso ore prima di provare a rinominarlo in "Index" originale come nel tuo post e ho capito che questo è importante. :( Deve esserci una costante per esso nel framework per non codificare la stringa.
Alexander Vasilyev

10
@AlexanderVasilyev La costante è definita comeIndexAnnotation.AnnotationName
Nathan il

3
@Nathan Grazie! Questo è tutto! L'esempio in questo post deve essere corretto usando questa costante.
Alexander Vasilyev,

2
Non riesco a trovarlo in EF7 - DNX
Shimmy Weitzhandler il

2
Credo che IsUnique debba essere impostato su true quando si crea IndexAttribute nel primo esempio. Come questo: new IndexAttribute() { IsUnique = true }. Altrimenti crea solo un indice regolare (non univoco).
Jakubka,

134

Come aggiunta alla risposta di Yorro, può anche essere fatto usando gli attributi.

Esempio per la intcombinazione di tasti univoca di tipo:

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

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

Se il tipo di dati è string, è MaxLengthnecessario aggiungere l'attributo:

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

Se esiste un problema di separazione del modello dominio / archiviazione, l'utilizzo Metadatatypedell'attributo / classe può essere un'opzione: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2.147,217396 millions


Un esempio di app per console rapida:

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

45
Non se vuoi mantenere il tuo modello di dominio completamente separato dai problemi di archiviazione.
Rickard Liljeberg,

4
Devi anche assicurarti di avere un riferimento a EntityFramework
Michael Tranchida,

2
Sii gentile se l'attributo Index fosse separato da Entity Framework in modo da poterlo includere nel mio progetto Modelli. Capisco che si tratta di un problema di archiviazione, ma il motivo principale per cui lo uso è quello di porre vincoli univoci su cose come UserNames e Role Names.
Sam,

2
Non riesco a trovarlo in EF7 - DNX
Shimmy Weitzhandler il

4
Questo funziona solo se si limita anche la lunghezza della stringa, poiché SQL non consente di utilizzare nvarchar (max) come chiave.
JMK,

17

Ecco un metodo di estensione per impostare gli indici univoci in modo più fluido:

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

Uso:

modelBuilder 
    .Entity<Person>() 
    .Property(t => t.Name)
    .IsUnique();

Genererà migrazione come:

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: creazione di un indice unico con API fluente Entity Framework 6.1


16

La risposta di @ coni2k è corretta, tuttavia è necessario aggiungere l' [StringLength]attributo affinché funzioni altrimenti si otterrà un'eccezione chiave non valida (esempio di seguito).

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


0
modelBuilder.Property(x => x.FirstName).IsUnicode().IsRequired().HasMaxLength(50);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.