entity framework中多列的唯一键约束

我使用的是Entity Framework 5.0 Code First;

public class Entity { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public string EntityId { get; set;} public int FirstColumn { get; set;} public int SecondColumn { get; set;} } 

我想将FirstColumnSecondColumn之间的组合作为唯一。

例:

 Id FirstColumn SecondColumn 1 1 1 = OK 2 2 1 = OK 3 3 3 = OK 5 3 1 = THIS OK 4 3 3 = GRRRRR! HERE ERROR 

有没有办法做到这一点?

使用Entity Framework 6.1,现在可以这样做:

 [Index("IX_FirstAndSecond", 1, IsUnique = true)] public int FirstColumn { get; set; } [Index("IX_FirstAndSecond", 2, IsUnique = true)] public int SecondColumn { get; set; } 

属性中的第二个参数是您可以指定索引中列的顺序的位置。
更多信息: MSDN

如果您使用Code-First ,则可以实现自定义扩展HasUniqueIndexAnnotation

 using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.Infrastructure.Annotations; using System.Data.Entity.ModelConfiguration.Configuration; internal static class TypeConfigurationExtensions { public static PrimitivePropertyConfiguration HasUniqueIndexAnnotation( this PrimitivePropertyConfiguration property, string indexName, int columnOrder) { var indexAttribute = new IndexAttribute(indexName, columnOrder) { IsUnique = true }; var indexAnnotation = new IndexAnnotation(indexAttribute); return property.HasColumnAnnotation(IndexAnnotation.AnnotationName, indexAnnotation); } } 

然后像这样使用它:

 this.Property(t => t.Email) .HasColumnName("Email") .HasMaxLength(250) .IsRequired() .HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 0); this.Property(t => t.ApplicationId) .HasColumnName("ApplicationId") .HasUniqueIndexAnnotation("UQ_User_EmailPerApplication", 1); 

这将导致这种迁移:

 public override void Up() { CreateIndex("dbo.User", new[] { "Email", "ApplicationId" }, unique: true, name: "UQ_User_EmailPerApplication"); } public override void Down() { DropIndex("dbo.User", "UQ_User_EmailPerApplication"); } 

最终最终在数据库中:

 CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User] ( [Email] ASC, [ApplicationId] ASC ) 

我find了三种方法来解决这个问题。

EntityFramework核心中的唯一索引:

第一种方法:

 protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Entity>() .HasIndex(p => new {p.FirstColumn , p.SecondColumn}).IsUnique(); } 

第二种方法是使用备用密钥来为EF Core创build唯一约束。

例子

一列:

 modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn"); 

多栏:

 modelBuilder.Entity<Entity>().HasAlternateKey(c => new [] {c.FirstColumn, c.SecondColumn}).HasName("IX_MultipleColumns"); 

EF 6及以下:


第一种方法:

 dbContext.Database.ExecuteSqlCommand(string.Format( @"CREATE UNIQUE INDEX LX_{0} ON {0} ({1})", "Entitys", "FirstColumn, SecondColumn")); 

这种方法是非常快速和有用的,但主要的问题是,entity framework不知道这些变化的任何事情!


第二种方法:
我在这篇文章中发现了它,但我并没有自己尝试。

 CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" }, true, "IX_Entitys"); 

这种方法的问题如下:它需要DbMigration,所以如果你没有它,你会怎么做?


第三种方法:
我认为这是最好的,但需要一些时间去做。 我只是告诉你它背后的想法:在这个链接http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a你可以find唯一的关键数据注释的代码&#xFF1A;

 [UniqueKey] // Unique Key public int FirstColumn { get; set;} [UniqueKey] // Unique Key public int SecondColumn { get; set;} // The problem hier 1, 1 = OK 1 ,2 = NO OK 1 IS UNIQUE 

这种方法的问题; 我怎样才能把它们结合起来 我有一个想法来扩展这个微软的实现,例如:

 [UniqueKey, 1] // Unique Key public int FirstColumn { get; set;} [UniqueKey ,1] // Unique Key public int SecondColumn { get; set;} 

稍后在Microsoft示例中所述的IDatabaseInitializer中,您可以根据给定的整数组合键。 有一点需要注意,但是:如果unique属性是stringtypes,那么你必须设置MaxLength。

您需要定义一个组合键。

有了数据注释,它看起来像这样:

 public class Entity { public string EntityId { get; set;} [Key] [Column(Order=0)] public int FirstColumn { get; set;} [Key] [Column(Order=1)] public int SecondColumn { get; set;} } 

在重写OnModelCreating时,您也可以使用modelBuilder执行此操作,方法是指定:

 modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn }); 

完成使用外键使用复合索引的 @ chuck答案。

您需要定义一个将保存外键值的属性。 然后可以在索引定义中使用这个属性。

例如,我们与员工有公司,只有我们对任何员工(姓名,公司)有一个独特的约束:

 class Company { public Guid Id { get; set; } } class Employee { public Guid Id { get; set; } [Required] public String Name { get; set; } public Company Company { get; set; } [Required] public Guid CompanyId { get; set; } } 

现在Employee类的映射:

 class EmployeeMap : EntityTypeConfiguration<Employee> { public EmployeeMap () { ToTable("Employee"); Property(p => p.Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); Property(p => p.Name) .HasUniqueIndexAnnotation("UK_Employee_Name_Company", 0); Property(p => p.CompanyId ) .HasUniqueIndexAnnotation("UK_Employee_Name_Company", 1); HasRequired(p => p.Company) .WithMany() .HasForeignKey(p => p.CompanyId) .WillCascadeOnDelete(false); } } 

请注意,我还使用了@ nnher扩展名进行唯一索引注释。

我假设你总是希望EntityId作为主键,所以用组合键replace它不是一个选项(如果仅仅因为组合键的工作复杂得多, 并且因为主键也是不合理的意义在业务逻辑)。

至less你应该做的是在数据库的两个字段上创build一个唯一的密钥,特别是在保存更改时检查唯一的密钥违例例外。

另外,您可以(应该)在保存更改之前检查唯一值。 最好的方法是通过Any()查询,因为它最大限度地减less了传输的数据量:

 if (context.Entities.Any(e => e.FirstColumn == value1 && e.SecondColumn == value2)) { // deal with duplicate values here. } 

小心这个检查是不够的。 检查和实际提交之间总会有一些延迟,所以你总是需要唯一的约束+exception处理。