Como criar índice no Entity Framework 6.2 com código primeiro

112

Existe uma maneira de criar um índice em uma propriedade / coluna usando o código primeiro, em vez de usar o novo IndexAttribute?

Valo
fonte
5
Um índice é um conceito de banco de dados, não um conceito de modelo de entidade. Mesmo se você pudesse especificar um índice com um atributo ou por meio da API fluente, ele não faria nada em seu aplicativo. Seria apenas uma instrução para EF usar ao criar o banco de dados. Acredito que tais instruções pertencem a migrações de código-fonte, que se preocupam inteiramente com a manipulação do esquema do banco de dados.
JC Ford

Respostas:

84

Bem, 26.10.2017 Entity Framework 6.2 foi lançado oficialmente . Inclui a possibilidade de definir índices com facilidade via API Fluent. Como ele deve ser usado já foi anunciado na versão beta do 6.2.

Agora você pode usar o HasIndex()método, seguido de IsUnique()se deveria ser um índice exclusivo.

Apenas um pequeno exemplo de comparação (antes / depois):

// before 
modelBuilder.Entity<Person>()
        .Property(e => e.Name)
        .HasColumnAnnotation(
            IndexAnnotation.AnnotationName, 
            new IndexAnnotation(new IndexAttribute { IsUnique = true }));

// after
modelBuilder.Entity<Person>()
    .HasIndex(p => p.Name)
    .IsUnique();

// multi column index
modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.Name, p.Firstname })
    .IsUnique();

Também é possível marcar o índice como agrupado com .IsClustered().


EDITAR # 1

Adicionado um exemplo de índice de várias colunas e informações adicionais sobre como marcar um índice como agrupado.


EDITAR # 2

Como informação adicional, no EF Core 2.1 é exatamente o mesmo que no EF 6.2 agora.
Aqui está o artigo do MS Doc como referência.

ChW
fonte
Isso é ótimo! Suponho que se eu tivesse um índice de várias colunas seria algo como: .HasIndex (p => novo {p.Name, p.Xyz})
Valo
Oh, desculpe, claro. Deve ser new. Eu irei consertar isso.
ChW
Você poderia mostrar como podemos escrever o mesmo código no Core 2.x?
user3417479
Pelo que eu sei, deve ser o mesmo código mostrado em "depois" e "índice de colunas múltiplas".
ChW
87

Atualmente não há "suporte de primeira classe" para a criação de um índice por meio da API fluente, mas o que você pode fazer é por meio da API fluente marcar propriedades como tendo atributos da API de anotação. Isso permitirá que você adicione o Indexatributo por meio de uma interface fluente.

Aqui estão alguns exemplos do item de trabalho do site de problemas da EF.

Crie um índice em uma única coluna:

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new IndexAttribute()));

Vários índices em uma única coluna:

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new[]
            {
                new IndexAttribute("Index1"),
                new IndexAttribute("Index2") { IsUnique = true }
            }));

Índices multicolunas:

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty1)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new IndexAttribute("MyIndex", 1)));

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty2)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new IndexAttribute("MyIndex", 2)));

O uso das técnicas acima fará com que as .CreateIndex()chamadas sejam criadas automaticamente para você em sua Up()função quando você organizar sua próxima migração (ou sejam criadas automaticamente no banco de dados se você não estiver usando migrações).

Scott Chamberlain
fonte
4
isso pode adicionar o índice na coluna, mas não removerá o índice clusterizado criado na chave primária. O hasKey cria o índice clusterizado nas chaves primárias que não são removidas por padrão. Isso deve ser removido explicitamente do arquivo de migração criado declarando clusered: false in .Primarykey(x=>x.id,clustered:false)method
Joy
8
Eu tentei o HasAnnotationmétodo e não existe nenhum método como este. mas encontrei um método cujo nome HasColumnAnnotationaceita os parâmetros que você fornece. Você precisa atualizar sua resposta ou estou errado?
Hakan Fıstık de
@HakamFostok Peguei o exemplo diretamente do site da EF. Talvez o nome tenha mudado em uma das versões ou haja um erro de digitação na versão original.
Scott Chamberlain
3
Veja na parte inferior do seguinte link de uma reunião de design no início deste ano: "Renomear HasAnnotation para HasColumnAnnotation (mais outros locais relevantes na base de código).". entityframework.codeplex.com/…
Zac Charles
36

Criei alguns métodos de extensão e os envolvi em um pacote nuget para tornar isso muito mais fácil.

Instale o EntityFramework.IndexingExtensionspacote nuget.

Então você pode fazer o seguinte:

public class MyDataContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Entity<Customer>()
        .HasIndex("IX_Customers_Name",          // Provide the index name.
            e => e.Property(x => x.LastName),   // Specify at least one column.
            e => e.Property(x => x.FirstName))  // Multiple columns as desired.

        .HasIndex("IX_Customers_EmailAddress",  // Supports fluent chaining for more indexes.
            IndexOptions.Unique,                // Supports flags for unique and clustered.
            e => e.Property(x => x.EmailAddress)); 
  }
}

O projeto e o código-fonte estão aqui . Aproveitar!

