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, и поскольку есть много сотрудников, было бы неплохо, чтобы это было индексировано по соображениям производительности.

можем ли мы сделать это с помощью fluent 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; }
}

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

обновление: как уже упоминалось в ответе Robba, эта функция реализована в EF версии 6.1

14 ответов


после того, как миграции были введены в 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");
        }
    }
}

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


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

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

[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)
                        {
                        }
                    }
                }

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

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

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

чтобы построить на ответе frozen, вы можете передать код в миграцию самостоятельно.

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

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

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

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


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

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

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


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

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

подробнее на документация.


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

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

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


расширения выше ответа 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));
    } 

расширение на Petoj

Я изменил 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);

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


Если вы хотите, чтобы эта функция была добавлена в EF, вы можете проголосовать за нее здесь http://entityframework.codeplex.com/workitem/57


расширение аннотаций данных 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's 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's properties
                // to be more correct (but this is sufficient in my case)
                var tableName = concreteClass.Name + "s";

                // Get concrete class's 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>();
        }
    }
}

чтобы продолжить работу над всеми этими замечательными ответами, мы добавили следующий код, чтобы атрибут 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; }
            }
       }

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

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

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

  1. чтобы соответствовать ограничению Sql Server на индексные столбцы (900 байт), я уменьшил размер нескольких полей в моей модели
  2. Я добавил миграцию с помощью Add-Migration "добавить уникальные индексы"
  3. Я вручную добавил методы 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));
}

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

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

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

enter image description here