C# 在EntityFramework6.1(非核心)中,如何使用IndexAttribute定义聚集索引?

C# 在EntityFramework6.1(非核心)中,如何使用IndexAttribute定义聚集索引?,c#,entity-framework,indexing,entity-framework-6,C#,Entity Framework,Indexing,Entity Framework 6,实体框架6.1(代码优先)增加了通过索引属性添加索引的可能性。该属性接受一个参数,用于指定索引应为聚集索引还是非聚集索引 同时,AFAIK,Entity框架要求每个实体都有一个主键(用KeyAttribute注释),并且该主键始终创建为聚集键 因此,只要我使用IsClustered=true应用IndexAttribute,我就会得到一个错误,因为由于键的原因,已经存在聚集索引 那么,如何使用IndexAttribute创建不是主键的聚集索引呢?IndexAttribute的IsClustere

实体框架6.1(代码优先)增加了通过
索引属性添加索引的可能性。该属性接受一个参数,用于指定索引应为聚集索引还是非聚集索引

同时,AFAIK,Entity框架要求每个实体都有一个主键(用
KeyAttribute
注释),并且该主键始终创建为聚集键

因此,只要我使用
IsClustered=true
应用
IndexAttribute
,我就会得到一个错误,因为由于键的原因,已经存在聚集索引

那么,如何使用
IndexAttribute
创建不是主键的聚集索引呢?
IndexAttribute
IsClustered
属性是否可用

(更详细地说:我正在映射一个仅用于通过LINQ查询进行读取的表。我不需要从该表中实际插入、更新或删除实体。因此,我根本不需要主键。理想情况下,我想要一个没有主键的表,但具有非唯一的、为读取而优化的聚集索引。)


编辑(2014-04-11):另见。

告诉你真相-指数化属性完全是多余的,不适合专业发展。他们缺乏核心功能,专注于毫无意义的东西

为什么??因为它永远不会也应该像构建脚本一样灵活。聚集索引只是一件事——接下来我会错过的是一个过滤索引,主要是字段上的“非空的唯一索引,非空的唯一索引”形式,我碰巧经常将其用于可选的唯一代码(因为在SQL Server中,NULL等于SQL生成中的另一个NULL,所以在唯一索引中一次只能有一个NULL)

如果我是你,我会远离数据库生成和迁移,使用经典的设置/迁移脚本方法。Thta是一种可以在不丢失ata的情况下执行更复杂的多步骤迁移的方法。EF只处理最基本的场景,在这些方面我怀疑这是否足够。是否可以是beca也可以使用I,并且主要是在大型数据库上工作,在这些数据库中,我们会非常小心地进行更改—当您达到数十亿行的两位数(!0+)时,添加索引可能需要一些时间


我更希望开发人员将注意力集中在她无法轻松解决的一些缺失领域,比如性能,比如核心ORM功能(更好的枚举、二级缓存、批量删除API、更多的性能插入和更新—所有这些都是可行的).Code First很好。Code First生成和维护数据库在非常简单的场景之外非常痛苦。

一个表上只能有一个聚集索引,默认情况下Entity Framework/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");

    }
这将创建不带主键但在其他列上具有聚集索引的表

编辑
在您不需要插入、更新或删除数据的场景中,您不需要完整的实体,您可以使用来填充类。您需要将自己的sql添加到迁移中以创建表,因为EF不会自动创建表,但这意味着您可以根据需要创建表和索引。

您可以派生自己的sql来自SqlServerMigrationSqlGenerator的n类 并在此处更改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);
    }
这里有完整的例子

以下是基于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());
    }
}
公共类非群集PrimaryKeySqlMigrationSqlGenerator:SqlServerMigrationSqlGenerator
{
公共重写IEnumerable生成(IEnumerable迁移操作,字符串providerManifestToken)
{
var primaries=migrationOperations.OfType()。其中(x=>x.PrimaryKey.IsClustered)。选择(x=>x.PrimaryKey.ToList();
var index=migrationOperations.OfType(),其中(x=>x.IsClustered.ToList();
foreach(指数中的var指数)
{
var primary=primaries.Where(x=>x.Table==index.Table).SingleOrDefault();
如果(主!=null)
{
primary.IsClustered=false;
}
}
返回base.Generate(migrationOperations,providerManifestToken);
}
}
公共类EFCustomConfiguration:DbConfiguration
{
公共EFCustomConfiguration()
{
SetMigrationSqlGenerator(“System.Data.SqlClient”,()=>new NonClusteredPrimaryKeySqlMigrationSqlGenerator());
}
}

如果还有人对这个主题感兴趣,我在这里写下我的解决方案。 下面的代码更改了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)
    {

    }
}

这篇文章是关于custom MigrationCodeGenerator的。

不确定“远离迁移”是什么意思。“经典的设置/迁移脚本方法”是什么?当我想要一个唯一的索引时,我会在迁移中使用Sql—有点像这样:或者这是一组脚本,用来更新数据库的版本号。就像过去50年来人们管理数据库版本控制的方式一样。新版本会执行一组脚本,以可控的方式更新数据库。好的,但您不使用迁移以生成这些脚本并处理版本控制?不,永远不会。请参阅,迁移是suuuper原语。希望从具有用户名的字段移动到查找表?必须首先生成表,然后填充表,然后添加字段
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);

    }