我使用EF6
代码第一种方法来创建数据库。当我添加迁移和更新数据库时,它默认为表中的每个外键创建Non-cluster Index
。
我的问题: EF6是否有任何全局设置不能在外键上创建Non-Cluster index
?
我搜索并找到以下解决方案
Solution 1: Remove index line from migration before updating database
解决方案1不适合我,因为我有很多表,我的db
已经创建。手动删除索引创建行需要花费很多时间。
此外,我还使用fluent api
是否有与此问题相关的选项?
答案 0 :(得分:4)
我不相信有一个简单的解决方案,但我知道你可以做些什么:创建一个自定义迁移生成器。
迁移生成器是负责从迁移代码文件创建在数据库上运行的SQL脚本的组件。我假设你有基于截图的SQL Server。在这种情况下,您可以编写一个自定义的sql生成器,它只是覆盖索引创建操作,这样如果索引是非群集的,则不会向脚本写入任何内容:
public class NoIndexGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(CreateIndexOperation createIndexOperation)
{
if (!createIndexOperation.IsClustered)
{
return;
}
}
}
然后,您必须在迁移的Configuration
类中注册此组件:
internal sealed class Configuration : DbMigrationsConfiguration<MyCtx>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Add this line to register the sql generator
this.SetSqlGenerator("System.Data.SqlClient", new NoIndexGenerator());
}
}
现在,如果您运行Add-Migration
,您将拥有一个正常的迁移文件,其中包含CreateIndexOperation。但是如果运行Update-Database
,则不会创建非聚集索引。如果您运行Update-Database -Script
,也可以检查此项。生成的脚本没有非聚集索引。
如果您愿意,您可以在管道中更高,并创建一个自定义C#迁移脚手架。它应用与sql生成器相同的逻辑:
internal class NoIndexMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(CreateIndexOperation createIndexOperation, IndentedTextWriter writer)
{
if (!createIndexOperation.IsClustered)
{
return;
}
}
}
然后,您可以在Configuration
类中注册,如下所示:
internal sealed class Configuration : DbMigrationsConfiguration<MyCtx>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
// Add this line to register the C# code generator
this.CodeGenerator = new NoIndexMigrationCodeGenerator();
}
}
现在,如果您运行Add-Migration
,CreateIndex操作也将从生成的迁移cs文件中消失。
我可能会选择第二个解决方案(对于其他人来说,阅读代码时可能会感到困惑,因为迁移cs文件中有CreateIndex操作,但SQL脚本中没有),但最终它是你的选择:)
如果必须,您可以使用createIndexOperation
方法的Generate()
参数的其他属性来实现更复杂的索引过滤。
如果需要,您还可以覆盖具有DropCreateIndexOperation
类型参数的Generate方法,但是因为索引会被删除,如果存在&#39; drop-if-exists&#39;模式,我不认为这是必要的。
修改强>
虽然上面的代码示例似乎有效,但为了公平并遵循一般的最佳实践和原则,您应该在if语句之后包含对两个生成器中的基本方法的调用。
答案 1 :(得分:4)
嗯,我认为这可能是'如果你拥有的只是一把锤......'那种情况。
我之前给出的答案是有效的(我支持它,因为它非常有趣和令人敬畏),但它可能不是最好的方法。
最近我检查了EF使用生成数据库的所有默认约定,并且有一个负责在FK-s上生成非聚集索引。只需完全删除该约定,问题就解决了:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// using System.Data.Entity.ModelConfiguration.Conventions;
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
}
答案 2 :(得分:0)
尝试使用CSharpMigrationCodeGenerator
之后,我开始思考重写ForeignKeyIndexConvention
的方式。
因此,我通过假设未重写PK命名约定来实施允许跳过在主键列上添加索引的检查。
public class ForeignKeyIndexConventionFix : ForeignKeyIndexConvention
{
private const string Id = "Id";
public override void Apply(AssociationType item, DbModel model)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
if (item.Constraint == null)
{
return;
}
if (item.IsForeignKey)
{
if (IsPrimaryKeyColumn(item.Constraint))
{
return;
}
}
base.Apply(item, model);
}
private static bool IsPrimaryKeyColumn(ReferentialConstraint constraint)
{
IEnumerable<string> dependentColumns = constraint.ToProperties.Select(p => p.Name);
if (dependentColumns.Count() == 1)
{
string dependentColum = dependentColumns.First();
if (dependentColum.Equals(Id, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
}
然后覆盖您的DbContext
类:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Conventions.Remove<ForeignKeyIndexConvention>();
modelBuilder.Conventions.Add<ForeignKeyIndexConventionFix>();
}
调试解决方案时遇到问题-Console.Write
和Debug.Write
没有提供输出-因此我只是将跟踪放在临时位置的某些文本文件中。也许有更好的方法..?
原始实现的代码源有助于弄清楚如何获取相关的列名:https://github.com/dotnet/ef6/blob/master/src/EntityFramework/ModelConfiguration/Conventions/Edm/Db/ForeignKeyIndexConvention.cs