Entity Framework 4-AddObjectとAttach


132

私は最近Entity Framework 4を使用していてObjectSet.AttachObjectSet.AddObjectをいつ使用するかについて少し混乱しています

私の理解から:

  • エンティティがシステムにすでに存在する場合は、「アタッチ」を使用します
  • 新しいエンティティを作成するときに「AddObject」を使用します

したがって、新しいPersonを作成する場合は、これを行います。

var ctx = new MyEntities();
var newPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.AddObject(newPerson);
ctx.SaveChanges();

既存のPersonを変更する場合は、次のようにします。

var ctx = new MyEntities();
var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
existingPerson.Name = "Joe Briggs";
ctx.SaveChanges();

これは非常に簡単な例です。実際には、Pure POCO(コード生成なし)、リポジトリパターン(ctx.Personsを扱わない)、および作業単位(ctx.SaveChangesを扱わない)を使用しています。しかし、「カバーの下」では、上記は私の実装で何が起こるかです。

さて、私の質問 -私はAttachを使用しなければならなかったシナリオをまだ見つけていません。

ここで何が欠けていますか?いつAttachを使用する必要がありますか?

編集

わかりやすくするために、AddObjectに対してAttachを使用する(またはその逆の)場合のを探しています。

編集2

以下の答えは正しいです(私はこれを受け入れました)が、アタッチが役立つ別の例を追加したいと思いました。

上記の既存のPerson変更する例では、2つのクエリが実際に実行されています。

1つはPerson(.SingleOrDefault)を取得し、もう1つはUPDATE(.SaveChanges)を実行します。

(何らかの理由で)システムに「Joe Bloggs」が存在することをすでに知っている場合、なぜ彼を最初に取得するために追加のクエリを実行するのですか?私はこれを行うことができます:

var ctx = new MyEntities();
var existingPerson = new Person { Name = "Joe Bloggs" };
ctx.Persons.Attach(existingPerson);
ctx.SaveChanges();

これにより、UPDATEステートメントのみが実行されます。


Attachは、モデルをEFに直接戻すときに、MVCでも使用されています。かなりうまく動作し、大量のコード行を節約できます。
Piotr Kula 2014年

回答:


162

ObjectContext.AddObject ObjectSet.AddObject AddObjectメソッドの方法はない、新しく作成されたオブジェクトの追加のためにあるではないデータベースに存在しています。エンティティは自動的に生成された一時的な EntityKeyを取得し、そのEntityStateは Addedに設定さます。SaveChangesが呼び出されると、このエンティティをデータベースに挿入する必要があることがEFに明らかになります。

ObjectContext.Attachおよび ObjectSet.Attach
一方、データベースにすでに存在するエンティティには Attachが使用されます。むしろ追加したEntityStateを設定するより、で結果を取り付け不変それは文脈に取り付けたので、それが変更されていないことを意味する、EntityState。アタッチするオブジェクトは、データベースに存在すると想定されています。アタッチ後にオブジェクトを変更した場合、SaveChangesを呼び出すと、EntityKeyの値を使用して、dbテーブルで一致するIDを見つけることにより、適切な行を更新(または削除)します。 さらに、Attachメソッドを使用すると、ObjectContextに既に存在しているが、

自動的には接続されません。基本的には、アタッチの主な目的は、既にObjectContextはに取り付けられているされている接続のエンティティにあるではないあなたがそのEntityState追加されたエンティティをアタッチするアタッチ使用することはできませんので新しいです。この場合は Add()を使用する必要があります。

たとえば、Personエンティティに、 Addressエンティティのコレクションである Addressesという名前のナビゲーションプロパティがあるとします。コンテキストから両方のオブジェクトを読み取ったが、それらは互いに関連しておらず、そのようにしたいとします。

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.PersonReference.Attach(existingPerson)
ctx.SaveChanges();

答えてくれてありがとう、私は2つの定義(別名最初の2つの段落)を理解しています。しかし、私はアタッチを使用する必要があるシナリオを理解していません。あなたの最後の段落は私には本当に意味がありません(基本的に最初の2つの段落の組み合わせのように読みます)、上記のシナリオで「添付」を使用する場所の例を教えてもらえますか?それは本当に私が探しているものです-定義ではなく例です。本当にあなたの時間に感謝します。:)
RPM1984

