Entity Framework Code First Fluent Api: добавление индексов в столбцы

Я запускаю EF 4.2 CF и хочу создавать индексы в определенных столбцах в моих объектах POCO.

В качестве примера можно сказать, что у нас есть этот класс сотрудников:

public class Employee
{
  public int EmployeeID { get; set; }
  public string EmployeeCode { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public DateTime HireDate { get; set; }
}

Мы часто проводим поиск сотрудников по их EmployeeCode, и, поскольку есть много сотрудников, было бы неплохо иметь индексированные по причинам производительности.

Можем ли мы сделать это с беглым api каким-то образом? или, возможно, аннотации данных?

Я знаю, что можно выполнить sql-команды примерно так:

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

Мне очень хотелось бы избежать использования raw SQL.

Я знаю, что этого не существует, но ищет что-то в этих строках:

class EmployeeConfiguration : EntityTypeConfiguration<Employee>
    {
        internal EmployeeConfiguration()
        {
            this.HasIndex(e => e.EmployeeCode)
                .HasIndex(e => e.FirstName)
                .HasIndex(e => e.LastName);
        }
    }

или, возможно, используя System.ComponentModel.DataAnnotations, POCO может выглядеть так (опять же я знаю, что этого не существует):

public class Employee
{
  public int EmployeeID { get; set; }
  [Indexed]
  public string EmployeeCode { get; set; }
  [Indexed]
  public string FirstName { get; set; }
  [Indexed]
  public string LastName { get; set; }
  public DateTime HireDate { get; set; }
}

У кого-нибудь есть идеи о том, как это сделать, или если есть какие-либо планы по реализации способа сделать это, первый код?

ОБНОВЛЕНИЕ: Как упоминалось в ответ Робба, эта функция реализована в версии EF 6.1

Ответ 1

После внесения изменений в EF 4.3 теперь вы можете добавлять индексы при изменении или создании таблицы. Вот отрывок из EF 4.3 пошагового перехода на основе кода из блога команды ADO.NET

namespace MigrationsCodeDemo.Migrations
{
    using System.Data.Entity.Migrations;

    public partial class AddPostClass : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "Posts",
                c => new
                    {
                        PostId = c.Int(nullable: false, identity: true),
                        Title = c.String(maxLength: 200),
                        Content = c.String(),
                        BlogId = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.PostId)
                .ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
                .Index(t => t.BlogId)
                .Index(p => p.Title, unique: true);

            AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
        }

        public override void Down()
        {
            DropIndex("Posts", new[] { "BlogId" });
            DropForeignKey("Posts", "BlogId", "Blogs");
            DropColumn("Blogs", "Rating");
            DropTable("Posts");
        }
    }
}

Это хороший сильно типизированный способ добавления индексов, который был тем, что я искал, когда я впервые разместил вопрос.

Ответ 2

Вы можете создать атрибут с индексированием (как вы сказали), который затем выбирается в пользовательском инициализаторе.

Я создал следующий атрибут:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
public class IndexAttribute : Attribute
{
    public IndexAttribute(bool isUnique = false, bool isClustered = false, SortOrder sortOrder = SortOrder.Ascending)
    {
        IsUnique = isUnique;
        IsClustered = isClustered;
        SortOrder = sortOrder == SortOrder.Unspecified ? SortOrder.Ascending : sortOrder;

    }

    public bool IsUnique { get; private set; }
    public bool IsClustered { get; private set; }
    public SortOrder SortOrder { get; private set; }
    //public string Where { get; private set; }
}

