实体框架中多列的唯一键约束

Bas*_*ili 222 entity-framework constraints unique-key multiple-columns ef-code-first

我正在使用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;}
 }
Run Code Online (Sandbox Code Playgroud)

我想在两者之间进行组合,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
Run Code Online (Sandbox Code Playgroud)

反正有吗?

chu*_*uck 347

使用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; }
Run Code Online (Sandbox Code Playgroud)

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

  • 这对于数据注释:)是正确的,如果您想要使用流畅的API的答案,请参阅下面的Niaher的答案http://stackoverflow.com/a/25779348/2362036 (11认同)
  • 不适用于导航/外键属性 (10认同)
  • 但我需要它为外键工作!你能帮助我吗? (7认同)
  • @JJS - 我让它工作,其中一个属性是外键..你的关键是varchar还是nvarchar?其长度有一个限制可用作唯一密钥.. http://stackoverflow.com/questions/2863993/is-of-a-type-that-is-invalid-for-use-as-一个键柱合的指数 (3认同)
  • @0xFEEDC0DE 请参阅下面我的答案,该答案解决了在索引中使用外键的问题。 (2认同)
  • 使用 System.ComponentModel.DataAnnotations.Schema; (2认同)

Bas*_*ili 108

我找到了三种解决问题的方法.

EntityFramework核心中的唯一索引:

第一种方法:

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

第二种使用备用密钥使用EF Core创建唯一约束的方法.

例子

一栏:

modelBuilder.Entity<Blog>().HasAlternateKey(c => c.SecondColumn).HasName("IX_SingeColumn");
Run Code Online (Sandbox Code Playgroud)

多列:

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

EF 6及以下:


第一种方法:

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

这种方法非常快速且有用,但主要问题是实体框架对这些更改一无所知!


第二种方法:
我在这篇文章中找到了它,但我没有亲自尝试过.

CreateIndex("Entitys", new string[2] { "FirstColumn", "SecondColumn" },
              true, "IX_Entitys");
Run Code Online (Sandbox Code Playgroud)

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


第三种方法:
我认为这是最好的方法,但需要一些时间来完成.我将向您展示它背后的想法:在此链接http://code.msdn.microsoft.com/CSASPNETUniqueConstraintInE-d357224a中, 您可以找到唯一键数据注释的代码:

[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
Run Code Online (Sandbox Code Playgroud)

这种方法的问题; 我该如何组合它们?我有一个想法扩展这个Microsoft实现,例如:

[UniqueKey, 1] // Unique Key 
public int FirstColumn  { get; set;}
[UniqueKey ,1] // Unique Key 
public int SecondColumn  { get; set;}
Run Code Online (Sandbox Code Playgroud)

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

  • 我正在使用**第二种方法**它有效! (2认同)
  • (y) 我发现这个答案更好。另外,第三种方法不一定是最好的。(实际上我喜欢第一个。)我个人不喜欢将任何 EF 工件转移到我的实体类中。 (2认同)
  • 愚蠢的问题:为什么你的名字都以“IX_”开头? (2认同)

nia*_*her 73

如果您使用的是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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

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);
Run Code Online (Sandbox Code Playgroud)

这将导致此迁移:

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");
}
Run Code Online (Sandbox Code Playgroud)

并最终在数据库中结束:

CREATE UNIQUE NONCLUSTERED INDEX [UQ_User_EmailPerApplication] ON [dbo].[User]
(
    [Email] ASC,
    [ApplicationId] ASC
)
Run Code Online (Sandbox Code Playgroud)

  • @RomanPokrovskij:唯一索引和唯一约束之间的区别似乎是它在SQL Server中如何维护记录的问题.有关详细信息,请参阅https://technet.microsoft.com/en-us/library/aa224827%28v=sql.80%29.aspx. (4认同)
  • 但那是索引而不是约束! (2认同)
  • 在你的第二个代码块(`this.Property(t => t.Email)`)中,包含类的内容是什么?(即:什么是'this`) (2认同)
  • 虚拟机。`EntityTypeConfiguration&lt;T&gt;` (2认同)

Adm*_*vić 16

您需要定义组合键.

使用数据注释,它看起来像这样:

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;}
 }
Run Code Online (Sandbox Code Playgroud)

当通过指定覆盖OnModelCreating时,您也可以使用modelBuilder执行此操作:

modelBuilder.Entity<Entity>().HasKey(x => new { x.FirstColumn, x.SecondColumn });
Run Code Online (Sandbox Code Playgroud)

  • 但是不是我只希望它们作为唯一键的键,键应该是 Id?我已经更新了问题感谢您的帮助! (2认同)

Kry*_*tos 8

完成使用带外键的复合索引的 @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; }
}
Run Code Online (Sandbox Code Playgroud)

现在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我还使用@niaher扩展名进行唯一索引注释.


Gil*_*lit 7

niaher的回答指出,要使用Fluent API,您需要自定义扩展名,在撰写本文时可能是正确的。您现在可以(EF core 2.1)如下使用fluent API:

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2}).IsUnique();
Run Code Online (Sandbox Code Playgroud)

  • 不适用于导航属性 -&gt; `System.InvalidOperationException: 'Organization' 不能用作实体类型 'Association' 的属性,因为它被配置为导航..` (2认同)

ful*_*ris 7

对于那些寻找 2021 年解决方案的人来说,已接受答案的工作版本现在应如下所示:

[Index(nameof(FirstColumn), nameof(SecondColumn), IsUnique = true)]
public class Entity
 {
   [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
   public string EntityId { get; set;}
   public int FirstColumn  { get; set;}
   public int SecondColumn  { get; set;}
 }
Run Code Online (Sandbox Code Playgroud)

这样注释应该存在于模型而不是单个列上。还要注意nameof()语法。

这个答案来自官方文档:https : //docs.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations


小智 5

在@chuck 接受的答案中,有一条评论说它在 FK 的情况下不起作用。

它对我有用,EF6 .Net4.7.2 的情况

public class OnCallDay
{
     public int Id { get; set; }
    //[Key]
    [Index("IX_OnCallDateEmployee", 1, IsUnique = true)]
    public DateTime Date { get; set; }
    [ForeignKey("Employee")]
    [Index("IX_OnCallDateEmployee", 2, IsUnique = true)]
    public string EmployeeId { get; set; }
    public virtual ApplicationUser Employee{ get; set; }
}
Run Code Online (Sandbox Code Playgroud)