web-dev-qa-db-ja.com

Entity Framework Code FirstのAddOrUpdateメソッドは重複値を挿入します

私は単純なエンティティを持っています:

public class Hall
{
    [Key]
    public int Id {get; set;}

    public string Name [get; set;}
}

次に、SeedメソッドでAddOrUpdateを使用してテーブルを作成します。

var hall1 = new Hall { Name = "French" };
var hall2 = new Hall { Name = "German" };
var hall3 = new Hall { Name = "Japanese" };

context.Halls.AddOrUpdate(
    h => h.Name,
    hall1,
    hall2,
    hall3
);

次に、パッケージ管理コンソールで実行します。

Add-Migration Current
Update-Database

大丈夫です。テーブル「Hall」に3つの行があります。ただし、パッケージ管理コンソールで実行するとUpdate-Database再び5行あります。

Id  Name
1   French
2   Japaneese
3   German
4   French
5   Japanese

どうして? 5行ではなく3行にする必要があると思います。 Idの代わりにNameプロパティを使用しようとしましたが、違いはありません。

更新:

このコードは同じ結果を生成します:

var hall1 = new Hall { Id = 1, Name = "French" };
var hall2 = new Hall { Id = 2, Name = "German" };
var hall3 = new Hall { Id = 3, Name = "Japanese" };

context.Halls.AddOrUpdate(
                h => h.Id,
                hall1);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall2);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall3);

また、私はnuget経由でインストールされた最新のEntityFrameworkを持っています。

57
Y.Yanavichus

これで1時間キーボードから顔を叩いた。テーブルのIdフィールドがIdentityフィールドである場合は機能しないため、identifierExpressionに別のフィールドを使用します。 Nameプロパティを使用し、new Hall {...}初期化子からIdフィールドも削除しました。

このOPコードの調整は私のために働いたので、誰かに役立つことを願っています:

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Name,   // Use Name (or some other unique field) instead of Id
        new Hall
        {
            Name = "Hall 1"
        },
        new Hall
        {
            Name = "Hall 2"
        });

    context.SaveChanges();
}
63
Ciaran Bruen

これは古い質問ですが、正しい答えは、自分でid#を設定していて、AddOrUpdateを使用する場合は、EF/SQLにID#を生成させたくないことを伝える必要があるということです。

_modelBuilder.Entity<MyClass>().Property(p => p.Id)
    .HasDatabaseGeneratedOption(System.ComponentModel
    .DataAnnotations.Schema.DatabaseGeneratedOption.None); 
_

これのマイナス面は、新しいアイテムを挿入するときにIDを設定する必要があるため、これが実行時に(シードデータからではなく)動的に行われる場合、次のIDを計算する必要があることです。 Context.MyClasses.Max(c=>c.Id) + 1はうまく機能します。

10
Anthony Nichols

このコードは機能します:

public Configuration()
{
    AutomaticMigrationsEnabled = true;
}

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Id,
        new Hall
        {
            Id = 1,
            Name = "Hall 1"
        },
        new Hall
        {
            Id = 2,
            Name = "Hall 2"
        });

    context.SaveChanges();
}
9
Y.Yanavichus

これは、エンティティの状態を誤って設定している場合にも発生する可能性があります。データベースの更新を実行すると、次のエラーが表示され続けました...「シーケンスに複数の一致する要素が含まれています。」

たとえば、各update-databaseコマンドで複製行が作成され(もちろん、データのシード時に発生するはずではありません)、次のupdate-databaseコマンドは複数の一致を検出したためまったく機能しませんでした(したがって、一致する行が複数あるというシーケンスエラーです)。これは、ApplyStateChangesのメソッド呼び出しでコンテキストファイルのSaveChangesをオーバーライドしたためです...

public override int SaveChanges()
{
    this.ApplyStateChanges();
    return base.SaveChanges();
}

