如何使用Fluent配置在Entity Framework 6.2中创建索引。

131

有没有一种使用流畅配置在属性/列上创建索引的方法,而不是使用新的IndexAttribute


5
索引是数据库的概念,而不是实体模型的概念。即使您可以通过属性或流畅API指定索引,它在应用程序中实际上并不会做任何事情。它只是EF在创建数据库时使用的指令。我认为这样的指令应该出现在代码优先迁移中,这完全涉及操作数据库架构。 - JC Ford
11个回答

108

2017年10月26日,Entity Framework 6.2 正式发布。它通过Fluent API 方便地定义索引。如何使用已经在6.2测试版中宣布

现在,您可以使用HasIndex()方法,然后是IsUnique()如果应该是唯一索引。

这里是一个小比较(之前/之后)的例子:

// before 
modelBuilder.Entity<Person>()
        .Property(e => e.Name)
        .HasColumnAnnotation(
            IndexAnnotation.AnnotationName, 
            new IndexAnnotation(new IndexAttribute { IsUnique = true }));

// after
modelBuilder.Entity<Person>()
    .HasIndex(p => p.Name)
    .IsUnique();

// multi column index
modelBuilder.Entity<Person>()
    .HasIndex(p => new { p.Name, p.Firstname })
    .IsUnique();

您也可以使用.IsClustered()将索引标记为聚集。


编辑 #1

添加了一个多列索引的示例,以及如何将索引标记为聚集的附加信息。


编辑 #2

额外的信息是,在EF Core 2.1中,现在与EF 6.2完全相同。
这里是MS Doc文章作为参考。


太好了!我想如果我有多列索引,它应该是这样的:.HasIndex(p => new {p.Name, p.Xyz}) - Valo
哦,抱歉,当然应该是“new”。我会修复它。 - ChW
请问您能否演示如何在Core 2.x中编写相同的代码? - user3417479
据我所知,应该是与“after”和“multi column index”下显示的代码相同。 - ChW
嗨,我想使用Roslyn部分添加HasIndex方法,你能帮忙吗? - Karthic G

87
目前,针对通过流畅API创建索引没有 "一流支持",但是您可以通过流畅API将属性标记为具有来自注释API的属性。这将允许您通过流畅接口添加 Index 属性。
以下是来自EF问题站点工作项的一些示例。
在单个列上创建索引:
modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new IndexAttribute()));

单列上的多个索引:

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new[]
            {
                new IndexAttribute("Index1"),
                new IndexAttribute("Index2") { IsUnique = true }
            }));

多列索引:

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty1)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new IndexAttribute("MyIndex", 1)));

modelBuilder.Entity<MyEntity>()
    .Property(e => e.MyProperty2)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName, 
        new IndexAnnotation(new IndexAttribute("MyIndex", 2)));

使用上述技术将会导致在下一次迁移生成时,在您的Up()函数中自动为您创建.CreateIndex()调用(或者如果您不使用迁移,则会在数据库中自动创建)。

4
可能会为该列添加索引,但不会删除已创建在主键上的聚集索引。hasKey 方法在默认情况下会为主键创建聚集索引,该聚集索引必须在迁移文件中明确指定以便被删除,可以通过在 .Primarykey(x=>x.id,clustered:false) 方法中声明 clustered:false 来实现。 - Joy
8
我尝试了HasAnnotation方法,但是没有这样的方法。不过,我找到了一个名为HasColumnAnnotation的方法,它接受你所提供的参数。你需要更新你的答案吗?还是我弄错了? - Hakan Fıstık
@HakamFostok 我直接从 EF 网站上拿了这个例子。也许在某个版本中名称已更改,或者原始版本中有错别字。 - Scott Chamberlain
3
请查看以下链接底部的设计会议记录:“将HasAnnotation重命名为HasColumnAnnotation(以及代码库中其他相关位置)。”http://entityframework.codeplex.com/wikipage?title=Design%20Meeting%20Notes%20%E2%80%93%20January%2016%2C%202014 - Zac Charles

36

我创建了一些扩展方法并将它们封装在一个NuGet包中,使这个过程变得更加容易。

安装EntityFramework.IndexingExtensions NuGet包。

然后你可以执行以下操作:

public class MyDataContext : DbContext
{
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Entity<Customer>()
        .HasIndex("IX_Customers_Name",          // Provide the index name.
            e => e.Property(x => x.LastName),   // Specify at least one column.
            e => e.Property(x => x.FirstName))  // Multiple columns as desired.

        .HasIndex("IX_Customers_EmailAddress",  // Supports fluent chaining for more indexes.
            IndexOptions.Unique,                // Supports flags for unique and clustered.
            e => e.Property(x => x.EmailAddress)); 
  }
}

项目和源代码在这里。享受!


