130

Is there a way to create an index on a property/column using fluent configuration, instead of using the new IndexAttribute ?

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
Valo
  • 1,872
  • 2
  • 15
  • 23
  • 5
    An index is a database concept, not an entity model concept. Even if you could specify an index with an attribute or through the fluent API it wouldn't actually DO anything in your application. It'd just be an instruction for EF to use when creating the database. I believe such instructions belong in code-first migrations, which is entirely concerned with manipulating database schema. – JC Ford Mar 24 '14 at 19:53

11 Answers11

107

Well 26.10.2017 Entity Framework 6.2 was officially released. It includes a possibility to define indexes with ease via Fluent API. Ho it is to use was already announced in the beta of 6.2.

Now you can use the HasIndex() method, followed by IsUnique() if it should be an unique index.

Just a small comparison (before/after) example:

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

It is also possible to mark the index as clustered with .IsClustered().


EDIT #1

Added an example for multi column index and additional information how to mark an index as clustered.


EDIT #2

As additional information, in EF Core 2.1 it is exactly the same like in EF 6.2 now.
Here is the MS Doc artcile as reference.

ChW
  • 3,168
  • 2
  • 21
  • 34
87

Currently there is no "first class support" for creating a index via the fluent API, but what you can do is via the fluent API you can mark properties as having attributes from the Annotation API. This will allow you to add the Index attribute via a fluent interface.

Here are some examples from the work item from Issues site for EF.

Create a index on a single column:

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

Multiple indexes on a single column:

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

Multi-Column indexes:

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

Using the above techniques will cause .CreateIndex() calls to be automatically created for you in your Up() function when you scaffold your next migration (or be automatically created in the database if you are not using migrations).

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • 4
    that might add the index on the column but that wont remove the clustered index created on primary key . The hasKey creates the clustered index on primary keys which aren't by default gets removed. That has to be explicitly removed from the migration file created by stating clusered:false in `.Primarykey(x=>x.id,clustered:false)` method – Joy May 10 '14 at 16:12
  • 8
    I tried the `HasAnnotation` method and there is NO method like this. but I found a method which name `HasColumnAnnotation` which accepts the parameters which you provide. Do you need to update your answer or am I wrong? – Hakan Fıstık Nov 20 '15 at 08:48
  • @HakamFostok I took the example directly from the EF site. Perhaps the name changed in one of the versions or there is a typo in the original version. – Scott Chamberlain Mar 31 '16 at 13:43
  • 3
    See right down the bottom of the following link from a design meeting earlier this year: "Rename HasAnnotation to HasColumnAnnotation (plus other relevant places in the code base).". http://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20%E2%80%93%20January%2016%2C%202014 – Zac Charles Apr 26 '16 at 10:32
36

I've created a some extension methods and wrapped them in a nuget package to make this much easier.

Install the EntityFramework.IndexingExtensions nuget package.

Then you can do the following:

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