ApplyStateChangesを使用して、オブジェクトグラフを追加するときに、Entity Frameworkがオブジェクトが追加または変更された状態にあるかどうかを明示的に確認していました。 ApplyStateChangesの使用方法に関する完全な説明は here にあります。

そして、これはうまく機能します(ただし注意!!)... CodeFirst移行を使用してデータベースをシードしている場合、上記のメソッドはSeed内のAddOrUpdate()呼び出しに大混乱を引き起こします=メソッドですので、何よりも前に、DBContextファイルをチェックして、上記の方法でSaveChangesをオーバーライドしていないことを確認するか、update-databaseコマンドを再度実行して重複データを取得し、動作しません。一致するアイテムごとに複数の行があるため、3回目です。

結局のところ、AddOrUpdate()でIdを構成する必要はありません...これは、簡単で初期のデータベースシードの目的全体を無効にします。それは次のようなものでうまく機能します:

context.Students.AddOrUpdate(
    p => p.StudentName,
    new Student { StudentName = "Bill Peters" },
    new Student { StudentName = "Jandra Nancy" },
    new Student { StudentName = "Rowan Miller" },
    new Student { StudentName = "James O'Dalley" },

applyStateChangesの呼び出しでコンテキストファイルのSaveChangesメソッドをオーバーライドしていないのと同じくらい長い。お役に立てれば。

3
firecape

これは私のために働いた

  1. テーブル内のすべての行を削除します。
  2. 増分IDを0にリセットします。DBCC CHECKIDENT (yourtablename, RESEED, 0)Seed()で指定された主キーは、重複しないようにデータベーステーブルの主キーと一致する必要があります。)
  3. 「シード」メソッドで主キーを指定します。
  4. Seed()メソッドを数回実行し、それらが重複しているかどうかを確認します。

AddOrUpdateはID以外のフィールドでも問題なく動作することがわかりました。これがあなたのために働くなら:context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)

「French_test_abc_100」、「German_test_abc_100」などのホール名を使用できます。

これにより、アプリのテスト中にハードコードされたテストデータが混乱するのを防ぎます。

1
Daryn

IDフィールドをID /キーとして使用し、サーバーがIDを割り当てないように属性を追加しました。これで問題は解決しました。

public class Hall
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id {get; set;}

    public string Name [get; set;}
 }
0
Guntars

これが機能するためには、シードが最初に実行されたときにアイデンティティ位置が0である必要があることがわかりました。以下を使用してリセットできます。

DBCC CHECKIDENT (tableName, RESEED, 0)
0
Brain Balaka

「Update-Database TargetMigration:0」に続いて「Update-Database」のように、既存のデータベース移行をバックアウトする(つまり、データベースをゼロから開始する)必要があると思われます。

現状では、既存のテーブルまたは値を削除するのではなく、それらの値を追加/更新するだけです。目的の結果を得るには、それを行う必要があります。

EF移行の参考資料を次に示します。 http://elegantcode.com/2012/04/12/entity-framework-migrations-tips/

0
Harvey Powers

IDフィールドはIDフィールドですか?私はこれと同じ問題に直面していました。 IDフィールドからIDステータスを削除し、データベースに入力するIDを設定すると、問題が解決しました。

これらはルックアップテーブルであり、とにかくIDフィールドであってはならないので、私にとってはうまくいきました。

0
Jason

Ciarenの答えに、ModelCreatingのコンテキストをリセットする以下のコードは、同様の問題を解決するのに役立ちました。 「ApplicationContext」をDbContext名に変更してください。

public class ApplicationContext : DbContext, IDbContext
    {
        public ApplicationContext() : base("ApplicationContext")
        {
             
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            Database.SetInitializer<ApplicationContext>(null);
            base.OnModelCreating(modelBuilder);
        }
     }
0
Riyaz Hameed

Object(hall)のidが0の場合、これは挿入です。ホールオブジェクトのidフィールドを再確認する必要があると思います

0
weeyoung