Entity Framework Code First Fluent Api:向列添加索引

Posted

技术标签:

【中文标题】Entity Framework Code First Fluent Api:向列添加索引【英文标题】:Entity Framework Code First Fluent Api: Adding Indexes to columns 【发布时间】:2012-01-05 22:46:57 【问题描述】:

我正在运行 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 ...");

我非常希望避免使用这样的原始 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 中实现

【问题讨论】:

在此处查看解决方案:***.com/a/23055838/187650 【参考方案1】:

在 EF 4.3 中引入迁移后,您现在可以在修改或创建表时添加索引。这是来自 ADO.NET 团队博客的EF 4.3 Code-Based Migrations Walkthrough 的摘录

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

这是一种很好的强类型添加索引的方法,这是我第一次发布问题时所寻找的。​​p>

【讨论】:

我的意思是原来的问题提到了 4.2 :) 这个解决方案还不够好。如果从头开始创建数据库,迁移中添加的索引将丢失。你真的想要一个索引的数据注释。 @Jez 我完全同意,或者通过 EntityTypeConfigurations 设置它的方式 @Jez 如果数据库是从头开始创建的,那么所有的迁移都会运行并重新创建索引,所以应该没问题。 @ciscoheat 根据我的经验,这不会发生。它不运行任何迁移;它只是创建与最新版本的数据模型相对应的数据库,并将单个条目插入到迁移历史表中以进行初始创建。【参考方案2】:

您可以创建一个名为 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)
                        
                        
                    
                

我什至继续以相同的方式添加基于类的索引(可能有多个列)、唯一约束和默认约束。还有一点非常棒的是,如果您将这些属性放在继承的类上,则索引或约束将应用于继承它的所有类(表)。

顺便说一句,复数助手包含以下内容:

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

【讨论】:

非常有趣,这正是我正在考虑的事情,使用数据注释或流利的 api 来定义模型中的索引。我将看看我是否可以很快为这种方法创建一些测试,干得好。【参考方案3】:

要基于frozen 的响应,您可以自己将其编码到迁移中。

首先,转到包管理器控制台并使用add-migration 创建一个新迁移,然后为其命名。将出现一个空白迁移。坚持下去:

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

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

请注意,如果您使用的是字符串字段,则它的长度也需要限制为 450 个字符。

【讨论】:

这正是我想要的,因为我需要在迁移中创建索引。谢谢! 这里使用的 DropIndex 方法不正确。第二个参数是string[] columnsstring name。 Down 方法将尝试删除名为“ColumnName”的索引。您需要在 Up 中指定索引名称或在 Down 中传入列名数组。见msdn.microsoft.com/en-us/library/hh829733(v=vs.103).aspx Sql Server 强制最大密钥大小为 900 字节。 msdn.microsoft.com/en-us/library/ms191241(v=sql.105).aspx。您的 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));
        
   

【讨论】:

我在 CreateIndex() 中为表传递了一个类型,以便在表名上具有编译时安全性。遗憾的是,对于字段名称来说这并不容易,所以我没有实现它。 谢谢 - 这对我有用。稍微扩展了 CreateIndex 方法以支持多列和唯一索引 - 请参阅下面的答案:【参考方案5】:

请注意,在 Entity Framework 6.1(目前处于测试阶段)中,将支持 IndexAttribute 来注释索引属性,这将自动在您的 Code First 迁移中生成(唯一)索引。

【讨论】:

现在可以了:EF 6.1: Creating indexes with IndexAttribute【参考方案6】:

对于使用 Entity Framework 6.1+ 的任何人,您可以使用 fluent api 执行以下操作:

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

在documentation 中了解更多信息。

【讨论】:

【参考方案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");
    

然后在你的 dbcontext 中重载 OnModelCreating

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

将索引属性应用于您的实体类型,使用此解决方案,您可以在同一索引中拥有多个字段,只需使用相同的名称和唯一性。

【讨论】:

