30

Is there a way to get EF CTP5 to create an index when it creates a schema?

Update: See here for how EF 6.1 handles this (as pointed out by juFo below).

Community
  • 1
  • 1
Sean Kearon
  • 10,987
  • 13
  • 77
  • 93
  • 1
    You can vote for this feature here: http://connect.microsoft.com/VisualStudio/feedback/details/624575/index-attribute-in-ef-code-first – Václav Dajbych Dec 09 '11 at 13:07
  • 2
    @vasek7: MS responded and said you should vote here instead: http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2231176-indexattribute (I added my +3) – mpen Apr 15 '12 at 22:29
  • see solution here: http://stackoverflow.com/a/23055838/187650 – juFo Jul 20 '14 at 17:44

4 Answers4

31

You can take advantage of the new CTP5’s ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database.

The best place to invoke SqlCommand method for this purpose is inside a Seed method that has been overridden in a custom Initializer class. For example:

protected override void Seed(EntityMappingContext context)
{
    context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ...");
}
Morteza Manavi
  • 33,026
  • 6
  • 100
  • 83
  • What a great idea! I presume there is no built in support other than this then? – Sean Kearon Feb 14 '11 at 18:53
  • 1
    Unfortunately not. I myself used this method to create a unique constraint here: http://weblogs.asp.net/manavi/archive/2011/01/23/associations-in-ef-code-first-ctp5-part-3-one-to-one-foreign-key-associations.aspx – Morteza Manavi Feb 14 '11 at 19:18
  • Thanks again - I thought as much. Nice artice series too, very useful :) – Sean Kearon Feb 14 '11 at 20:29
  • No problem, glad you find them useful :) – Morteza Manavi Feb 14 '11 at 20:32
  • 4
    For the Magic Unicorn edition (EF 4.1 Code-First) -- use context.Database.ExecuteSqlCommand("CREATE INDEX IX_NAME ON ..."); – Rob Koch May 06 '11 at 14:26
  • 8
    If you're using Migrations, you want this to be a part of a Code-Based Migration, not your Seed method, since you presumably add Indexes over time. – Chris Moschini Mar 09 '12 at 05:14
  • 2
    @ChrisMoschini: If you're doing this in a migration, there appears to be a function called `CreateIndex` -- we don't need to write any raw SQL. – mpen Apr 14 '12 at 22:48
  • @Mark Good point. The down method is DropIndex - http://msdn.microsoft.com/en-us/library/hh829368(v=vs.103).aspx – Chris Moschini Apr 15 '12 at 16:11
  • Doesn't this create the index every time migrations are applied? And doesn't that error? – Carl G Mar 03 '13 at 20:31
  • Service info: From 6.1 and onwards there is an [Index] Attribute available - http://msdn.microsoft.com/en-US/data/jj591583#Index – MartinF Aug 27 '14 at 13:57
17

As some mentioned in the comments to Mortezas answer there is a CreateIndex/DropIndex method if you use migrations.

But if you are in "debug"/development mode and is changing the schema all the time and are recreating the database every time you can use the example mentioned in Morteza answer.

To make it a little easier, I have written a very simple extension method to make it strongly typed, as inspiration that I want to share with anyone who reads this question and maybe would like this approach aswell. Just change it to fit your needs and way of naming indexes.

You use it like this: context.Database.CreateUniqueIndex<User>(x => x.Name);

.

    public static void CreateUniqueIndex<TModel>(this Database database, Expression<Func<TModel, object>> expression)
    {
        if (database == null)
            throw new ArgumentNullException("database");

        // Assumes singular table name matching the name of the Model type

        var tableName = typeof(TModel).Name;
        var columnName = GetLambdaExpressionName(expression.Body);
        var indexName = string.Format("IX_{0}_{1}", tableName, columnName);

        var createIndexSql = string.Format("CREATE UNIQUE INDEX {0} ON {1} ({2})", indexName, tableName, columnName);

        database.ExecuteSqlCommand(createIndexSql);
    }

    public static string GetLambdaExpressionName(Expression expression)
    {
        MemberExpression memberExp = expression as MemberExpression;

        if (memberExp == null)
        {
            // Check if it is an UnaryExpression and unwrap it
            var unaryExp = expression as UnaryExpression;
            if (unaryExp != null)
                memberExp = unaryExp.Operand as MemberExpression;
        }

        if (memberExp == null)
            throw new ArgumentException("Cannot get name from expression", "expression");

        return memberExp.Member.Name;
    }

Update: From version 6.1 and onwards there is an [Index] attribute available.

For more info, see http://msdn.microsoft.com/en-US/data/jj591583#Index

MartinF
  • 5,929
  • 5
  • 40
  • 29
  • Nice, although you can't create multicolumn indexes. – majkinetor Apr 22 '13 at 09:39
  • Where to add this index? I tried it in the seed method, but I get the error: Cannot find the object "" because it does not exist or you do not have permissions – Roel Sep 26 '13 at 10:00
  • @majkinetor you *can* create multicolumn indexes in 6.1. It's on the msdn page from the last link. Just put the index attribute on each property, like this: `[Index("IX_BlogIdAndRating" /*use the same name for all properties*/, 2 /*change the number for each property*/)]` – Cristian Diaconescu Jun 19 '15 at 12:50
2

This feature should be available in the near-future via data annotations and the Fluent API. Microsoft have added it into their public backlog:

http://entityframework.codeplex.com/workitem/list/basic?keywords=DevDiv [Id=87553]

Until then, you'll need to use a seed method on a custom Initializer class to execute the SQL to create the unique index, and if you're using code-first migrations, create a new migration for adding the unique index, and use the CreateIndex and DropIndex methods in your Up and Down methods for the migration to create and drop the index.

Jez
  • 27,951
  • 32
  • 136
  • 233
1

Check my answer here Entity Framework Code First Fluent Api: Adding Indexes to columns this allows you to define multi column indexes by using attributes on properties.

Community
  • 1
  • 1
Peter
  • 37,042
  • 39
  • 142
  • 198
  • Be careful when posting copy and paste boilerplate/verbatim answers to multiple questions, these tend to be flagged as "spammy" by the community. If you're doing this then it usually means the questions are duplicates so flag them as such instead: http://stackoverflow.com/a/13144786/419 – Kev Oct 30 '12 at 23:32
  • Well the questions are not identical as they use different versions of EF, now i have only tested my solution with EF5 but it could work on both.. any way ill edit my post and refer to the other post... – Peter Oct 31 '12 at 14:00