实体框架Code First Fluent Api:向列添加索引

66

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

我非常想避免像那样使用原始的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中实现了。


请查看此处的解决方案:https://dev59.com/cWkv5IYBdhLWcg3waAJT#23055838 - juFo
15个回答

3

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

有一个很好的想法,就是使用一个不是字符串的字段和表,并且具有编译时的安全性。我认为这比较难实现,因为它必须使用数据注释或流畅API中的字段名和表名。 - Jim Wolff
@FRoZeN 只需将类型从字符串更改为接口,创建一个空接口,在方法内获取对象的类名并用作表名。 - SparK

2

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

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

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

enter image description here


2
为了进一步完善所有这些出色的响应,我们添加了以下代码,以便从相关元数据类型中获取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);
    }

请注意,我们正在使用EF 5.0,因此上述代码未在EF 4.2上进行测试。 - Tim Long

1
我发现@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));
}

0

您可以在ModelBuilder中指定索引

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

有什么新功能?此外,为什么要使用HasNoKey?您似乎认为它是创建索引所必需的。 - Gert Arnold
抱歉,您可以在上面的示例中删除HasNoKey。但这就是您声明索引到列的方式。 - Dennis Hisanan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接