Затем я создал пользовательский инициализатор, который получил список имен таблиц, созданных для сущностей в моем контексте. У меня есть два базовых класса, которые наследуют все мои объекты, поэтому для получения имен таблиц я сделал следующее:

 var baseEF = typeof (BaseEFEntity);
        var baseLink = typeof (BaseLinkTable);
        var types =
            AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(s => s.GetTypes()).Where(
                baseEF.IsAssignableFrom).Union(AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(
                    s => s.GetTypes()).Where(
                        baseLink.IsAssignableFrom));

        var sqlScript = context.ObjectContext.CreateDatabaseScript();

        foreach (var type in types)
        {
            var table = (TableAttribute) type.GetCustomAttributes(typeof (TableAttribute), true).FirstOrDefault();
            var tableName = (table != null ? table.Name : null) ?? Pluralizer.Pluralize(type.Name);

Затем я нашел все свойства для каждого объекта, которые имеют этот атрибут, а затем выполнил команду SQL для создания индекса для каждого свойства. Классно!

//Check that a table exists
            if (sqlScript.ToLower().Contains(string.Format(CREATETABLELOOKUP, tableName.ToLower())))
            {

                //indexes

                var indexAttrib = typeof (IndexAttribute);
                properties = type.GetProperties().Where(prop => Attribute.IsDefined(prop, indexAttrib));
                foreach (var property in properties)
                {
                    var attributes = property.GetCustomAttributes(indexAttrib, true).ToList();

                    foreach (IndexAttribute index in attributes)
                    {
                        var indexName = string.Format(INDEXNAMEFORMAT, tableName, property.Name,
                                                      attributes.Count > 1
                                                          ? UNDERSCORE + (attributes.IndexOf(index) + 1)
                                                          : string.Empty);
                        try
                        {
                            context.ObjectContext.ExecuteStoreCommand(
                                string.Format(INDEX_STRING, indexName,
                                              tableName,
                                              property.Name,
                                              index.IsUnique ? UNIQUE : string.Empty,
                                              index.IsClustered ? CLUSTERED : NONCLUSTERED,
                                              index.SortOrder == SortOrder.Ascending ? ASC : DESC));
                        }
                        catch (Exception)
                        {
                        }
                    }
                }

Я даже включил индексы класса (которые могли иметь несколько столбцов), уникальные ограничения и ограничения по умолчанию все одинаково. Также приятно, что если вы поместите эти атрибуты в унаследованный класс, индекс или ограничение будут применены ко всем классам (таблицам), которые наследуют его.

Кстати, помощник плюрализатора содержит следующее:

public static class Pluralizer
{
    private static object _pluralizer;
    private static MethodInfo _pluralizationMethod;

    public static string Pluralize(string word)
    {
        CreatePluralizer();
        return (string) _pluralizationMethod.Invoke(_pluralizer, new object[] {word});
    }

    public static void CreatePluralizer()
    {
        if (_pluralizer == null)
        {
            var aseembly = typeof (DbContext).Assembly;
            var type =
                aseembly.GetType(
                    "System.Data.Entity.ModelConfiguration.Design.PluralizationServices.EnglishPluralizationService");
            _pluralizer = Activator.CreateInstance(type, true);
            _pluralizationMethod = _pluralizer.GetType().GetMethod("Pluralize");
        }
    }
}

Ответ 3

Чтобы использовать замороженный ответ, вы можете вручную передать его в перенос.

Сначала перейдите в консоль диспетчера пакетов и создайте новую миграцию с помощью add-migration, а затем дайте ей имя. Появится пустая миграция. Вставьте это в:

    public override void Up()
    {
        CreateIndex("TableName", "ColumnName");
    }

    public override void Down()
    {
        DropIndex("TableName",new[] {"ColumnName"});
    }

Обратите внимание: если вы используете поле строки, оно должно быть ограничено длиной 450 символов.

Ответ 4

Я также изучил это недавно и не нашел другого способа, поэтому я решил создать индексы при посеве базы данных:

public class MyDBInitializer : DropCreateDatabaseIfModelChanges<MyContext>
{
    private MyContext _Context;

    protected override void Seed(MyContext context)
    {
        base.Seed(context);
        _Context = context;

        // We create database indexes
        CreateIndex("FieldName", typeof(ClassName));

        context.SaveChanges();
    }

    private void CreateIndex(string field, Type table)
    {
        _Context.Database.ExecuteSqlCommand(String.Format("CREATE INDEX IX_{0} ON {1} ({0})", field, table.Name));
    }    
}   

Ответ 5

Обратите внимание, что в Entity Framework 6.1 (в настоящее время в бета-версии) поддержка IndexAttribute будет аннотировать свойства индекса, которые автоматически приведут к (уникальному) индексу в ваших первых переходах кода.

Ответ 6

Для всех, кто использует Entity Framework 6.1+, вы можете сделать следующее с быстрым api:

modelBuilder 
    .Entity<Department>() 
    .Property(t => t.Name) 
    .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));

Подробнее в документации.

Ответ 7