我非常喜欢这个包,但是在使用 up 脚本脚手架后,索引名称有时会丢失。只有当我的索引中使用了 4 个或更多属性时,它才会出现。我正在使用 EF 6.1.3。 - Mixxiphoid
@Mixxiphoid - 请你在这里记录问题,并提供支持细节,好吗?另外,请确保你使用的是1.0.1版本,因为1.0.0版本存在一个错误 - Matt Johnson-Pint
我有1.0.1版本。我会记录问题,但现在无法这样做。 - Mixxiphoid
如何将索引参与列的顺序添加到降序?默认情况下,.HasIndex("IX_Customers_EmailAddress", IndexOptions.Unique, ...) 为索引中所有参与列创建升序。 - GDroid
@GDroid - 很遗憾,EF的IndexAttribute类没有暴露这个功能,所以我不能在我的库中包含它。 - Matt Johnson-Pint

27
从EF 6.1开始支持属性[Index]
使用[Index(IsUnique = true)]创建唯一索引。
这里是Microsoft提供的链接
public class User 
{ 
    public int UserId { get; set; } 

    [Index(IsUnique = true)] 
    [StringLength(200)] 
    public string Username { get; set; } 

    public string DisplayName { get; set; } 
}

2
虽然这可能在理论上回答了问题,但最好将回答的关键部分包含在此处,并提供参考链接。 - Enamul Hassan
@manetsus 好的。我添加了一个代码片段以反映更改。 - Darie Dorlus
3
否则你会看到一个“is of a type that is invalid for use as a key column in an index”异常,因此需要字符串长度。我的同事更喜欢在上下文中使用modelBuilder解决方案,这样不会使您的User类变得混乱,我认为这是合理的。 - andrew pate
多列唯一性索引怎么办?拥有多列唯一键索引非常普遍... - enorl76
1
@enorl76 这也是被支持的。对于每一列,您需要使用类似以下的属性, [Index("IX_BlogIdAndRating", 2)] public int Rating { get; set; } [Index("IX_BlogIdAndRating", 1)] public int BlogId { get; set; }这里是来自Microsoft的参考。 - Darie Dorlus
@DarieDorlus 是的,谢谢。基本上你的意思是只要在至少一个索引属性上IsUnique=true,并且在所有索引属性上索引名称匹配,那么这些列就会被添加到唯一键约束索引中。这就是我最终在本地发现的。 - enorl76

26

没有明确的名称:

[Index]
public int Rating { get; set; } 

有一个特定的名称:

[Index("PostRatingIndex")] 
public int Rating { get; set; }

索引似乎已被弃用 :( - Hamed Zakery Miab
1
@HamedZakeryMiab 你使用的是哪个版本的Entity Framework?索引并未被弃用。 - Hugo Hilário
对不起,我忘记包含 EntityFramework。它包含在那个程序集中。只是对 NS 感到困惑。 - Hamed Zakery Miab
@HamedZakeryMiab 是的,那真的很令人困惑!我以为那是 System.DataAnnotations 的一部分!它绝对是 Entity Framework 包。 - AlbatrossCafe
3
你注意到了吗?问题中包含以下语句:“而不是使用新的IndexAttribute”。 - Hakan Fıstık

8

实体框架 6

Property(c => c.MyColumn)
        .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_MyIndex")));

然后添加使用:

using System.Data.Entity.Infrastructure.Annotations;
using System.ComponentModel.DataAnnotations.Schema;

7

3
nvarchar 的最大键长度为 900 字节,varchar 的最大键长度为 450 字节。如果您正在使用 Code First,则字符串属性将是 nvarchar 类型,并且应该在其上包含 "StringLength" 属性,如 [[StringLength(450)]]。 - dunwan
从EF 6.1开始,这就是正确的答案。https://learn.microsoft.com/en-us/ef/ef6/modeling/code-first/data-annotations?redirectedfrom=MSDN#index - Chris Schaller

3

如果您不想在 POCO 上使用属性,那么您可以按照以下方式进行:

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

你可以在自定义的DbInitializer派生类中执行此语句。但我不知道任何使用Fluent API的方法来执行此操作。

2
当然,Mert。目前我正在使用迁移,在Up()方法中,您还可以放置:CreateIndex("dbo.Table1", "Column1", true, "Column1_IX"),在Down()方法中使用DropIndex(("dbo.Table1", "Column1_IX")。我只是希望他们也添加了一个流畅的API... - Valo

2

您可以使用以下其中之一

// 索引

 this.HasIndex(e => e.IsActive)
            .HasName("IX_IsActive");

或者
  this.Property(e => e.IsActive).HasColumnAnnotation(
            "Index",
            new IndexAnnotation(new IndexAttribute("IX_IsActive")));

1

我编写了一个扩展方法,用于流畅地使用EF以避免额外的代码:

public static PrimitivePropertyConfiguration HasIndexAnnotation(
    this PrimitivePropertyConfiguration primitivePropertyConfiguration, 
    IndexAttribute indexAttribute = null
    )
{
    indexAttribute = indexAttribute ?? new IndexAttribute();

    return primitivePropertyConfiguration
        .HasColumnAnnotation(
            IndexAnnotation.AnnotationName, 
            new IndexAnnotation(indexAttribute)
        );
}

然后像这样使用它:
Property(t => t.CardNo)
    .HasIndexAnnotation();

或者如果索引需要一些配置:

Property(t => t.CardNo)
    .HasIndexAnnotation(new IndexAttribute("IX_Account") { IsUnique = true });

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