我想在多个列上放置索引(如本问题所述),但其中一个属性是导航属性,模型中没有外键属性。
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"必须是不可为null的值类型以便将其用作泛型类型或方法中的参数"T"'System.Data.Entity.ModelConfiguration.Configuration.StructureTypeConfiguration.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
)
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});
使用C#和流利的语法必须做几件事才能实现这一点。
-
将ShopId(外键添加到User类。EF会理解这是Shop导航属性的外键,因此不需要执行更多操作
public virtual Shop Shop { get; set; } public int ShopId { get; set; }
-
在建模器索引配置中使用x.ShopId
modelBuilder.Entity<User>().Property(x => x.ShopId).AddMultiColumnIndex("UX_User_EmailShopId", 1, unique: true);
-
在OnModelCreating中为User.Email属性配置添加MaxLength约束。这是因为索引列的最大值可以是900字节,而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添加到User类中,可以在迁移UP方法中添加索引创建。
CreateIndex("Users", new[] { "Email", "ShopId" }, true, "UX_User_EmailShopId");
如果没有在模型中定义shop_id,就无法使用fluent api。
如果您注意到,您正在使用DbModelBuilder,它是指模型,即代码中"定义"的模型。流利的API只走一条路,从代码到数据库,但不是相反。
尽管如此,您仍然可以在不向模型添加shop_id的情况下创建索引,但使用纯SQL,而不是流畅的API
你可以使用注释来完成。你的用户类需要像,但只有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; }
}