11

Say I have the following model:

[Table("Record")]
public class RecordModel
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    [Display(Name = "Record Id")]
    public int RecordId { get; set; }

    [StringLength(150)]
    public string Name { get; set; }

    [Required]
    [StringLength(15)]
    public string IMEI { get; set; }
}

Is it possible to add an index to the IMEI column through using an attribute, data annotation, or something from the model?

2 Answers 2

20

EF Core 5

In EF Core 5, the Index attribute should be placed on the class. See: MSDN

[Index(nameof(Url))]
public class Post
{
    public int PostId { get; set; }
    public string Url { get; set; }
    public string Title { get; set; }
    public DateTime PublishedOn { get; set; }
}

or revert to the fluent syntax for more advanced option:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>()
        .HasIndex(p => p.Url)
        .IncludeProperties(
            p => new { p.Title, p.PublishedOn });
}

EF 6.1

Since the release of EF 6.1. (March 17th, 2014) there is indeed an [Index] attribute available.

Functionality as:

[Index("IMEIIndex", IsUnique = true)]
public string IMEI { get; set; }

comes out of the box.

PS: other properties are Order and IsClustered.


According to this link: http://blogs.msdn.com/b/adonet/archive/2014/02/11/ef-6-1-0-beta-1-available.aspx

It will be available in EF 6.1 as a standard DataAnnotation attribute.

IndexAttribute allows indexes to be specified by placing an [Index] attribute on a property (or properties) in your Code First model. Code First will then create a corresponding index in the database.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. Just a note: you need to add an EntityFramework reference in the project where you want to use this. A System.ComponentModel.DataAnnotations is not enough. Took a bit for me to figure out.
To be fair, the current state of EF 6.x has "IsUnique = true/false" as second parameter.
12
+50

According to this link: Creating Indexes via Data Annotations with Entity Framework 5.0 you should write some kind of extension code:

using System; 

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
    public IndexAttribute(string name, bool unique = false)
    {
        this.Name = name;
        this.IsUnique = unique;
    }

    public string Name { get; private set; }

    public bool IsUnique { get; private set; }
}

and the second class:

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Reflection;

public class IndexInitializer<T> : IDatabaseInitializer<T> where T : DbContext
{
    private const string CreateIndexQueryTemplate = "CREATE {unique} INDEX {indexName} ON {tableName} ({columnName})";

    public void InitializeDatabase(T context)
    {
        const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;

        foreach (var dataSetProperty in typeof(T).GetProperties(PublicInstance).Where(
            p => p.PropertyType.Name == typeof(DbSet<>).Name))
        {
            var entityType = dataSetProperty.PropertyType.GetGenericArguments().Single();

            TableAttribute[] tableAttributes = (TableAttribute[])entityType.GetCustomAttributes(typeof(TableAttribute), false);

            foreach (var property in entityType.GetProperties(PublicInstance))
            {
                IndexAttribute[] indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
                NotMappedAttribute[] notMappedAttributes = (NotMappedAttribute[])property.GetCustomAttributes(typeof(NotMappedAttribute), false);
                if (indexAttributes.Length > 0 && notMappedAttributes.Length == 0)
                {
                    ColumnAttribute[] columnAttributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), false);

                    foreach (var indexAttribute in indexAttributes)
                    {
                        string indexName = indexAttribute.Name;
                        string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;
                        string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
                        string query = CreateIndexQueryTemplate.Replace("{indexName}", indexName)
                            .Replace("{tableName}", tableName)
                            .Replace("{columnName}", columnName)
                            .Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);

                        context.Database.CreateIfNotExists();

                        context.Database.ExecuteSqlCommand(query);
                    }
                }
            }
        }
    }
}

After it you can use your index this way:

[Required]
[Index("IMEIIndex", unique: true)]
[StringLength(15)]
public string IMEI { get; set; }

5 Comments

What do you have to be using to be able to use Index? If I try to use this [Index("IMEIIndex", unique: true)] it asks me to generate my own Index class
Very nice, seems to have done the trick for me. Thanks! I'll give you the bounty when this site lets me (I have to wait 1 hour apparently)
upvote done! - one thing you forgot from that site (wasn't obvious as it was in a link) but you have to include Database.SetInitializer(new IndexInitializer<DbContext>()); in your OnModelCreating of the dbcontext
@Pete - ok! Now it is save here in comments )
Thanks, I ended up adding the Index in the seeding method but I like this way better so I'll probably do this from now on as it just seems a bit neater - allows you to see the index on your model rather than having to look through your seeding methods too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.