5

MVC3でエンティティフレームワークのコードに問題が発生しました。Entity Frameworkコードファースト: "ObjectStateManagerは同じキーを持つ複数のオブジェクトを追跡できません。"

ObjectStateManagerに同じキーを持つオブジェクトがすでに存在しています。 ObjectStateManagerは同じ キーを持つ複数のオブジェクトを追跡できません。

これは何度もSOに対処されていますが、私の状況で提案されている解決法を利用するのに問題があります。ここで

は、コードサンプルです:

明確に編集
FestORM.SaleMethod method = new FestORM.SaleMethod 
{ 
    Id = 2, 
    Name = "Test Sale Method" 
}; 
FestContext context = new FestContext(); 

//everything works without this line: 
string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

context.Entry(method).State = System.Data.EntityState.Modified; 
context.SaveChanges(); 

私はすでにデータベースに存在するオブジェクトを更新しようとしています。

コードに記載されているクエリがなくてもすべて正常に動作します。私のアプリケーションでは、私のコントローラはコンテキストをインスタンス化しており、同じコンテキストがコントローラによって使用されるいくつかのリポジトリに渡されるため、最初のクエリ操作に別のコンテキストを使用することはできません。私は、ObjectStateManagerで追跡されているエンティティを削除しようとしましたが、そのいずれかを取得することはできません。私は、両方の条件で動作する解決策を見つけようとしています。ObjectStateManagerによって追跡されるオブジェクトを更新することがありますが、時にはまだ追跡されていないことがあります。

FWIW、私の本当のリポジトリ機能は、ちょうど上記のコードのように、次のようになります。

public void Update(T entity) 
{ 
    //works ONLY when entity is not tracked by ObjectStateManager 
    _context.Entry(entity).State = System.Data.EntityState.Modified; 
} 

public void SaveChanges() 
{ 
    _context.SaveChanges(); 
} 

任意のアイデア?私はあなたが呼び出す前に明白な答えは、あなたが実際にデータベースにメソッドオブジェクトを保存していないということでしょう...あまりにも長い間

答えて

12

問題が

context.Entry(method).State = System.Data.EntityState.Modified; 

コンテキストに異なるインスタンスを添付あります。どちらのインスタンスも同じプライマリキーを持っているので、同じキーを持つ2つの異なるエンティティをコンテキストにアタッチしようとしているとEFは考えています。彼らはどちらも同じ存在になっているとは知られていません。

あなただけの名前を照会する必要がありますが、実際にはコンテキストに完全な実体を持ってしたくない何らかの理由で、あなたはこれを行うことができる場合:あなたが抱き合わせているものならば

string thisQueryWillMessThingsUp =   
    context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name; 

を行うには、更新が既存のエンティティであり、あなたはそのエンティティのすべてのマップされたプロパティの値を持っている、そして最も簡単な方法は、クエリを実行し、ちょうど使用しないことです。

context.Entry(method).State = System.Data.EntityState.Modified; 

あなたが更新しない場合すべてのプロパティ。おそらく、すべてのプロパティの値がないため、エンティティとSaveChangesを呼び出す前にそのプロパティを設定することは容認できるアプローチです。正確な要件に応じて、これを行うにはいくつかの方法があります。一つの方法は、プロパティメソッドを使用するので、のようなものです:

var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query 
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName; 
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue; 
context.SaveChanges(); 

これらのブログの記事が役立つかもしれないいくつかの追加情報が含まれています応答のための

http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx

+0

ありがとうございます。 (私が新しいのであなたの回答に投票することはできません)これは、将来の更新に先行するクエリをいつ実行するのか分かりません。クエリがサービス層のどこかに埋め込まれている可能性があります。最後に、クエリされた同じエンティティで更新を行うと、エラーが発生します。コンテキストトラッキングからオブジェクトを削除したり、コンテキストによってトラッキングされているオブジェクトを取得したり、問題のオブジェクトを更新していることをコンテキストに伝える方法はありますか?私はこの方向でいくつかを研究しましたが、まだ運はありません。 – Josh

+0

これは、クエリの意図に依存します。クエリがエンティティをコンテキストに持って来て、いつもそうであるように、それが更新され、保存されることを意図しているのであれば、そのエンティティが持ち込まれたことを保存する前に、以前に持ち込まれた。クエリの目的が、エンティティをコンテキストに持ち込まずに何らかの理由でデータベースからエンティティの一部のプロパティを取得する場合は、上記のようにそのクエリでAsNoTrackingを使用します。 –

+0

エンティティが持ち込まれていることを確認するには、以前のコメントに追加するには、エンティティのプロパティを更新して保存する前にFindメソッドを使用します。 Findではなくクエリを使用することもできます。デフォルトでは、クエリはエンティティがコンテキストにまだ存在しない場合にのみエンティティを持ち込みます。しかしFindはより効率的です。 –

1

これを戦ってきた:

//everything works without this line: 
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

しかし、私は多分これだと思いますあなたが放棄したちょっとしたコード。 エンティティを抽象クラスから継承させるとどうなりますか?

public abstract class BaseClass 
{ 
    public int Id { get; set; } 
} 

はその後

public class Repository<T> where T : BaseClass 
{ 
..... 
    public void Update(T entity) 
    {   
     _context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified; 
    } 
} 

にあなたのリポジトリを更新する。また、あなたのSaleMethodのIDを設定し、それをデータベースによって生成させないようにするかもしれません。問題は、データベース内のSaleMethodオブジェクトが2のIdを持っているため、ID 2の別のSaleMethodオブジェクトを追加しようとしたためです。 2のIDを持つ別のSaleMethodオブジェクトをObjectStateManagerに追加しようとするとエラーが発生します。このクエリ

string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

コンテキストにSaleMethodエンティティの1つのインスタンスをもたらし、次に、このコードこと

+0

感謝を。はい、IDは確実にデータベースによって生成されます。これは具体的には**更新**操作です。オブジェクトが最初に保存された部分を除外しました(何年も前に別のコードで保存されていた可能性があります)。つまり、最初にクエリを実行しない限り、アップデートは正常に機能します。クエリを実行すると、ObjectStateManagerのトラッキングに追加されます。この時点で、私の更新はうまくいきます。 – Josh

関連する問題