简体   繁体   English

EF Code First - 具有导航属性的多列索引

[英]EF Code First - Multiple column index with navigation property

I want to put an index on multiple columns (like stated in this question ), but one of the properties is a navigation property without a foreign key property in the model.我想在多个列上放置索引(如本问题所述),但其中一个属性是导航属性,模型中没有外键属性。

TL;DR at the bottom. TL; DR在底部。


My model:我的型号:

public class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    public virtual Shop Shop { get; set; }
}

public class Shop
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Context : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Shop> Shops { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasRequired(u => u.Shop).WithMany();
        modelBuilder.Entity<User>().Property(u => u.Email).HasMaxLength(256);
    }
}

I use an extension (based on the for mentioned question):我使用了一个扩展(基于提到的问题):

internal static class ModelBuilderExtensions
{
    public static StringPropertyConfiguration AddMultiColumnIndex(this StringPropertyConfiguration config, string indexName, int columnOrder, bool unique = false, bool clustered = false)
    {
        var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = unique, IsClustered = clustered };
        var indexAnnotation = new IndexAnnotation(indexAttribute);

        return config.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
    }

    public static PrimitivePropertyConfiguration AddMultiColumnIndex(this PrimitivePropertyConfiguration property, string indexName, int columnOrder, bool unique = false, bool clustered = false)
    {
        var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = unique, IsClustered = clustered };
        var indexAnnotation = new IndexAnnotation(indexAttribute);

        return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation);
    }
}

I wish to create an index as follows:我希望创建一个索引如下:

modelBuilder.Entity<User>().Property(x => x.Email).AddMultiColumnIndex("UX_User_EmailShopId", 0, unique: true);
modelBuilder.Entity<User>().Property(x => x.Shop.Id).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);

But this gives me an error when scaffolding a migration:但这在构建迁移时给了我一个错误:

System.InvalidOperationException: The type 'Shop' has already been configured as an entity type. System.InvalidOperationException: 类型“商店”已配置为实体类型。 It cannot be reconfigured as a complex type.它不能重新配置为复杂类型。

When I try to add the index as follows it gives another error:当我尝试按如下方式添加索引时,它给出了另一个错误:

modelBuilder.Entity<User>().Property(x => x.Shop).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);

The type 'My.NameSpace.Shop' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)类型“My.NameSpace.Shop”必须是不可为空的值类型,以便将其用作泛型类型或方法“System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System. Linq.Expressions.Expression>)


TL;DR TL; 博士

So my question is, how do I add an index using the fluent-api on the email and shop (id) combination when the shop_id is not defined in my model (and I don't want to define it)?所以我的问题是,当我的模型中没有定义 shop_id 时(我不想定义它),我如何使用 fluent-api 在电子邮件和商店 (id) 组合上添加索引?

The resultant SQL should look something like:生成的 SQL 应该类似于:

CREATE UNIQUE NONCLUSTERED INDEX [UX_User_EmailShopId] ON [dbo].[User]
(
    [Email] ASC,
    [Shop_Id] ASC
)

TL&DR : If you want to have multiple indexes in the FLUENT Pattern, you can do the following: TL&DR :如果你想在 FLUENT 模式中有多个索引,你可以执行以下操作:

You can now (EF core 2.1) use the fluent API as follows:

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2});

There is a couple of things you must do to achieve this using C# and fluent syntax.要使用 C# 和流畅的语法实现这一点,您必须做几件事。

  1. Add ShopId (foreign key to User class. EF will understand that this is the foreign key for the Shop navigation property, so no more actions needed添加 ShopId(用户类的外键。EF 会理解这是 Shop 导航属性的外键,因此不需要更多操作

    public virtual Shop Shop { get; set; } public int ShopId { get; set; }
  2. Use x.ShopId in you modelbuilder index-config在您的模型构建器索引配置中使用 x.ShopId

     modelBuilder.Entity<User>().Property(x => x.ShopId).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);
  3. Add MaxLength constraint on your User.Email property config in OnModelCreating.在 OnModelCreating 中对 User.Email 属性配置添加 MaxLength 约束。 This because index columns can max be 900 bytes , and a regular public string in C# translates into a varchar(max) in sql server.这是因为索引列最多可以是 900 个字节,并且 C# 中的常规公共字符串在 sql server 中转换为 varchar(max)。

     modelBuilder.Entity<User>().Property(c => c.Email).HasMaxLength(255).AddMultiColumnIndex("UX_User_EmailShopId", 0, unique: true);;

So what you end up with is one extra property in the User class and this OnModelCreating method in your dbcontext:所以你最终得到的是 User 类中的一个额外属性和 dbcontext 中的这个 OnModelCreating 方法:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().HasRequired(u => u.Shop).WithMany();
    modelBuilder.Entity<User>().Property(x => x.Email).HasMaxLength(255).AddMultiColumnIndex("UX_User_EmailShopId", 0, unique: true);
    modelBuilder.Entity<User>().Property(x => x.ShopId).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);
}

These small changes will result in this index being created in SQL-server:这些小的更改将导致在 SQL-server 中创建此索引:

CREATE UNIQUE NONCLUSTERED INDEX [UX_User_EmailShopId]
ON [dbo].[Users]([Email] ASC, [ShopId] ASC);

Alternatively using migrations, and not wanting to add the "FK" property ShopId to you User class, you could add the index creation in the migration UP method.或者使用迁移,并且不想将“FK”属性 ShopId 添加到您的 User 类,您可以在迁移 UP 方法中添加索引创建。

CreateIndex("Users", new[] { "Email", "ShopId" }, true, "UX_User_EmailShopId");

You cannot do it using fluent-api without shop_id is defined in the model.如果没有在模型中定义 shop_id,您不能使用 fluent-api 来做到这一点。

If you noticed, you are using DbModelBuilder, which is refer to model, ie the models 'defined' in your code.如果您注意到,您正在使用 DbModelBuilder,它是指模型,即代码中“定义”的模型。 fluent API goes only one way, from code to DB, but not the opposite. fluent API 只有一种方式,从代码到数据库,但不是相反。

Still you can create the index without add shop_id to the model but with pure SQL , not the fluent API您仍然可以在不向模型添加 shop_id 的情况下创建索引,但使用纯 SQL 而不是 fluent API

You could do it using annotation.您可以使用注释来做到这一点。 Your User class need to be like , but only with with EF 6.1您的 User 类需要像 ,但仅适用于 EF 6.1

public class User
{
    [Index("UX_User_EmailShopId", 1, IsUnique =  true)]
    public int Id { get; set; }

    [Index("UX_User_EmailShopId", 2, IsUnique = true)]
    public string Email { get; set; }

    [Index("UX_User_EmailShopId", 3, IsUnique = true)]
    public int ShopId { get; set; }

    [ForeignKey("ShopId")]
    public virtual Shop Shop { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM