Definindo restrição exclusiva com API fluente?


185

Estou tentando criar uma entidade EF com o Code First e uma EntityTypeConfigurationAPI fluente usando. criar chaves primárias é fácil, mas não é assim com uma restrição exclusiva. Eu estava vendo postagens antigas que sugeriam a execução de comandos SQL nativos para isso, mas que parecem anular o objetivo. isso é possível com o EF6?

Respostas:


275

No EF6.2 , você pode usar HasIndex()para adicionar índices para migração por meio da API fluente.

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

Exemplo

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

No EF6.1 em diante, você pode usar IndexAnnotation()para adicionar índices para migração em sua API fluente.

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

Você deve adicionar referência a:

using System.Data.Entity.Infrastructure.Annotations;

Exemplo básico

Aqui está um uso simples, adicionando um índice na User.FirstNamepropriedade

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

Exemplo prático:

Aqui está um exemplo mais realista. Ele adiciona um índice exclusivo em várias propriedades: User.FirstNamee User.LastName, com um nome de índice "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
Isso é necessário para nomear a anotação da coluna como "Índice"! Eu escrevi outro nome e não funcionou! Passei horas antes de tentar renomeá-lo para "Índice" original, como na sua postagem, e entendi que isso é importante. :( Deve haver uma constante no framework para não codificar a string com
firmeza

10
@AlexanderVasilyev A constante é definida como #IndexAnnotation.AnnotationName
Nathan

3
@ Nathan Obrigado! É isso aí! O exemplo nesta postagem deve ser corrigido usando essa constante.
Alexander Vasilyev

2
Não consigo encontrá-lo no EF7 - DNX
Shimmy Weitzhandler

2
Acredito que IsUnique precisa ser definido como true ao criar IndexAttribute no primeiro exemplo. Como esta: new IndexAttribute() { IsUnique = true }. Caso contrário, ele cria apenas um índice regular (não exclusivo).
jakubka

134

Como complemento à resposta de Yorro, isso também pode ser feito usando atributos.

Amostra para intcombinação de teclas exclusiva do 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 o tipo de dados for string, o MaxLengthatributo deverá ser adicionado:

[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 houver uma preocupação de separação de modelo de domínio / armazenamento, o uso de Metadatatypeatributo / classe pode ser uma opção: https://msdn.microsoft.com/en-us/library/ff664465%28v=pandp.50%29.aspx?f= 255 & MSPPError = -2147217396


Um exemplo rápido de aplicativo 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; }
    }
}

45
Não, se você deseja manter seu modelo de domínio completamente separado das preocupações de armazenamento.
Rickard Liljeberg 26/10

4
Você também precisa ter certeza de que você tem uma referência a EntityFramework
Michael Tranchida

2
Seja legal se o atributo Index foi separado do Entity Framework para que eu pudesse incluí-lo no meu projeto Models. Entendo que é um problema de armazenamento, mas o principal motivo para usá-lo é colocar restrições exclusivas em itens como UserNames e Role Names.
Sam

2
Não consigo encontrá-lo no EF7 - DNX
Shimmy Weitzhandler

4
Isso funciona apenas se você também restringir o comprimento da cadeia, pois o SQL não permite que o nvarchar (max) seja usado como chave.
JMK

17

Aqui está um método de extensão para definir índices exclusivos com mais fluência:

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

Irá gerar migração como:

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: Criando índice exclusivo com API fluente do Entity Framework 6.1


16

A resposta de @ coni2k está correta, no entanto, você deve adicionar um [StringLength]atributo para que ele funcione, caso contrário, você receberá uma exceção de chave inválida (exemplo a seguir).

[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);
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.