Código Entity Framework primeira coluna exclusiva

114

Estou usando o Entity Framework 4.3 e o Code Fist.

Eu tenho uma aula

public class User
{
   public int UserId{get;set;}
   public string UserName{get;set;}
}

Como posso dizer ao Entity Framework que UserName deve ser exclusivo ao criar uma tabela de banco de dados? Eu preferiria usar anotações de dados em vez de arquivo de configuração, se possível.

cpoDesign
fonte

Respostas:

257

No Entity Framework 6.1+, você pode usar este atributo em seu modelo:

[Index(IsUnique=true)]

Você pode encontrá-lo neste namespace:

using System.ComponentModel.DataAnnotations.Schema;

Se o campo do seu modelo for uma string, certifique-se de que não esteja definido como nvarchar (MAX) no SQL Server ou você verá este erro com Entity Framework Code First:

A coluna 'x' na tabela 'dbo.y' é de um tipo inválido para uso como coluna-chave em um índice.

A razão é por isso:

O SQL Server retém o limite de 900 bytes para o tamanho total máximo de todas as colunas de chave de índice. "

(de: http://msdn.microsoft.com/en-us/library/ms191241.aspx )

Você pode resolver isso definindo um comprimento máximo da corda em seu modelo:

[StringLength(450)]

Seu modelo ficará assim agora no EF CF 6.1+:

public class User
{
   public int UserId{get;set;}
   [StringLength(450)]
   [Index(IsUnique=true)]
   public string UserName{get;set;}
}

Atualizar:

se você usa Fluent:

  public class UserMap : EntityTypeConfiguration<User>
  {
    public UserMap()
    {
      // ....
      Property(x => x.Name).IsRequired().HasMaxLength(450).HasColumnAnnotation("Index", new IndexAnnotation(new[] { new IndexAttribute("Index") { IsUnique = true } }));
    }
  }

e use em seu modelBuilder:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  // ...
  modelBuilder.Configurations.Add(new UserMap());
  // ...
}

Atualização 2

para EntityFrameworkCore consulte também este tópico: https://github.com/aspnet/EntityFrameworkCore/issues/1698

Atualização 3

para EF6.2 consulte: https://github.com/aspnet/EntityFramework6/issues/274

Atualização 4

ASP.NET Core Mvc 2.2 com EF Core:

[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Unique { get; set; }
JuFo
fonte
23
Obrigado por responder à postagem antiga desse cara com algumas informações atuais relevantes!
Jim Yarbro de
3
por que um índice, por que não apenas uma restrição?
John Henckel
2
Para responder à pergunta de índice versus restrição ... MSDN: "There are no significant differences between creating a UNIQUE constraint and creating a unique index that is independent of a constraint. Data validation occurs in the same manner, and the query optimizer does not differentiate between a unique index created by a constraint or manually created. However, creating a UNIQUE constraint on the column makes the objective of the index clear." msdn.microsoft.com/en-us/library/ms187019.aspx
user919426
3
Nota; no Entity Framework Core 1.0.0-preview2-final, um método de anotações de dados não está disponível - docs.efproject.net/en/latest/modeling/…
Edward Comeau
26

EF não oferece suporte a colunas exclusivas, exceto chaves. Se você estiver usando EF Migrations, você pode forçar EF a criar um índice exclusivo na UserNamecoluna (no código de migração, não por qualquer anotação), mas a exclusividade será aplicada apenas no banco de dados. Se você tentar salvar um valor duplicado, terá que capturar a exceção (violação de restrição) acionada pelo banco de dados.

Ladislav Mrnka
fonte
1
Isso é exatamente o que eu estava procurando! Eu queria saber se seria possível adicionar essas restrições usando migrações.
Alex Jorgenson
@Ladislav Mrnka: Isso também é possível por meio do método Seed?
Raheel Khan
2
Quer dar um exemplo? Obrigado!
Zero3
10

A partir do seu código, torna-se aparente que você usa POCO. Ter outra chave é desnecessário: você pode adicionar um índice conforme sugerido por juFo .
Se você usar a API Fluent em vez de atribuir a propriedade UserName, a anotação da coluna deve ser semelhante a esta:

this.Property(p => p.UserName)
    .HasColumnAnnotation("Index", new IndexAnnotation(new[] { 
        new IndexAttribute("Index") { IsUnique = true } 
    }
));

Isso criará o seguinte script SQL:

CREATE UNIQUE NONCLUSTERED INDEX [Index] ON [dbo].[Users]
(
    [UserName] ASC
)
WITH (
    PAD_INDEX = OFF, 
    STATISTICS_NORECOMPUTE = OFF, 
    SORT_IN_TEMPDB = OFF, 
    IGNORE_DUP_KEY = OFF, 
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS = ON, 
    ALLOW_PAGE_LOCKS = ON
) ON [PRIMARY]

Se você tentar inserir vários usuários com o mesmo nome de usuário, receberá uma DbUpdateException com a seguinte mensagem:

Cannot insert duplicate key row in object 'dbo.Users' with unique index 'Index'. 
The duplicate key value is (...).
The statement has been terminated.

Novamente, as anotações de coluna não estão disponíveis no Entity Framework antes da versão 6.1.

Alexander Christov
fonte
Que tal criar um índice com várias colunas usando a configuração?
FindOutIslamNow,
Um está em UserName, qual é o outro?
Alexander Christov
5

Observe que no Entity Framework 6.1 (atualmente em beta) oferecerá suporte ao IndexAttribute para anotar as propriedades do índice, o que resultará automaticamente em um índice (exclusivo) em suas Migrações do Code First.

Robba
fonte
2

No EF 6.2 usando FluentAPI , você pode usarHasIndex()

modelBuilder.Entity<User>().HasIndex(u => u.UserName).IsUnique();
Anas Alweish
fonte
-13

Solução para EF4.3

Nome de usuário exclusivo

Adicione anotação de dados sobre a coluna como:

 [Index(IsUnique = true)]
 [MaxLength(255)] // for code-first implementations
 public string UserName{get;set;}

ID único , adicionei decoração [chave] em minha coluna e pronto. Mesma solução conforme descrito aqui: https://msdn.microsoft.com/en-gb/data/jj591583.aspx

IE:

[Key]
public int UserId{get;set;}

Respostas alternativas

usando anotação de dados

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("UserId")]

usando mapeamento

  mb.Entity<User>()
            .HasKey(i => i.UserId);
        mb.User<User>()
          .Property(i => i.UserId)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
          .HasColumnName("UserId");
cpoDesign
fonte
25
Tenha cuidado com esta solução. Adicionar o Keyatributo à UserNamepropriedade fará com que a UserNamepropriedade se torne a chave primária no banco de dados. Apenas a UserIdpropriedade deve ser marcada com o Keyatributo. Esta solução fornecerá o comportamento 'correto' no lado da programação, enquanto fornece um design de banco de dados incorreto.
Alex Jorgenson,