Entity Framework 6.1(代码优先)增加了通过IndexAttribute
添加索引的可能性。该属性采用一个参数来指定索引应该是聚簇还是非聚簇。
同时,AFAIK,实体框架要求每个实体都有一个主键(使用KeyAttribute
注释),并且该主键始终创建为集群键。
因此,只要我将IndexAttribute
与IsClustered = true
一起使用,就会出现错误,因为由于密钥,已经 聚集索引。
那么,如何使用IndexAttribute
创建不是主键的聚簇索引? IsClustered
的{{1}}属性是否可用?
(对于更多上下文:我正在映射一个仅用于通过LINQ查询进行读取的表。我不需要实际插入,更新或删除该表中的实体。因此,我不需要一个主键。理想情况下,我想要一个没有主键的表,但是有一个非唯一的聚簇索引,专为阅读而优化。)
修改(2014-04-11):另请参阅https://entityframework.codeplex.com/workitem/2212。
答案 0 :(得分:9)
表上只能有一个聚簇索引,默认情况下,实体框架/ Sql Server将其放在主键上。
那么索引上的IsClustered
属性用作主键是什么用途?好问题! (1)
这堂课:
public class Blog
{
[Key()]
public int Id { get; set; }
[MaxLength(256)]//Need to limit size of column for clustered indexes
public string Title { get; set; }
[Index("IdAndRating", IsClustered = true)]
public int Rating { get; set; }
}
将生成此迁移:
public override void Up()
{
CreateTable(
"dbo.Blogs",
c => new
{
Id = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 256),
Rating = c.Int(nullable: false),
});
.PrimaryKey(t => t.Id)
.Index(t => t.Rating, clustered: true, name: "IdAndRating");
}
改变迁移到此:
public override void Up()
{
CreateTable(
"dbo.Blogs",
c => new
{
Id = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 256),
Rating = c.Int(nullable: false),
});
CreateIndex("dbo.Blogs",
new[] { "Rating", "Title" },
clustered: true,
name: "IdAndRating");
}
这应该创建没有主键但在其他列上使用聚簇索引的表
修改强> 在您不需要插入,更新或删除数据的场景中,您不需要完整的实体,可以使用raw sql queries填充类。您需要将自己的sql添加到迁移中以创建表,因为EF不会自动化它,但这意味着您可以根据需要创建表和索引。
答案 1 :(得分:5)
您可以从SqlServerMigrationSqlGenerator派生自己的类 并在那里改变pk的创建:
public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(System.Data.Entity.Migrations.Model.AddPrimaryKeyOperation addPrimaryKeyOperation)
{
addPrimaryKeyOperation.IsClustered = false;
base.Generate(addPrimaryKeyOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.CreateTableOperation createTableOperation)
{
createTableOperation.PrimaryKey.IsClustered = false;
base.Generate(createTableOperation);
}
protected override void Generate(System.Data.Entity.Migrations.Model.MoveTableOperation moveTableOperation)
{
moveTableOperation.CreateTableOperation.PrimaryKey.IsClustered = false;
base.Generate(moveTableOperation);
}
答案 2 :(得分:2)
以下是基于raditch的答案的代码,对我有用。这允许主键默认为群集。它可能需要调整,因为我们不使用内置的ef迁移来实际处理更改
public class NonClusteredPrimaryKeySqlMigrationSqlGenerator : SqlServerMigrationSqlGenerator
{
public override IEnumerable<System.Data.Entity.Migrations.Sql.MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
var primaries = migrationOperations.OfType<CreateTableOperation>().Where(x => x.PrimaryKey.IsClustered).Select(x => x.PrimaryKey).ToList();
var indexes = migrationOperations.OfType<CreateIndexOperation>().Where(x => x.IsClustered).ToList();
foreach (var index in indexes)
{
var primary = primaries.Where(x => x.Table == index.Table).SingleOrDefault();
if (primary != null)
{
primary.IsClustered = false;
}
}
return base.Generate(migrationOperations, providerManifestToken);
}
}
public class EFCustomConfiguration : DbConfiguration
{
public EFCustomConfiguration()
{
SetMigrationSqlGenerator("System.Data.SqlClient", () => new NonClusteredPrimaryKeySqlMigrationSqlGenerator());
}
}
答案 3 :(得分:1)
告诉你真相 - IndexAttribute是完全多余的,不适合专业发展。他们缺乏核心功能,专注于那些毫无意义的东西。
为什么呢?因为它永远不会,也应该像构建脚本一样灵活。聚簇索引只是一件事 - 接下来我会想念的是一个过滤索引,主要是在一个字段上的“非空的唯一索引,非唯一索引为null”的形式,我碰巧经常使用它来选择唯一代码(因为在SQL Server中,NULL等于SQL生成中的另一个NULL,因此在唯一索引中一次只能有一个NULL)。
如果我是你,我将远离数据库生成和迁移 - 并使用经典的设置/迁移脚本方法。这是你可以进行更复杂的多步骤迁移而不会丢失的东西。除了最基本的场景外,EF不会处理任何事情 - 在这些方面我怀疑这已经足够了。可能是因为我也主要在大型数据库上工作,我们非常谨慎地进行更改 - 添加索引可能需要一些时间才能达到数十亿行的两位数(!0 +)。
我希望开发人员能够专注于一些无法轻松解决的缺失领域,比如性能,如核心ORM功能(更好的枚举,二级缓存,批量删除API,更多性能插入和更新 - 所有可行的事情)。 Code First很不错。 Code First生成和维护数据库是非常简单的场景之外的痛苦。
答案 4 :(得分:0)
如果有人对此主题感兴趣,我会在此写下我的解决方案。 下面的代码更改了add-migration命令的输出。
public class CustomMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
protected override void Generate(CreateTableOperation createTableOperation, IndentedTextWriter writer)
{
if (createTableOperation.Columns.Any(x => x.Name == "Index") &&
createTableOperation.Columns.Any(x => x.Name == "Id"))
{
if (createTableOperation.PrimaryKey != null)
{
createTableOperation.PrimaryKey.IsClustered = false;
}
}
base.Generate(createTableOperation, writer);
}
}
您可以在迁移配置中注册此生成器:
internal sealed class Configuration : DbMigrationsConfiguration<Ubrasoft.Freeman.WebApi.Db.MainDb>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
CodeGenerator = new CustomMigrationCodeGenerator();
SetSqlGenerator("System.Data.SqlClient", new CustomMigrationSqlGenerator());
}
protected override void Seed(Ubrasoft.Freeman.WebApi.Db.MainDb context)
{
}
}
这是生成的迁移代码:
public override void Up()
{
CreateTable(
"Tenant.Tenant",
c => new
{
Id = c.Guid(nullable: false),
TenantNo = c.Byte(nullable: false),
Name = c.String(nullable: false, maxLength: 20),
Index = c.Int(nullable: false, identity: true),
CreatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"),
UpdatedDate = c.DateTime(nullable: false, precision: 0, storeType: "datetime2"),
IsDeleted = c.Boolean(nullable: false),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.Index, unique: true, clustered: true);
}
Here是关于自定义MigrationCodeGenerator的文章。