Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a Clustered Index with Entity Framework Core

From EF6.1, we have a way of specifying a clustered index on a property

public class Person 
{
  [Index(IsClustered = true, IsUnique = true)]
  public long UserName { get; set; }
}

But this Index attribute does not seem to be in EF Core right now? In EF Core how do you achieve this?

like image 312
Ray Avatar asked Aug 14 '16 16:08

Ray


3 Answers

From the current EF Core documentation - Indexes section:

Data Annotations

Indexes can not be created using data annotations.

But for sure you can specify that via Fluent API (note the extension methods having ForSqlServer prefix which seem to denote SqlServer specific features):

modelBuilder.Entity<Person>()
    .HasIndex(e => e.UserName)
    .IsUnique()
    .ForSqlServerIsClustered();

Update: for EF Core 3.0+ the method is called just IsClustered:

modelBuilder.Entity<Person>()
    .HasIndex(e => e.UserName)
    .IsUnique()
    .IsClustered();

Update: Starting with EF Core 5.0, there is Index data annotation now mentioned in the Indexes documentation link, but it can't be used to specify database specific attributes like clustered (SqlServer specific), so the original answer still applies.

like image 78
Ivan Stoev Avatar answered Oct 19 '22 10:10

Ivan Stoev


For EF Core 3.0+ You can now use IsClustered:

modelBuilder.Entity<Person>()
.HasIndex(e => e.UserName)
.IsUnique()
.IsClustered();

.ForSqlServerIsClustered() is now marked as obsolete.

Also be aware that if you have a Primary Key on the table you may also need to explicitly remove the clustering on it before you add the clustering on your Username:

modelBuilder.Entity<Person>()
.HasKey(e => e.PersonId)
.IsClustered(false);

modelBuilder.Entity<Person>()
.HasIndex(e => e.UserName)
.IsUnique()
.IsClustered();
like image 44
sarin Avatar answered Oct 19 '22 10:10

sarin


In the absence of built-in support, you can use a custom attribute of your own to annotate model properties and apply in OnModelCreating():

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    foreach (var entity in modelBuilder.Model.GetEntityTypes())
    {
        foreach (var prop in entity.GetProperties())
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<IndexAttribute>();
            if (attr != null)
            {
                var index = entity.AddIndex(prop);
                index.IsUnique = attr.IsUnique;
                index.SqlServer().IsClustered = attr.IsClustered;
            }
        }
    }
}

With a simple marker attribute class:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IndexAttribute : Attribute
{
    public bool IsUnique { get; set; } 
    public bool IsClustered { get; set; } 
}

Then in your model class, just add the attribute to create a secondary index:

public class User
{
    public int UserId { get; set; }
    [Index(IsUnique = true, IsClustered = true)]
    public string Nickname { get; set; }
}
like image 10
StephenD Avatar answered Oct 19 '22 11:10

StephenD