3

EF DbContext/POCO エンティティを独立した方法で使用したい。つまり、ビジネス層からエンティティの階層を取得し、いくつかの変更を加えてから、階層全体をビジネス層に送り返し、データベースに永続化します。各 BLL 呼び出しは、DbContext の異なるインスタンスを使用します。これをテストするために、そのような環境をシミュレートするコードを書きました。

Customerまず、関連するプラスを取得しますOrders: OrderLines-

Customer customer;
using (var context = new TestContext())
{
    customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}

Order次に、新しいものを 2 つ追加しOrderLinesます :-

var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;

最後に、変更を永続化します (新しいコンテキストを使用):-

using (var context = new TestContext())
{
    context.Customers.Attach(customer);

    context.SaveChanges();
}

SaveChanges() を呼び出す前に、新しいエンティティの状態を変更する必要があることは間違いないため、この最後の部分は不完全であることに気付きました。顧客を追加または添付しますか? どのエンティティの状態を変更する必要がありますか?

この段階に到達する前に、上記のコードを実行すると例外がスローされます。

同じキーを持つオブジェクトが ObjectStateManager に既に存在します。

2 つのエンティティの ID を明示的に設定していないOrderLineことが原因のようで、どちらもデフォルトで 0 です。EF が自動的に処理するので、これで問題ないと思いました。私は何か間違ったことをしていますか?

また、この「切り離された」方法で作業すると、関係を設定するために多くの作業が必要になるようです。新しい注文エンティティをcustomer.Ordersコレクションに追加し、新しい注文のCustomerプロパティとそのCustomerIdプロパティを設定する必要があります。これは正しいアプローチですか、それとももっと簡単な方法がありますか?

自己追跡エンティティを見たほうがよいでしょうか? それらが廃止されているか、少なくともPOCOを支持して落胆していることをどこかで読んだことがあります。

4

1 に答える 1

3

基本的に 2 つのオプションがあります。

A) 楽観的。

現在進めている方法にかなり近い方法で進めることができ、すべてを Modified として添付し、希望するだけです。.Attach() の代わりに探しているコードは次のとおりです。

context.Entry(customer).State = EntityState.Modified;

間違いなく直感的ではありません。この奇妙に見える呼び出しは、切り離された (または新しく構築された) オブジェクトを Modified としてアタッチします。ソース: http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states。 aspx

オブジェクトが追加または変更されたかどうかわからない場合は、最後のセグメントの例を使用できます。

context.Entry(customer).State = customer.Id == 0 ?
                               EntityState.Added :
                               EntityState.Modified;

追加/変更されるすべてのオブジェクトに対してこれらのアクションを実行する必要があるため、このオブジェクトが複雑で、FK 関係を介して DB で更新する必要がある他のオブジェクトがある場合は、それらの EntityState も設定する必要があります。

シナリオによっては、別の Context バリエーションを使用して、これらの種類の don't-care 書き込みを安価にすることができます。

public class MyDb : DbContext
{
    . . .
    public static MyDb CheapWrites()
    {
        var db = new MyDb();
        db.Configuration.AutoDetectChangesEnabled = false;
        db.Configuration.ValidateOnSaveEnabled = false;
        return db;
    }
}

using(var db = MyDb.CheapWrites())
{
    db.Entry(customer).State = customer.Id == 0 ?
        EntityState.Added :
        EntityState.Modified;
    db.SaveChanges();
}

基本的に、とにかく結果を無視しているEFがあなたに代わって行ういくつかの追加の呼び出しを無効にしているだけです。

B) 悲観的。実際にDBにクエリを実行して、データが最後に取得されてから変更/追加されていないことを確認し、安全であれば更新できます。

var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then

db.Entry(existing).CurrentValues.SetValues(customer);
于 2013-03-29T00:11:36.947 に答える