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;} }
我想将FirstColumn
和SecondColumn
之间的组合作为唯一。
例:
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唯一的关键数据注释的代码:;
[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处理。