1
問題ありません。最後の段落を明確にするためにコードスニペットを追加しました。2つの無関係なオブジェクトがあり、Attachがそれらを相互に関連付けるのに役立ちます。もう1つの例は、Attach()メソッドを使用して「切り離されたエンティティ」をコンテキストに接続し直すことです(切り離されたエンティティをコンテキストに接続し直すには、さまざまな理由があります)
Morteza Manavi

1
うん、私は今やっちゃった。例を示したEF4(Julie Lerman著)でTechEd vidを視聴したところです。クエリから取得しなかったエンティティ(つまり、それが切断されている)がある可能性がありますが、エンティティが存在することがわかっているため、Attachを使用してそのエンティティに対してUPDATEを実行します。理にかなっていますが、私はまだ「切断された」エンティティがあるシナリオを検討するために努力しています。ご協力いただきありがとうございます。
RPM1984

1
すごい。この投稿をたまたま読む可能性がある他の開発者のために、ビデオのリンクを共有してもらえますか?
Morteza Manavi、

3
RPM1984によって共有された上記のリンクは壊れており、現在channel9.msdn.com/Events/TechEd/NorthAmerica/2010/DEV205にリダイレクトされています。楽しむ
スタックされた2016年

31

これは遅い応答ですが、これを見つける他の人を助けるかもしれません。

基本的に、「使用されていない」スコープの外でエンティティを操作すると、「切断された」エンティティが発生する可能性があります。

Employee e = null;

using (var ctx = new MyModelContainer())
{
     e = ctx.Employees.SingleOrDefault(emp => emp .....);
}

using (var ctx2 = new MyModelContainer())
{
     e; // This entity instance is disconnected from ctx2
}

別の「using」スコープを入力すると、「e」変数は以前の「using」スコープに属しているため切断され、以前の「using」スコープは破棄されるため、「e」は切断されます。

それが私の理解です。


3
Tchiの例は優れた単純な例です。ええ、Employee変数は外部で宣言する必要があります。スコープ外でe.Address.Streetを試してみると、null参照例外ポップアップが表示されます。接続する場合、アプリケーションは2番目のスコープの従業員のDBに戻る必要はありません。
スティーブ

9

これは、プログラミングエンティティフレームワーク:DbContextからの引用です。

コンテキストで追跡されていないエンティティでRemoveを呼び出すと、InvalidOperationExceptionがスローされます。Entity Frameworkがこの例外をスローするのは、削除しようとしているエンティティが、削除対象としてマークする必要がある既存のエンティティなのか、単に無視する必要がある新しいエンティティなのかが明確でないためです。このため、切断されたエンティティを削除済みとしてマークするために単に削除を使用することはできません。最初にアタッチする必要があります。

private static void TestDeleteDestination()
{
    Destination canyon;
    using (var context = new BreakAwayContext())
    {
        canyon = (from d in context.Destinations
        where d.Name == "Grand Canyon"
        select d).Single();
    }
    DeleteDestination(canyon);
}
private static void DeleteDestination(Destination destination)
{
    using (var context = new BreakAwayContext())
    {
        context.Destinations.Attach(destination);
        context.Destinations.Remove(destination);
        context.SaveChanges();
    }
}

TestDeleteDestinationメソッドは、クライアントアプリケーションがサーバーから既存の宛先をフェッチし、それをサーバー上のDeleteDestinationメソッドに渡すことをシミュレートします。DeleteDestinationメソッドは、Attachメソッドを使用して、既存の宛先であることをコンテキストに通知します。次に、Removeメソッドを使用して、削除する既存の宛先を登録します。


-8

アタッチする代わりに主キーのみを参照するのはどうですか?

つまり:

var existingPerson = ctx.Persons.SingleOrDefault(p => p.Name = "Joe Bloggs" };
var myAddress = ctx.Addresses.First(a => a.PersonID != existingPerson.PersonID);
existingPerson.AddressId = myAddress.Id // not -> existingPerson.Addresses.Attach(myAddress);
// OR:
myAddress.Person.Id = existingPerson.Id // not -> myAddress.PersonReference.Attach(existingPerson);
ctx.SaveChanges();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.