1

Entity Framework でエンティティを更新するのに苦労しています。

シナリオ: - new DbContext().GetById(guid) でエンティティをロードします - 拡張メソッドを使用してこのエンティティを保存しようとし、次に new DbContext() を使用します

私の更新方法は次のとおりです。

 public virtual void Update(IEntity entityToUpdate)
    {
        var dbEntry = Context.Entry(entityToUpdate);
        if (dbEntry == null) return;

        if (Context.Entry(entityToUpdate).State == EntityState.Detached)
            DbSet.Attach(entityToUpdate);
        else
        {
            dbEntry.CurrentValues.SetValues(entityToUpdate);
            Context.Entry(entityToUpdate).State = EntityState.Modified;
        }

        Context.SaveChanges();
    }

これは私の試みのコレクションです。SetValues を使用すると、エンティティがデタッチされているため不可能であると通知され、アタッチを使用すると、次のエラーが表示されます。ObjectStateManager は、同じキーを持つ複数のオブジェクトを追跡できません。

私は明らかに根本的に間違ったことをしています。誰かが正しい方向に私を助けてくれますか?

アップデート:

    protected void TransferClubs(object sender, EventArgs e)
    {
        var clubHelper = new ClubHelper();
        var club = clubHelper.GetClub(new Guid("A009D0CD-71C4-42E8-88E2-037F059B12EE"));
        club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);
        club.AddUser(Guid.NewGuid(), ClubRoleType.Admin);

        club.Save();
    }

    public static bool Save(this ClubItem item)
    {
        var clubHelper = new ClubHelper();
        clubHelper.AddOrUpdate(item);
        return true;
    }

    public ClubItem AddOrUpdate(ClubItem item)
    {
        if (item.Id == Guid.Empty)
            Insert(item);
        else
            Update(item);

        return item;
    }

そして、私の元の投稿に表示される Update() メソッド...

4

1 に答える 1

2

私の意見ではTransferClubs、最後の行club.Save();を次のように置き換えると、更新が機能するはずです

clubHelper.SaveChanges();

このメソッドは単に呼び出しContext.SaveChanges();て、ロードされたエンティティへの変更を保存する必要があります。つまり、ロードされたエンティティに設定された外部キーを持つ 2 人の新しいユーザーを作成しますclub

正直なところ、エンティティを更新するアプローチはかなり奇妙で複雑です。あなたのコードが " An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key " 例外をスローする理由がわかりません。しかし、いくつかの欠陥や意味をなさないものがあります。

  • インスタンス化されたコンテキストを破棄していません。新しいコンテキストを作成する場合ClubHelperは、スコープ外になったときに破棄する必要があります。したがって、ClubHelper実装する必要がIDisposableあり、実装Disposeは を呼び出す必要がありますcontext.Dispose()using次に、インスタンス化されたオブジェクトを最後に自動的に破棄するa を使用できます。

    protected void TransferClubs(object sender, EventArgs e)
    {
        using (var clubHelper = new ClubHelper())
        {
            // stuff...
        } // Dispose called here automatically
    }
    
  • これらの行は意味がありません:

    var dbEntry = Context.Entry(entityToUpdate);
    if (dbEntry == null) return;
    

    entityToUpdateオブジェクト モデルのエンティティである場合は、dbEntry決してありませんnull。状態のみを持つことができますDetachedentityToUpdateオブジェクト モデルのエンティティではない場合Entry、例外がスローされますが、返されませんnull

  • ケースはif意味をなさない:

    if (Context.Entry(entityToUpdate).State == EntityState.Detached)
        DbSet.Attach(entityToUpdate);
    // ...
    Context.SaveChanges();
    

    Attachstate のコンテキストにエンティティを追加しますUnchanged。その後に呼び出すだけではSaveChanges、何も変更されていないため、何も起こらず、データベースに書き込まれます。

  • また、elseケースは意味をなさない:

    dbEntry.CurrentValues.SetValues(entityToUpdate);
    Context.Entry(entityToUpdate).State = EntityState.Modified;
    

    SetValuesentityToUpdateプロパティが同じ名前を持つ場合、同じキーを持ち、コンテキストに既にアタッチされているエンティティのプロパティに のプロパティをコピーします。プロパティ値が異なる場合、プロパティは としてマークされModifiedます。後でエンティティ全体を設定すると、すべてのプロパティが冗長になるようModifiedにマークされます。ModifiedSetValues

    さらに、どちらの行も必要な UPDATE ステートメントを作成するのに役立ちません。関係を変更済みとしてマークせず、スカラー (および複雑な) プロパティのみに影響し、ナビゲーション プロパティには影響しないためです。しかし、関係の更新、つまりclubと 2 人の新しいユーザーの間の関係は、まさにこの例で必要なものです。

  • 最後に、まったく新しいコンテキストで更新を実行しても意味がありません...

    var clubHelper = new ClubHelper();
    clubHelper.AddOrUpdate(item);
    

    ...別のコンテキストの直前の行でエンティティをロードして変更した場合。Entity Framework は、必要な SQL ステートメントを生成するために、最初のコンテキストで変更を追跡するためのすべての作業を既に完了しています。この後、変更を保存する新しいコンテキストを作成すると、すべての作業が破棄され、元のエンティティに対して行った変更を EF に伝えるためにゼロから開始する必要があります。

于 2012-06-14T14:10:03.933 に答える