[entity-framework] 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


답변

문제를 해결하는 세 가지 방법을 찾았습니다.

EntityFramework Core의 고유 인덱스 :

첫 번째 접근법 :

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

두 번째 접근법대체 키를 사용하여 EF Core로 고유 제약 조건을 만드는 입니다.

하나의 열 :

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
에서 고유 키 데이터 주석에 대한 코드를 찾을 수 있습니다 :

[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

이 접근법의 문제점; 어떻게 결합 할 수 있습니까? 이 Microsoft 구현을 확장하려는 아이디어가 있습니다.

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

나중에 Microsoft 예제에 설명 된대로 IDatabaseInitializer에서 지정된 정수에 따라 키를 결합 할 수 있습니다. 한 가지 주목할 점은 다음과 같습니다. 고유 속성이 문자열 유형 인 경우 MaxLength를 설정해야합니다.


답변

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
)


답변

복합 키를 정의해야합니다.

데이터 주석을 사용하면 다음과 같습니다.

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


답변

유창한 API를 사용하려면 사용자 정의 확장이 필요하다는 niaher의 답변은 작성 당시에 정확했을 수 있습니다. 이제 (EF core 2.1) 다음과 같이 유창한 API를 사용할 수 있습니다.

modelBuilder.Entity<ClassName>()
            .HasIndex(a => new { a.Column1, a.Column2}).IsUnique();


답변

외래 키 와 함께 복합 인덱스 를 사용하기위한 @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);
    }
}

고유 인덱스 주석에 @niaher 확장을 사용했습니다.


답변

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