【问题标题】:EF Code First - Multiple column index with navigation propertyEF Code First - 具有导航属性的多列索引
【发布时间】:2016-10-10 03:39:01
【问题描述】:

我想在多个列上放置索引(如this question 中所述),但其中一个属性是模型中没有外键属性的导航属性。

TL;DR 在底部。


我的模特:

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);
    }
}

我使用了一个扩展(基于前面提到的问题):

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);
    }
}

我希望创建一个索引如下:

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);

但这在构建迁移时给了我一个错误:

System.InvalidOperationException: 'Shop' 类型已经存在 配置为实体类型。它不能重新配置为复合体 输入。

当我尝试按如下方式添加索引时,会出现另一个错误:

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

类型“My.NameSpace.Shop”必须是不可为空的值类型 为了将其用作泛型类型或方法中的参数“T” 'System.Data.Entity.ModelConfiguration.Configuration.StructuralTypeConfiguration.Property(System.Linq.Expressions.Expression>)


TL;DR

所以我的问题是,当我的模型中没有定义 shop_id(我不想定义它)时,如何使用 fluent-api 在电子邮件和商店(id)组合上添加索引?

生成的 SQL 应该类似于:

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

【问题讨论】:

    标签: c# entity-framework ef-code-first entity-framework-migrations ef-fluent-api


    【解决方案1】:

    TL&DR:如果你想在 FLUENT Pattern 中有多个索引,你可以这样做:

    You can now (EF core 2.1) use the fluent API as follows:
    
    modelBuilder.Entity<ClassName>()
                .HasIndex(a => new { a.Column1, a.Column2});
    

    【讨论】:

      【解决方案2】:

      您必须做几件事才能使用 C# 和流畅的语法来实现这一目标。

      1. 添加 ShopId(User 类的外键。EF 会理解这是 Shop 导航属性的外键,因此不需要更多操作

        public virtual Shop Shop { get; set; }
        public int ShopId { get; set; }
        
      2. 在模型构建器索引配置中使用 x.ShopId

        modelBuilder.Entity<User>().Property(x => x.ShopId).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);
        
      3. 在 OnModelCreating 中的 User.Email 属性配置上添加 MaxLength 约束。这是因为index columns can max be 900 bytes 和 C# 中的常规公共字符串在 sql server 中转换为 varchar(max)。

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

      所以你最终得到的是 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);
      }
      

      这些小的更改将导致在 SQL-server 中创建此索引:

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

      或者使用迁移,并且不想将“FK”属性 ShopId 添加到您的用户类中,您可以在迁移 UP 方法中添加索引创建。

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

      【讨论】:

      • 我确实忘了提到HasmaxLength,但我的模型中确实有它。但我的问题的重点是如何在没有外键属性 int Shopid 的情况下做到这一点。
      • 您的答案与我给出的链接问题中的答案几乎相同。重点是在没有 key 属性的情况下做到这一点。
      • 显然没有很好地阅读您的问题。如果使用迁移并且不向模型添加 ShopId 属性,则添加了替代方法。
      【解决方案3】:

      如果没有在模型中定义 shop_id,你不能使用 fluent-api 来做到这一点。

      如果您注意到,您正在使用 DbModelBuilder,它是指模型,即代码中“定义”的模型。 fluent API 只有一种方式,从代码到数据库,而不是相反。

      您仍然可以在不向模型中添加 shop_id 而是使用纯 SQL 而不是流式 API 的情况下创建索引

      您可以使用注释来做到这一点。您的 User 类需要是 like ,但仅限于 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; }
      }
      

      【讨论】:

      • 我希望使用fluent-api,并且模型中没有属性ShopId。所以没有快乐。
      猜你喜欢
      • 2014-01-20
      • 2017-06-26
      • 1970-01-01
      • 2016-03-16
      • 1970-01-01
      • 1970-01-01
      • 2022-07-18
      • 2016-01-09
      • 1970-01-01
      相关资源
      最近更新 更多