如何使用Fluent API使用ASC / DESC排序在多列上添加索引?

Max*_*ime 4 indexing entity-framework ef-code-first entity-framework-6 ef-fluent-api

我有一个使用实体框架6-代码优先方法的MVC ASP.NET应用程序。

使用Fluent API,如何使用ASC / DESC排序在每列不同的多个列上添加索引?

我已经看到了许多使用多个列的示例,但是没有办法设置索引中列的排序顺序。

Table
-----
Id
Type
DateFor
DateCreated
Value
Run Code Online (Sandbox Code Playgroud)

我要在以下列上建立索引:Type(ASC),DateFor(Desc),DateCreated(Desc)。

Sae*_*jad 8

EF 7 支持索引排序顺序!

使用 EF 7,您可以在多个列上有多个排序顺序。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasIndex(b => new { b.Url, b.Rating })
        .IsDescending(false, true); //Added in EF 7.0
}
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下是这样的:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<YourModel>()
        .HasIndex(b => new { b.Type, b.DateFor, b.DateCreated })
        .IsDescending(false, true, true); 
}
Run Code Online (Sandbox Code Playgroud)


Max*_*ime 5

简短的答案: Entity Framework 6不允许具有不同排序的多个索引。

长答案:可能无法直接做到,但可以通过一些调整来实现。经过大量阅读后,我发现创建一个继承IndexAnnotation并添加SortOrder属性的新类真的很复杂。

我发现实现此目标的最简单方法是查看可以调整哪些现有属性以实现多索引排序。使用Name属性可以做到这一点,因为它是一个字符串。您可以直接在名称中添加排序索引,稍后在生成SQL代码时对其进行拦截。

因此,假设我需要像这样索引属性:

  • 类型(ASC)
  • DateFor(描述)
  • DateCreated(Desc)

然后,我将命名我的索引,后跟一个分隔符(:)和排序顺序。它看起来像这样:

var indexName = "IX_Table:ASC,DESC,DESC";

具有多个字段的索引如下所示:

this.Property(t => t.Type)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new[]
            {
                new IndexAttribute(indexName) { Order = 1 }
            }
        )
    );

this.Property(t => t.DateFor)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new[]
            {
                new IndexAttribute(indexName) { Order = 2 }
            }
        )
    );

this.Property(t => t.DateCreated)
    .HasColumnAnnotation(
        IndexAnnotation.AnnotationName,
        new IndexAnnotation(new[]
            {
                new IndexAttribute(indexName) { Order = 3 }
            }
        )
    );
Run Code Online (Sandbox Code Playgroud)

现在,我们必须创建一个自定义SQL生成类,以便生成正确的SQL代码来解析“调整后的”索引名称:

public class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
    protected override void Generate(CreateIndexOperation createIndexOperation)
    {
        using (var writer = Writer())
        {
            writer.Write("CREATE ");

            if (createIndexOperation.IsUnique)
            {
                writer.Write("UNIQUE ");
            }

            if (createIndexOperation.IsClustered)
            {
                writer.Write("CLUSTERED ");
            }
            else
            {
                writer.Write("NONCLUSTERED ");
            }

            string name = createIndexOperation.Name;
            string[] sorts = {};
            if (createIndexOperation.Name.Contains(":"))
            {
                var parts = createIndexOperation.Name.Split(':');

                if (parts.Length >= 1)
                {
                    name = parts[0];
                }
                if (parts.Length >= 2)
                {
                    sorts = parts[1].Split(',');
                }
            }

            writer.Write("INDEX ");
            writer.Write(Quote(name));
            writer.Write(" ON ");
            writer.Write(Name(createIndexOperation.Table));
            writer.Write("(");

            // Add the columns to the index with their respective sort order
            string fields = "";
            if (sorts.Length == 0 || sorts.Length == createIndexOperation.Columns.Count)
            {
                for (int i=0 ; i<createIndexOperation.Columns.Count ; i++)
                {
                    string sort = "ASC";
                    if (sorts.Length == 0)
                    {
                        // Do nothing
                    }
                    else if (sorts[i] != "ASC" && sorts[i] != "DESC")
                    {
                        throw new Exception(string.Format("Expected sort for {0} is 'ASC' or 'DESC. Received: {1}", name, sorts[i]));
                    }
                    else 
                    { 
                        sort = sorts[i];  
                    }

                    fields = fields + Quote(createIndexOperation.Columns[i]) + " " + sort + ",";
                }
                fields = fields.Substring(0, fields.Length - 1);
            }
            else
            {
                throw new Exception(string.Format("The sort (ASC/DEC) count is not equal to the number of fields in your Index ({0}).", name));
            }

            writer.Write(fields);

            writer.Write(")");
            Statement(writer);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,您需要通过编辑Configuration.cs文件来告诉Entity Framework使用新代码生成的方法,而不是默认的方法:

internal sealed class MyConfiguration : DbMigrationsConfiguration<MyContext>
{

    /// <summary>
    /// Constructor
    /// </summary>
    public MyConfiguration()
    {
        // Other stuff here...

        // Index/Unique custom generation (Ascending and Descending)
        SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator());
    }
}
Run Code Online (Sandbox Code Playgroud)

而已。它可能不是最干净的解决方案,但是如果您即时生成实体(如我所做的那样),则将节省大量时间,并避免忘记运行原始SQL。

在这里查看代码

非常感谢Rowan Miller及其博客上的所有文章。这个答案的灵感来自:定制代码优先迁移提供程序