284

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

とにかくそれを行うことはありますか?

4

11 に答える 11

395

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

属性の 2 番目のパラメーターは、インデックス内の列の順序を指定できる場所です。
詳細: MSDN

于 2014-04-15T17:02:47.427 に答える
191

問題を解決する 3 つの方法を見つけました。

EntityFramework Core の一意のインデックス:

最初のアプローチ:

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

代替キーを使用して EF Core で一意の制約を作成する 2 番目の方法。

1 列:

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 がこれらの変更について何も知らないことです!


2 番目のアプローチ:
この投稿で見つけましたが、自分で試したことはありません。

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

このアプローチの問題点は次のとおりです。DbMigration が必要なので、それがない場合はどうしますか?


3 番目のアプローチ:
これが最善の方法だと思いますが、それには時間がかかります。その背後にあるアイデアをお見せします: このリンク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 で、指定された整数に従ってキーを組み合わせることができます。ただし、注意すべき点が 1 つあります。一意のプロパティが文字列型の場合は、MaxLength を設定する必要があります。

于 2013-09-19T13:50:02.957 に答える
77

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
)
于 2014-09-11T04:55:38.263 に答える
21

複合キーを定義する必要があります。

データ注釈を使用すると、次のようになります。

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 });
于 2013-09-19T08:52:50.837 に答える
8

これを見つけて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;}
 }

注釈が個々の列ではなくモデルに存在するようにします。nameof()構文にも注意してください。

この回答は、公式ドキュメントから派生したものです: https://docs.microsoft.com/en-us/ef/core/modeling/indexes?tabs=data-annotations

于 2021-04-13T13:28:39.217 に答える
3

常に主キーになりたいと思うEntityIdので、複合キーで置き換えることはオプションではありません(複合キーは操作がはるかに複雑であり、意味を持つ主キーを持つことはあまり賢明ではないという理由だけで)ビジネスロジックで)。

少なくとも、データベースの両方のフィールドに一意のキーを作成し、変更を保存するときに一意のキー違反の例外を特にチェックする必要があります。

さらに、変更を保存する前に一意の値を確認できます (すべきです)。これを行う最善の方法はAny()、転送されるデータの量を最小限に抑えるため、クエリを使用することです。

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

このチェックだけでは十分ではないことに注意してください。チェックと実際のコミットの間には常にある程度の待ち時間があるため、一意の制約と例外処理が常に必要になります。

于 2013-09-19T09:47:01.293 に答える