The project and source code are here. Enjoy!

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • I really like the package but it seems the index name is sometimes missing after scaffolding in the up script. It only appeared for me when using 4 or more properties in my index. I'm working with EF 6.1.3. – Mixxiphoid Jul 11 '15 at 10:14
  • @Mixxiphoid - would you please log the issue [here](https://github.com/mj1856/EntityFramework.IndexingExtensions/issues) with supporting details? Also be sure you have version 1.0.1, since there was [a bug](https://github.com/mj1856/EntityFramework.IndexingExtensions/issues/2) in 1.0.0. – Matt Johnson-Pint Jul 12 '15 at 01:55
  • I do have version 1.0.1. I will log the issue but cannot do so at this moment. – Mixxiphoid Jul 12 '15 at 11:42
  • How do I add index participating column order to descending? By default .HasIndex("IX_Customers_EmailAddress", IndexOptions.Unique, ... creates ascending order for all participating columns in index. – GDroid Mar 15 '17 at 21:52
  • @GDroid - Unfortunately, this isn't exposed by EF's `IndexAttribute` class, so I cannot include it in my library. – Matt Johnson-Pint Mar 15 '17 at 22:58
27

From EF 6.1 onward the attribute [Index] is supported.
Use [Index(IsUnique = true)] for unique index.
Here is the link from Microsoft

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

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

    public string DisplayName { get; set; } 
}
Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
Darie Dorlus
  • 396
  • 3
  • 8
  • 2
    Whilst this may theoretically answer the question, [it would be preferable](//meta.stackoverflow.com/q/8259) to include the essential parts of the answer here, and provide the link for reference. – Enamul Hassan May 03 '16 at 00:42
  • @manetsus Very well. I added a code snippet to reflect the change. – Darie Dorlus Oct 03 '16 at 18:37
  • 3
    The string length is needed otherwise you see a 'is of a type that is invalid for use as a key column in an index' exception. My collegue prefers the modelBuilder solution on the Conntext so your not cluttering up your User class, which I guess is valid. – andrew pate Jun 29 '17 at 09:19
  • What about indexes with multiple columns for uniqueness? Quite common to have a multi-column Unique Key index... – enorl76 Feb 03 '19 at 19:11
  • 1
    @enorl76 That is also supported. For each columns you would need to use an attribute like the following, `[Index("IX_BlogIdAndRating", 2)]` `public int Rating { get; set; }` `[Index("IX_BlogIdAndRating", 1)]` `public int BlogId { get; set; }` Here the reference from [Microsoft](https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations#multiple-column-indexes) – Darie Dorlus Feb 04 '19 at 17:16
  • @DarieDorlus yes thank you. Basically you're saying as long as IsUnique=true on at least one of the Index attributes, and on all the Index attributes that the index name matches, the columns will get added into a unique-key constraint index. This is what I ended up finding out locally. – enorl76 Feb 07 '19 at 23:17
26

Without an explicit name:

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

With a specific name:

[Index("PostRatingIndex")] 
public int Rating { get; set; }
Hugo Hilário
  • 2,848
  • 2
  • 27
  • 43
8

Entity Framework 6

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

And add using:

using System.Data.Entity.Infrastructure.Annotations;
using System.ComponentModel.DataAnnotations.Schema;
Guilherme Ferreira
  • 1,503
  • 2
  • 18
  • 31
7

You can use the INDEX data annotaion Code First Data Annotations

Emre
  • 388
  • 3
  • 10
  • 3
    Maximum key length is 900 bytes for nvarchar and 450 bytes for varchar. If you are using code first the string properties will be nvarchar and you should include the attribute "StringLength" as in [[StringLength(450)] – dunwan Mar 04 '15 at 12:03
  • As of EF 6.1, this is the correct answer. https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations?redirectedfrom=MSDN#index – Chris Schaller Oct 21 '19 at 12:47
3

If you don't want to use attributes on your POCO's, then you can always do it like the following:

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

You can execute this statement in your custom DbInitializer derived class. I don't know any Fluent API way of doing this though.

Mert Akcakaya
  • 3,109
  • 2
  • 31
  • 42
  • 2
    Sure, Mert. At the moment I am using migrations and there in the Up() method you can also put: CreateIndex("dbo.Table1", "Column1", true, "Column1_IX") and in Down() DropIndex(("dbo.Table1", "Column1_IX"). I was just hoping that they added a fluent API too... – Valo Mar 24 '14 at 19:35
2

You can use one of this

// Indexes

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

or

  this.Property(e => e.IsActive).HasColumnAnnotation(
            "Index",
            new IndexAnnotation(new IndexAttribute("IX_IsActive")));
Nayas Subramanian
  • 2,269
  • 21
  • 28
1

I write an extension method for use in fluent EF to avoid extra code:

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

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

then use it like this:

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

or like this if index needs some configs:

Property(t => t.CardNo)
    .HasIndexAnnotation(new IndexAttribute("IX_Account") { IsUnique = true });
Omid.Hanjani
  • 1,444
  • 2
  • 20
  • 29
0

Currently, on .net core it will be :

[Index(nameof(AdvId), IsUnique = true)]
public class tblAdvertisement
{
    public int Id { get; set; }
    public int AdvId { get; set; }
    public DateTime Created { get; set; }
}

So, attribute set is on class, not on member. Output migration looks like this after :

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.CreateIndex(
        name: "IX_Advertisements_AdvId",
        table: "Advertisements",
        column: "AdvId",
        unique: true);
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropIndex(
        name: "IX_Advertisements_AdvId",
        table: "Advertisements");

}
Nigrimmist
  • 10,289
  • 4
  • 52
  • 53
  • Yeah, but the question is how to use fluent API (I edited it to make that more clear. OP mentioned "code-first" vs "attributes", but of course attributes are also code-first). – Gert Arnold Nov 07 '22 at 11:43