Ну, я нашел решение онлайн и адаптировал его, чтобы он соответствовал моим потребностям:

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

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;
        Dictionary<IndexAttribute, List<string>> indexes = new Dictionary<IndexAttribute, List<string>>();
        string query = string.Empty;

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

            indexes.Clear();
            string tableName = tableAttributes.Length != 0 ? tableAttributes[0].Name : dataSetProperty.Name;

            foreach (PropertyInfo 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 (IndexAttribute indexAttribute in indexAttributes)
                    {
                        if (!indexes.ContainsKey(indexAttribute))
                        {
                            indexes.Add(indexAttribute, new List<string>());
                        }

                        if (property.PropertyType.IsValueType || property.PropertyType == typeof(string))
                        {
                            string columnName = columnAttributes.Length != 0 ? columnAttributes[0].Name : property.Name;
                            indexes[indexAttribute].Add(columnName);
                        }
                        else
                        {
                            indexes[indexAttribute].Add(property.PropertyType.Name + "_" + GetKeyName(property.PropertyType));
                        }
                    }
                }
            }

            foreach (IndexAttribute indexAttribute in indexes.Keys)
            {
                query += CreateIndexQueryTemplate.Replace("{indexName}", indexAttribute.Name)
                            .Replace("{tableName}", tableName)
                            .Replace("{columnName}", string.Join(", ", indexes[indexAttribute].ToArray()))
                            .Replace("{unique}", indexAttribute.IsUnique ? "UNIQUE" : string.Empty);
            }
        }

        if (context.Database.CreateIfNotExists())
        {
            context.Database.ExecuteSqlCommand(query);
        }
    }

    private string GetKeyName(Type type)
    {
        PropertyInfo[] propertyInfos = type.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
        foreach (PropertyInfo propertyInfo in propertyInfos)
        {
            if (propertyInfo.GetCustomAttribute(typeof(KeyAttribute), true) != null)
                return propertyInfo.Name;
        }
        throw new Exception("No property was found with the attribute Key");
    }
}

Затем перегрузите OnModelCreating в свой dbcontext

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        Database.SetInitializer(new IndexInitializer<MyContext>());
        base.OnModelCreating(modelBuilder);
    }

Применить атрибут индекса к типу Entity, с помощью этого решения вы можете иметь несколько полей в одном и том же индексе, просто используя одно и то же имя и уникальный.

Ответ 8

Расширение ответа Tsuushin выше для поддержки нескольких столбцов и уникальных ограничений:

    private void CreateIndex(RBPContext context, string field, string table, bool unique = false)
    {
        context.Database.ExecuteSqlCommand(String.Format("CREATE {0}NONCLUSTERED INDEX IX_{1}_{2} ON {1} ({3})", 
            unique ? "UNIQUE " : "",
            table,
            field.Replace(",","_"),
            field));
    } 

Ответ 9

расширяющееся на Petoj

i изменил параметр CreateIndexQueryTemplate на

private const string CreateIndexQueryTemplate = "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '{indexName}') CREATE {unique} INDEX {indexName} ON {tableName} ({columnName});";

и удалил из OnModelCreating

следующее:
Database.SetInitializer(new IndexInitializer<MyContext>());

и добавил следующее в метод настройки посева

new IndexInitializer<MyContext>().InitializeDatabase(context);

таким образом атрибуты индекса запускаются каждый раз, когда вы делаете базу данных обновлений.

Ответ 11

Расширение jwsadler аннотаций данных было хорошо для нас. Мы используем аннотации для влияния на обработку класса или свойства и Fluent API для глобальных изменений.

Наши аннотации охватывают индексы (уникальные, а не уникальные) плюс значения по умолчанию для getdate() и (1). Образец кода показывает, как мы применили его к нашей ситуации. Все наши классы наследуются от одного базового класса. Эта реализация делает много предположений, потому что у нас довольно простая модель. Мы используем Entity Framework 6.0.1. Было добавлено много комментариев.

using System;
using System.Linq;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;

namespace YourNameSpace
{
    public enum SqlOption
    {
        Active = 1,
        GetDate = 2,
        Index = 3,
        Unique = 4,
    }

