EF Code First - 具有导航属性

时间:2016-06-09 13:00:22

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

我想在多个列上放置索引(如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:“商店”类型已经存在   配置为实体类型。它不能被重新配置为复杂的   类型。

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

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&GT)

TL; DR

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

结果SQL应该类似于:

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

3 个答案:

答案 0 :(得分:0)

使用C#和流利的语法,您必须做一些事情才能实现这一目标。

  1. 添加ShopId(用户类的外键.EF会理解这是Shop导航属性的外键,因此不再需要其他操作

    public virtual Shop Shop { get; set; }
    public int ShopId { get; set; }
    
  2. 在模型构建器index-config

    中使用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);;
    
  4. 所以你最终得到的是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);
    

    或者使用迁移,而不想添加&#34; FK&#34;属性ShopId给你用户类,你可以在迁移UP方法中添加索引创建。

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

答案 1 :(得分:0)

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

答案 2 :(得分:-1)

如果未在模型中定义shop_id,则无法使用fluent-api进行操作。

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

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

您可以使用注释来完成。您的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; }
}