4

Entity Framework 6 - Code First アプローチを使用する MVC ASP.NET アプリケーションがあります。

Fluent API を使用して、列ごとに異なる ASC/DESC ソートで複数の列にインデックスを追加するにはどうすればよいですか?

複数の列を使用する多くの例を見てきましたが、インデックス内の列の並べ替え順序を設定する方法はありません。

Table
-----
Id
Type
DateFor
DateCreated
Value

次の列にインデックスが必要です: Type(ASC)、DateFor(Desc)、DateCreated(Desc)。

4

3 に答える 3

5

簡単な答え: Entity Framework 6 では、並べ替えが異なる複数のインデックスを使用できません。

長い答え:直接行うことはできないかもしれませんが、微調整することで実現できます。IndexAnnotationたくさん読んだ後、プロパティを継承して追加する新しいクラスを作成するのは非常に複雑であることがわかりましたSortOrder

これを達成するために私が見つけた最も簡単な方法は、複数のインデックスの並べ替えを達成するために微調整できる既存のプロパティを確認することでした。プロパティを使用するNameと、文字列として実行できます。ソート インデックスを名前に直接追加し、後で SQL コードを生成するときにインターセプトできます。

したがって、次のようにプロパティにインデックスを付ける必要があると仮定しましょう。

  • タイプ (ASC)
  • 日付 (説明)
  • 作成日(説明)

次に、インデックスに続けて区切り記号 (:) と並べ替え順序を付けます。次のようになります。

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

「調整された」インデックス名を解析するための適切な 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);
        }
    }
}

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

それでおしまい。これは最もクリーンなソリューションではないかもしれませんが、(私のように) その場でエンティティを生成すると、多くの時間を節約でき、生の SQL を実行するのを忘れることがなくなります。

コードはこちら

Rowan Millerと彼のブログのすべての記事に感謝します。この回答は、 Code First Migrations Provider のカスタマイズに触発されました。

于 2015-07-24T16:03:43.813 に答える
2

私は@Maximeの答えが本当に好きですが、それは非常に複雑ですが、そのことを学ぼうとします.

私の解決策は少し簡単で、うまくいくので、誰かに役立つ場合に備えて、ここに追加してください。

移行を手動で編集し、適切なコードを追加することで、これを行うことができました。モデルチェックでは、EFはインデックスが存在するかどうかのみをチェックし、順序はチェックしません。

    public override void Up()
    {
        DropIndex("dbo.MonitoringItemHistory", "IX_ItemDate");
        Sql("ALTER TABLE dbo.MonitoringItemHistory DROP CONSTRAINT [PK_dbo.MonitoringItemHistory]");
        CreateIndex("dbo.MonitoringItemHistory", new[] { "MonitoringItemId", "ChangeTime" }, clustered: true, name: "IX_ItemDate");
        Sql("ALTER TABLE dbo.MonitoringItemHistory ADD CONSTRAINT [PK_dbo.MonitoringItemHistory] PRIMARY KEY NONCLUSTERED (Id)");
    }

    public override void Down()
    {
        Sql("ALTER TABLE dbo.MonitoringItemHistory DROP CONSTRAINT [PK_dbo.MonitoringItemHistory]");
        DropIndex("dbo.MonitoringItemHistory", "IX_ItemDate");
        Sql("ALTER TABLE dbo.MonitoringItemHistory ADD CONSTRAINT [PK_dbo.MonitoringItemHistory] PRIMARY KEY CLUSTERED (Id)");
        CreateIndex("dbo.MonitoringItemHistory", new[] { "MonitoringItemId", "ChangeTime" }, name: "IX_ItemDate");
    }

そして私のDBエンティティコードで:

    [Index("IX_ItemDate", 1, IsClustered = true)]
    public int MonitoringItemId { get; set;}
    [Index("IX_ItemDate", 2, IsClustered = true)]
    public DateTimeOffset ChangeTime { get; set; }
于 2016-05-14T17:32:44.047 に答える