Matt Johnson-Pint
fonte
Eu realmente gosto do pacote, mas parece que o nome do índice às vezes está faltando após o scaffolding no script up. Ele só apareceu para mim ao usar 4 ou mais propriedades em meu índice. Estou trabalhando com EF 6.1.3.
Mixxiphoid de
@Mixxiphoid - você poderia registrar o problema aqui com os detalhes de suporte? Certifique-se também de ter a versão 1.0.1, pois havia um bug em 1.0.0.
Matt Johnson-Pint
Eu tenho a versão 1.0.1. Vou registrar o problema, mas não posso fazer isso no momento.
Mixxiphoid
Como adiciono a ordem das colunas participantes do índice em ordem decrescente? Por padrão .HasIndex ("IX_Customers_EmailAddress", IndexOptions.Unique, ... cria uma ordem crescente para todas as colunas participantes no índice.
GDroid
@GDroid - Infelizmente, isso não foi exposto pela IndexAttributeclasse de EF , então não posso incluí-lo em minha biblioteca.
Matt Johnson-Pint
24

Sem um nome explícito:

[Index]
public int Rating { get; set; } 

Com um nome específico:

[Index("PostRatingIndex")] 
public int Rating { get; set; }
Hugo Hilário
fonte
O índice parece estar depreciado :(
Hamed Zakery Miab
1
@HamedZakeryMiab Qual versão do Entity Framework você está usando? O índice não foi descontinuado.
Hugo Hilário
com licença, esqueci de incluir EntityFramework. está incluído nessa montagem. apenas confuso sobre o NS.
Hamed Zakery Miab
@HamedZakeryMiab sim, isso foi super confuso! Achei que fosse parte do System.DataAnnotations! É definitivamente o pacote de estrutura de entidade
AlbatrossCafe
3
a pergunta contém a seguinte afirmação instead of using the new IndexAttributevocê notou?
Hakan Fıstık
22

Do EF 6.1 em diante, o atributo [Index]é suportado.
Use [Index(IsUnique = true)]para um índice exclusivo.
Aqui está o link da Microsoft

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

    [Index(IsUnique = true)] 
    [StringLength(200)] 
    public string Username { get; set; } 

    public string DisplayName { get; set; } 
}
Darie Dorlus
fonte
2
Embora isso possa teoricamente responder à pergunta, seria preferível incluir as partes essenciais da resposta aqui e fornecer o link para referência.
Enamul Hassan
@manetsus Muito bem. Eu adicionei um trecho de código para refletir a mudança.
Darie Dorlus,
3
O comprimento da string é necessário, caso contrário, você verá uma exceção 'é de um tipo inválido para uso como coluna-chave em um índice'. Meu colega prefere a solução modelBuilder no Conntext para que você não atrapalhe sua classe de usuário, o que eu acho que é válido.
andrew pate
E quanto aos índices com várias colunas de exclusividade? É muito comum ter um índice de chave exclusiva com várias colunas ...
enorl76
1
@ enorl76 Isso também é compatível. Para cada coluna, você precisaria usar um atributo como o seguinte, [Index("IX_BlogIdAndRating", 2)] public int Rating { get; set; } [Index("IX_BlogIdAndRating", 1)] public int BlogId { get; set; } Aqui a referência da Microsoft
Darie Dorlus
8

Entity Framework 6

Property(c => c.MyColumn)
        .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_MyIndex")));

E adicione usando:

using System.Data.Entity.Infrastructure.Annotations;
using System.ComponentModel.DataAnnotations.Schema;
Guilherme Ferreira
fonte
7

Você pode usar a anotação de dados INDEX Code First Data Annotations

Emre
fonte
3
O comprimento máximo da chave é de 900 bytes para nvarchar e 450 bytes para varchar. Se você estiver usando o código primeiro, as propriedades da string serão nvarchar e você deve incluir o atributo "StringLength" como em [[StringLength (450)]
dunwan
A partir do EF 6.1, essa é a resposta correta. docs.microsoft.com/en-us/ef/ef6/modeling/code-first/…
Chris Schaller
2

Se não quiser usar atributos em seus POCO, você sempre pode fazer da seguinte forma:

context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ..."); 

Você pode executar esta instrução em sua DbInitializerclasse derivada personalizada . Não conheço nenhuma forma de API fluente de fazer isso.

Mert Akcakaya
fonte
1
Claro, Mert. No momento estou usando migrações e lá no método Up () você também pode colocar: CreateIndex ("dbo.Table1", "Column1", true, "Column1_IX") e em Down () DropIndex (("dbo.Table1 "," Column1_IX "). Eu só esperava que eles adicionassem uma API fluente também ...
Valo
1

Escrevo um método de extensão para uso em EF fluente para evitar código extra:

public static PrimitivePropertyConfiguration HasIndexAnnotation(
    this PrimitivePropertyConfiguration primitivePropertyConfiguration, 
    IndexAttribute indexAttribute = null
    )
{
    indexAttribute = indexAttribute ?? new IndexAttribute();

    return primitivePropertyConfiguration
        .HasColumnAnnotation(
            IndexAnnotation.AnnotationName, 
            new IndexAnnotation(indexAttribute)
        );
}

então use-o assim:

Property(t => t.CardNo)
    .HasIndexAnnotation();

ou assim se o índice precisar de algumas configurações:

Property(t => t.CardNo)
    .HasIndexAnnotation(new IndexAttribute("IX_Account") { IsUnique = true });
Omid-RH
fonte
1

Você pode usar um destes

// Indexes

 this.HasIndex(e => e.IsActive)
            .HasName("IX_IsActive");

ou

  this.Property(e => e.IsActive).HasColumnAnnotation(
            "Index",
            new IndexAnnotation(new IndexAttribute("IX_IsActive")));
Nayas Subramanian
fonte