    [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
    public class SqlAttribute : Attribute
    {
        public SqlAttribute(SqlOption selectedOption = SqlOption.Index)
        {
            this.Option = selectedOption;
        }

        public SqlOption Option {get; set;}
    }

    // See enum above, usage examples: [Sql(SqlOption.Unique)] [Sql(SqlOption.Index)] [Sql(SqlOption.GetDate)]
    public class SqlInitializer<T> : IDatabaseInitializer<T> where T : DbContext
    {
        // Create templates for the DDL we want generate
        const string INDEX_TEMPLATE = "CREATE NONCLUSTERED INDEX IX_{columnName} ON [dbo].[{tableName}] ([{columnName}]);";
        const string UNIQUE_TEMPLATE = "CREATE UNIQUE NONCLUSTERED INDEX UQ_{columnName} ON [dbo].[{tableName}] ([{columnName}]);";
        const string GETDATE_TEMPLATE = "ALTER TABLE [dbo].[{tableName}] ADD DEFAULT (getdate()) FOR [{columnName}];";
        const string ACTIVE_TEMPLATE = "ALTER TABLE [dbo].[{tableName}] ADD DEFAULT (1) FOR [{columnName}];";

        // Called by Database.SetInitializer(new IndexInitializer< MyDBContext>()); in MyDBContext.cs
        public void InitializeDatabase(T context)
        {
            // To be used for the SQL DDL that I generate
            string sql = string.Empty;

            // All of my classes are derived from my base class, Entity
            var baseClass = typeof(Entity);

            // Get a list of classes in my model derived from my base class
            var modelClasses = AppDomain.CurrentDomain.GetAssemblies().ToList().
                SelectMany(s => s.GetTypes()).Where(baseClass.IsAssignableFrom);

            // For debugging only - examine the SQL DDL that Entity Framework is generating
            // Manipulating this is discouraged.
            var generatedDDSQL = ((IObjectContextAdapter)context).ObjectContext.CreateDatabaseScript();

            // Define which Annotation Attribute we care about (this class!)
            var annotationAttribute = typeof(SqlAttribute);

            // Generate a list of concrete classes in my model derived from
            // Entity class since we follow Table Per Concrete Class (TPC).
            var concreteClasses = from modelClass in modelClasses
                                  where !modelClass.IsAbstract
                                  select modelClass;

            // Iterate through my model concrete classes (will be mapped to tables)
            foreach (var concreteClass in concreteClasses)
            {
                // Calculate the table name - could get the table name from list of DbContext properties
                // to be more correct (but this is sufficient in my case)
                var tableName = concreteClass.Name + "s";

                // Get concrete class properties that have this annotation
                var propertiesWithAnnotations = concreteClass.GetProperties().Where(prop => Attribute.IsDefined(prop, annotationAttribute));

                foreach (var annotatedProperty in propertiesWithAnnotations)
                {
                    var columnName = annotatedProperty.Name;
                    var annotationProperties = annotatedProperty.GetCustomAttributes(annotationAttribute, true).ToList();

                    foreach (SqlAttribute annotationProperty in annotationProperties)
                    {
                        // Generate the appropriate SQL DLL based on the attribute selected
                        switch (annotationProperty.Option)
                        {
                            case SqlOption.Active: // Default value of true plus an index (for my case)
                                sql += ACTIVE_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                sql += INDEX_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                break;
                            case SqlOption.GetDate: // GetDate plus an index (for my case)
                                sql += GETDATE_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                sql += INDEX_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                break;
                            case SqlOption.Index: // Default for empty annotations for example [Sql()]
                                sql += INDEX_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                break;
                            case SqlOption.Unique:
                                sql += UNIQUE_TEMPLATE.Replace("{tableName}", tableName).Replace("{columnName}", columnName);
                                break;
                        } // switch
                    } // foreach annotationProperty
                } // foreach annotatedProperty
            } // foreach concreteClass

            // Would have been better not to go through all the work of generating the SQL
            // if we weren't going to use it, but putting it here makes it easier to follow.
            if (context.Database.CreateIfNotExists())
                context.Database.ExecuteSqlCommand(sql);

        } // InitializeDatabase
    } // SqlInitializer
} // Namespace

Здесь наш контекст:

using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace YourNameSpace
{
    public class MyDBContext : DbContext
    {
       protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Only including my concrete classes here as we're following Table Per Concrete Class (TPC)
            public virtual DbSet<Attendance> Attendances { get; set; }
            public virtual DbSet<Course> Courses { get; set; }
            public virtual DbSet<Location> Locations { get; set; }
            public virtual DbSet<PaymentMethod> PaymentMethods { get; set; }
            public virtual DbSet<Purchase> Purchases { get; set; }
            public virtual DbSet<Student> Students { get; set; }
            public virtual DbSet<Teacher> Teachers { get; set; }

            // Process the SQL Annotations
            Database.SetInitializer(new SqlInitializer<MyDBContext>());
            base.OnModelCreating(modelBuilder);

            // Change all datetime columns to datetime2
            modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));

            // Turn off cascading deletes
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
        }
    }
}

Ответ 12

Чтобы продолжить работу над этими замечательными ответами, мы добавили следующий код, чтобы активировать атрибут Index из связанного с ним типа метаданных. Для полной информации, пожалуйста, см. Мой пост в блоге, но вкратце вот подробности.