非常有趣的解决方案,遇到任何方法来扩展 add-migrations 功能和用于类型配置的 fluent api,以允许在使用迁移时创建索引? 我从未使用过迁移,所以我不知道,但这应该适用于迁移,但您必须检查索引是否存在并在创建之前将其删除 我在 [link]blogs.southworks.net/dschenkelman/2012/08/18/… 找到了相同的解决方案,但它不适用于我。我使用按层次结构表的方法,并且代码仅通过基类上的属性。 @RudolfDvoracek 更改以下行 const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance;到类“const BindingFlags PublicInstance = BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy;”这应该使它循环基类中的属性。 @Petoj 我已经尝试过您的建议,但没有成功。应用程序没有处理派生类型,只检查基类型的属性。【参考方案8】:

将 Tsuushin 的上述答案扩展为支持多列和唯一约束:

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

【讨论】:

简洁的方法是使用不是字符串的字段和表,以获得编译时安全性,我猜说起来难做起来难,因为它必须使用 dataannotations/fluent api 的字段名/表名。 @FRoZeN 只需将类型从字符串更改为接口,创建一个空接口并在方法内部获取对象的类名并用作表名。【参考方案9】:

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

这样每次更新数据库时都会运行索引属性。

【讨论】:

为什么不更改它,让它删除并重新创建它,这样您就可以更改它包含的字段,并且每次更新都会更新它..【参考方案10】:

如果您希望将此功能添加到 EF,那么您可以在这里投票http://entityframework.codeplex.com/workitem/57

【讨论】:

【参考方案11】:

jwsadler 的数据注释扩展非常适合我们。我们使用 Annotations 来影响对类或属性的处理,并使用 Fluent API 来进行全局更改。

我们的注释涵盖索引(唯一和非唯一)以及 getdate() 和 (1) 的默认值。代码示例显示了我们如何将其应用于我们的情况。我们所有的类都继承自一个基类。这个实现做了很多假设,因为我们有一个非常简单的模型。我们正在使用实体框架 6.0.1。已经包含了很多 cmets。

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

【讨论】:

【参考方案12】:

为了进一步构建所有这些出色的响应,我们添加了以下代码,以便能够从关联的元数据类型中提取 Index 属性。完整详情请see my blog post,但这里是详细信息的总结。

元数据类型的使用如下:

    [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>IEnumerableIndexAttribute.</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);
    

【讨论】:

P.S.我们使用的是 EF 5.0,所以上面的代码在 EF 4.2 上未经测试【参考方案13】:

对于 EF7,您可以使用 hasIndex() 方法。 我们也可以设置聚集索引和非聚集索引。 默认情况下,主键将被聚集。我们也可以改变这种行为。

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

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

【讨论】:

【参考方案14】:

我发现@highace 给出的答案存在问题 - 向下迁移对 DropIndex 使用了错误的覆盖。这是我所做的:

    为了遵守 Sql Server 对索引列(900 字节)的限制,我减小了模型中几个字段的大小 我使用 Add-Migration "Add Unique Indexes" 添加了迁移 我手动将 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));

【讨论】:

【参考方案15】:

您可以在模型构建器中指定索引

modelBuilder
            .Entity<UserSalary>(builder =>
            
                builder.HasNoKey();
                builder.HasIndex("UserId").IsUnique(false);
                builder.ToTable("UserSalary");
            );

【讨论】:

What's new?。另外,为什么HasNoKey?您认为创建索引是必要的。 对不起,您可以在上面的示例中删除 HasNoKey。但这就是您为列声明索引的方式

以上是关于Entity Framework Code First Fluent Api:向列添加索引的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 5.0 Code First全面学习

Entity Framework Code First

ADO.NET Entity Framework -Code Fisrt 开篇

转:Entity Framework 5.0 Code First全面学习

Entity Framework Code First 不允许Entity直接实现接口

Entity Framework Code First 迁移 Migrations