Entity Framework Core adiciona restrição exclusiva ao código-primeiro

152

Não consigo encontrar uma maneira de adicionar uma restrição exclusiva ao meu campo usando o atributo:

public class User
{
    [Required]
    public int Id { get; set; }

    [Required]
    // [Index("IX_FirstAndSecond", 2, IsUnique = true)] not supported by core
    public string Email { get; set; }

    [Required]
    public string Password { get; set; }
}

Estou usando estes pacotes:

 "Microsoft.EntityFrameworkCore": "1.0.1",
 "Microsoft.EntityFrameworkCore.SqlServer": "1.0.1",
 "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.1",
 "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
Vadim M
fonte
Amostras de Api fluente para índices ver em learnentityframeworkcore.com/configuration/fluent-api/...
Michael Freidgeim

Respostas:

301

No núcleo EF, você não pode criar índices usando anotações de dados, mas pode fazê-lo usando a API do Fluent.

Assim dentro do seu {Db}Context.cs:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<User>()
        .HasIndex(u => u.Email)
        .IsUnique();
}

... ou se você estiver usando a sobrecarga com o buildAction:

protected override void OnModelCreating(ModelBuilder builder)
{
    builder.Entity<User>(entity => {
        entity.HasIndex(e => e.Email).IsUnique();
    });
}

Você pode ler mais sobre isso aqui: Índices

Sampath
fonte
1
Muito obrigado pela sua ajuda, é a solução certa!
Vadim M
Caso alguém chegue aqui procurando uma solução que funcione ao usar o esquema do MS Identity, sugiro definir a restrição exclusiva no campo NormalizedEmail, não o Email (da entidade AspNetUsers). O uso de maiúsculas e minúsculas diferentes pode não violar a restrição, mesmo que seja o mesmo endereço de email, dependendo da configuração de agrupamento do banco de dados.
Tom
4
Este código não está funcionando para a coluna do tipo String :( Existe alguma maneira de adicionar restrição exclusiva em coluna corda?
Johar Zaman
Ir paraHasAlternateKey
Chamika Sandamal
1
Eu tentei isso quando estava trabalhando nisso, mas agora não preciso implementar isso. Obrigado pelo seu comentário. @Sampath
Johar Zaman
70

Além disso, se você deseja criar restrições exclusivas em várias colunas, você pode simplesmente fazer isso (seguindo o link do @ Sampath )

class MyContext : DbContext
{
    public DbSet<Person> People { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .HasIndex(p => new { p.FirstName, p.LastName })
            .IsUnique(true);
    }
}

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Hossein Narimani Rad
fonte
9

Solução para EF Core

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Passport { get; set; }
}

public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public ApplicationContext()
    {
        Database.EnsureCreated();
    }
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=efbasicsappdb;Trusted_Connection=True;");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasAlternateKey(u => u.Passport);
        //or: modelBuilder.Entity<User>().HasAlternateKey(u => new { u.Passport, u.Name})
    }
}

A tabela DB ficará assim:

CREATE TABLE [dbo].[Users] (
    [Id]       INT            IDENTITY (1, 1) NOT NULL,
    [Name]     NVARCHAR (MAX) NULL,
    [Passport] NVARCHAR (450) NOT NULL,
    CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED ([Id] ASC),
    CONSTRAINT [AK_Users_Passport] UNIQUE NONCLUSTERED ([Passport] ASC)
);

Consulte os documentos principais da EF

Evgeniy Miroshnichenko
fonte
11
De acordo com os documentos da EF Core:If you just want to enforce uniqueness of a column then you want a unique index rather than an alternate key. In EF, alternate keys provide greater functionality than unique indexes because they can be used as the target of a foreign key.
bigworld12
Referência ao comentário de bigworld12: docs.microsoft.com/en-us/ef/core/modeling/…
granadaCoder 07/01
3

O OP está perguntando se é possível adicionar um atributo a uma classe de entidade para uma chave exclusiva. A resposta curta é que é possível, mas não um recurso pronto para o uso da equipe principal da EF. Se você quiser usar um Atributo para adicionar Chaves Exclusivas às suas classes de entidade do Entity Framework Core, faça o que postamos aqui

public class Company
{
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid CompanyId { get; set; }

    [Required]
    [UniqueKey(groupId: "1", order: 0)]
    [StringLength(100, MinimumLength = 1)]
    public string CompanyName { get; set; }

    [Required]
    [UniqueKey(groupId: "1", order: 1)]
    [StringLength(100, MinimumLength = 1)]
    public string CompanyLocation { get; set; }
}
Justin Tubbs
fonte
O atributo [UniqueKey] é um atributo definido pela Microsoft? ou deve-se definir ele mesmo?
Mohammed Osman
[UniqueKey] atributo é um atributo personalizado que eu desenvolvi para permitir adição de chaves únicas dentro das classes de entidade .cs (e não através do método OnModelCreating do DbContext ())
Justin Tubbs
3
Isso é ótimo. Você poderia colocar o código do atributo personalizado que você desenvolveu ?!
Mohammed Osman
2

Para alguém que está tentando todas essas soluções, mas não está trabalhando, tente esta, funcionou para mim

protected override void OnModelCreating(ModelBuilder builder)
{

    builder.Entity<User>().Property(t => t.Email).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_EmailIndex") { IsUnique = true }));

}
Shonie
fonte
2

Para usá-lo no núcleo EF via configuração do modelo

public class ApplicationCompanyConfiguration : IEntityTypeConfiguration<Company>
{
    public void Configure(EntityTypeBuilder<Company> builder)
    {
        builder.ToTable("Company"); 
        builder.HasIndex(p => p.Name).IsUnique();
    }
}
valentasm
fonte
1

Nenhum desses métodos funcionou para mim no .NET Core 2.2, mas fui capaz de adaptar algum código que eu tinha para definir uma chave primária diferente para trabalhar para esse fim.

Na instância abaixo, quero garantir que o campo OutletRef seja exclusivo:

public class ApplicationDbContext : IdentityDbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Outlet>()
                .HasIndex(o => new { o.OutletRef });
        }
    }

Isso adiciona o índice exclusivo necessário no banco de dados. O que isso não faz é fornecer a capacidade de especificar uma mensagem de erro personalizada.

Robin Wilson
fonte
0

Podemos adicionar um índice de chave exclusivo usando API fluente. O código abaixo funcionou para mim

protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

        modelBuilder.Entity<User>().Property(p => p.Email).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_EmailIndex") { IsUnique = true }));

    }
Kuldeep Singh
fonte