Типы метаданных используются следующим образом:

    [MetadataType(typeof(UserAccountAnnotations))]
    public partial class UserAccount : IDomainEntity
        {
        [Key]
        public int Id { get; set; } // Unique ID
        sealed class UserAccountAnnotations
            {
            [Index("IX_UserName", unique: true)]
            public string UserName { get; set; }
            }
       }

В этом примере тип метаданных является вложенным классом, но он не обязательно должен быть, он может быть любым типом. Сопоставление свойств выполняется только по имени, поэтому тип метаданных должен иметь свойство с тем же именем, и любые аннотации данных, применяемые к этому, должны затем применяться к связанному классу сущностей. Это не работает в исходном решении, потому что оно не проверяет соответствующий тип метаданных. Мы применили следующий вспомогательный метод:

/// <summary>
///   Gets the index attributes on the specified property and the same property on any associated metadata type.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>IEnumerable{IndexAttribute}.</returns>
IEnumerable<IndexAttribute> GetIndexAttributes(PropertyInfo property)
    {
    Type entityType = property.DeclaringType;
    var indexAttributes = (IndexAttribute[])property.GetCustomAttributes(typeof(IndexAttribute), false);
    var metadataAttribute =
        entityType.GetCustomAttribute(typeof(MetadataTypeAttribute)) as MetadataTypeAttribute;
    if (metadataAttribute == null)
        return indexAttributes; // No metadata type

    Type associatedMetadataType = metadataAttribute.MetadataClassType;
    PropertyInfo associatedProperty = associatedMetadataType.GetProperty(property.Name);
    if (associatedProperty == null)
        return indexAttributes; // No metadata on the property

    var associatedIndexAttributes =
        (IndexAttribute[])associatedProperty.GetCustomAttributes(typeof(IndexAttribute), false);
    return indexAttributes.Union(associatedIndexAttributes);
    }

Ответ 13

Я обнаружил проблему с ответом @highace - миграция вниз использует неправильное переопределение для DropIndex. Вот что я сделал:

  • Чтобы соответствовать ограничениям Sql Server на столбцах индекса (900 байт), я уменьшил размер пары полей в моей модели.
  • Я добавил перенос с помощью Add-Migration "Добавить уникальные индексы"
  • Я вручную добавил методы CreateIndex и DropIndex для переноса. Я использовал переопределение, которое принимает имя индекса для индекса с одним столбцом. Я использовал переопределение, которое принимает массив имен столбцов, где индекс охватывает более одного столбца

И вот код с примерами обоих переопределений каждого метода:

public partial class AddUniqueIndexes : DbMigration
{
    public override void Up()
    {
        //Sql Server limits indexes to 900 bytes, 
        //so we need to ensure cumulative field sizes do not exceed this 
        //otherwise inserts and updates could be prevented
        //http://www.sqlteam.com/article/included-columns-sql-server-2005
        AlterColumn("dbo.Answers",
            "Text",
            c => c.String(nullable: false, maxLength: 400));
        AlterColumn("dbo.ConstructionTypes",
            "Name",
            c => c.String(nullable: false, maxLength: 300));

        //[IX_Text] is the name that Entity Framework would use by default
        // even if it wasn't specified here
        CreateIndex("dbo.Answers",
            "Text",
            unique: true,
            name: "IX_Text");

        //Default name is [IX_Name_OrganisationID]
        CreateIndex("dbo.ConstructionTypes",
            new string[] { "Name", "OrganisationID" },
            unique: true);
    }

    public override void Down()
    {
        //Drop Indexes before altering fields 
        //(otherwise it will fail because of dependencies)

        //Example of dropping an index based on its name
        DropIndex("dbo.Answers", "IX_Text");

        //Example of dropping an index based on the columns it targets
        DropIndex("dbo.ConstructionTypes", 
            new string[] { "Name", "OrganisationID" }); 

        AlterColumn("dbo.ConstructionTypes",
            "Name",
            c => c.String(nullable: false));

        AlterColumn("dbo.Answers",
            "Text",
            c => c.String(nullable: false, maxLength: 500));
}

Ответ 14

Для EF7 вы можете использовать метод hasIndex(). Мы можем также установить кластерный и некластеризованный индекс. По умолчанию первичный ключ будет кластеризован. Мы также можем изменить это поведение.

supplierItemEntity.HasKey(supplierItem => supplierItem.SupplierItemId).ForSqlServerIsClustered(false);

supplierItemEntity.HasIndex(s => new { s.ItemId }).ForSqlServerIsClustered(true);

введите описание изображения здесь