【问题标题】:Entity Framework Code First Fluent Api: Adding Indexes to columnsEntity Framework Code First Fluent Api:向列添加索引
【发布时间】: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 中实现

【问题讨论】:

标签: c# entity-framework-4 ef-code-first data-annotations fluent-interface


【解决方案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 个字符。

【讨论】:

【解决方案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 迁移中生成(唯一)索引。

【讨论】:

【解决方案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 {0}NONCLUSTERED 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>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);
            }
        

        【讨论】:

        • 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 使用了错误的覆盖。这是我所做的:

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

          【讨论】:

            【解决方案15】:

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

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

            【讨论】:

            • What's new?。另外,为什么HasNoKey?您认为创建索引是必要的。
            • 对不起,您可以在上面的示例中删除 HasNoKey。但这就是您为列声明索引的方式
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-01-24
            • 2013-12-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-07-01
            • 1970-01-01
            相关资源
            最近更新 更多