使用 Entity Framework 6、自定义代码优先迁移和 CSharpMigrationCodeGenerator

Using Entity Framework 6, Custom Code-First Migrations & CSharpMigrationCodeGenerator

我最近开始使用 Entity Framework 6 的代码优先自定义迁移。它运行良好,但我想做的一件事是在尝试重命名索引时生成一对 CreateIndex()DropIndex() 语句,而不是像默认 CSharpMigrationCodeGenerator想。

例如,我目前在流式映射中使用数据注释来重命名索引,如下所示:

Property(x => x.TeacherId).HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute("IX_Students_TeacherId")));

这里的问题是,默认情况下,当我添加新的迁移以捕获对模型的更改时,EF6 想要生成以下代码:

using System;
using System.Data.Entity.Migrations;

namespace MyApp.Migrations
{
    public partial class RenameIndexTest : DbMigration
    {
        public override void Up()
        {
            // BAD: [RenameIndex] will generate a "EXEC sp_rename" statement.
            RenameIndex(table: "dbo.Students", name: "IX_TeacherId", newName: "IX_Students_TeacherId");
        }

        public override void Down()
        {
            RenameIndex(table: "dbo.Students", name: "IX_Students_TeacherId", newName: "IX_TeacherId");
        }
    }
}

但我真正需要 EF6 生成的是:

using System;
using System.Data.Entity.Migrations;

namespace MyApp.Migrations
{
    public partial class RenameIndexTest : DbMigration
    {
        public override void Up()
        {
            // GOOD: We generate separate SQL statements to drop & add the index.
            DropIndex(table: "dbo.Students", name: "IX_TeacherId");
            CreateIndex(table: "dbo.Students", name: "IX_Students_TeacherId", column: "TeacherId");
        }

        public override void Down()
        {
            DropIndex(table: "dbo.Students", name: "IX_Students_TeacherId");
            CreateIndex(table: "dbo.Students", name: "IX_TeacherId", column: "TeacherId");
        }
    }
}

我们的数据团队硬性要求开发人员在重命名索引时使用 T-SQL DROP/CREATE 语句。到目前为止,我还没有找到一种方法来覆盖 RenameIndex() 语句的行为,使用使用 CSharpMigrationCodeGenerator 作为其基础 class 的自定义 class,因为 RenameIndexOperation class 没有关于在其上创建索引的列的任何信息。

这是我自己所能做到的:

namespace MyApp.Migrations
{
    internal class CustomCSharpMigrationCodeGenerator : CSharpMigrationCodeGenerator
    {
        protected override string Generate(IEnumerable<MigrationOperation> operations, string @namespace, string className)
        {
            var customizedOperations = new List<MigrationOperation>();

            foreach (var operation in operations)
            {
                if (operation is RenameIndexOperation)
                {
                    var renameIndexOperation = operation as RenameIndexOperation;

                    var dropIndexOperation = new DropIndexOperation(operation.AnonymousArguments)
                    {
                        Table = renameIndexOperation.Table,
                        Name = renameIndexOperation.Name
                    };

                    var createIndexOperation = new CreateIndexOperation(operation.AnonymousArguments)
                    {
                        Table = renameIndexOperation.Table,
                        Name = renameIndexOperation.NewName,

                        // HELP: How do I get this information about the existing index?
                        // HELP: How do I specify what columns the index should be created on?
                        IsUnique = false,
                        IsClustered = false
                    };

                    // Do not generate a RenameIndex() statement; instead, generate a pair of DropIndex() and CreateIndex() statements.
                    customizedOperations.Add(dropIndexOperation);
                    customizedOperations.Add(createIndexOperation);
                }
                else
                {
                    customizedOperations.Add(operation);
                }
            }

            return base.Generate(customizedOperations, @namespace, className);
        }
    }
}

这有意义吗?更重要的是,有人对如何进行有任何建议或想法吗?无论哪种方式,提前致谢!

我正在关闭这个问题。我永远无法完全按照我的意愿去做...这不是一个破坏者,我只是希望 EF6 有某种方式(轻松地)对正在创建的索引的名称施加控制。

IIRC,我按照 @steve-greene 的建议做了,并使用 Sql() 方法手动指定了索引的名称。