62

現在、最新バージョンのEntity Frameworkを使用してプロジェクトに取り組んでいますが、解決できないと思われる問題が発生しました。

既存のオブジェクトの更新に関しては、別のクラスへの参照であるプロパティになるまで、オブジェクトのプロパティをかなり簡単に更新できます。

次の例では、さまざまなプロパティを格納するFooというクラスがあり、そのうちの2つは他のクラスのインスタンスです。

public class Foo
{
     public int Id {get; set;}
     public string Name {get; set;}
     public SubFoo SubFoo {get; set}
     public AnotherSubFoo AnotherSubFoo {get; set}
}

以下のEdit()メソッドを使用すると、更新したいオブジェクトを渡して、Nameを適切に更新することができますが、SubFooのプロパティを変更する方法を見つけることができませんでした。たとえば、SubFooクラスにのプロパティがありName、これが変更されており、私のDBとの間で異なる場合、クラスはnewFoo更新されません。

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
        .Include(x => x.SubFoo)
        .Include(x => x.AnotherSubFoo)
        .Single(c => c.Id == newFoo.Id);

    var entry = context.Entry<Foo>(dbFoo);
    entry.OriginalValues.SetValues(dbFoo);
    entry.CurrentValues.SetValues(newFoo);

    context.SaveChanges();

    return newFoo;
}

ヘルプやポインタをいただければ幸いです。

更新:Slaumaのコメントに基づいて、メソッドを次のように変更しました

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
        .Include(x => x.SubFoo)
        .Include(x => x.AnotherSubFoo)
        .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);

    context.SaveChanges();

    return newFoo;
}

これを今実行すると、次のエラーが発生します。

エンティティタイプCollection`1は、現在のコンテキストのモデルの一部ではありません。

newFooこれを回避するために、サブクラスをコンテキストにアタッチしようとするコードを追加しましたが、これは、ObjectManagerすでに同じエンティティが存在するというエラーによるものです。

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

4

3 に答える 3

100

CurrentValues.SetValuesスカラープロパティのみを更新し、関連するエンティティは更新しないため、関連するエンティティごとに同じことを行う必要があります。

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);
    context.Entry(dbFoo.AnotherSubFoo).CurrentValues.SetValues(newFoo.AnotherSubFoo);

    context.SaveChanges();

    return newFoo;
}

関係が完全に削除された可能性がある場合、または作成された可能性がある場合は、これらのケースも明示的に処理する必要があります。

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);
    if (dbFoo.SubFoo != null)
    {
        if (newFoo.SubFoo != null)
        {
            if (dbFoo.SubFoo.Id == newFoo.SubFoo.Id)
                // no relationship change, only scalar prop.
                context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo);
            else
            {
                // Relationship change
                // Attach assumes that newFoo.SubFoo is an existing entity
                context.SubFoos.Attach(newFoo.SubFoo);
                dbFoo.SubFoo = newFoo.SubFoo;
            }
        }
        else // relationship has been removed
            dbFoo.SubFoo = null;
    }
    else
    {
        if (newFoo.SubFoo != null) // relationship has been added
        {
            // Attach assumes that newFoo.SubFoo is an existing entity
            context.SubFoos.Attach(newFoo.SubFoo);
            dbFoo.SubFoo = newFoo.SubFoo;
        }
        // else -> old and new SubFoo is null -> nothing to do
    }

    // the same logic for AnotherSubFoo ...

    context.SaveChanges();

    return newFoo;
}

最終的には、アタッチされたエンティティの状態をModified、関係が変更されたかどうか、およびスカラープロパティも設定する必要があります。

編集

コメントによると、Foo.SubFoo実際にはコレクションであり、参照だけではない場合、関連するエンティティを更新するには、次のようなものが必要になります。

public Foo Edit(Foo newFoo)
{
    var dbFoo = context.Foo
                       .Include(x => x.SubFoo)
                       .Include(x => x.AnotherSubFoo)
                       .Single(c => c.Id == newFoo.Id);

    // Update foo (works only for scalar properties)
    context.Entry(dbFoo).CurrentValues.SetValues(newFoo);

    // Delete subFoos from database that are not in the newFoo.SubFoo collection
    foreach (var dbSubFoo in dbFoo.SubFoo.ToList())
        if (!newFoo.SubFoo.Any(s => s.Id == dbSubFoo.Id))
            context.SubFoos.Remove(dbSubFoo);

    foreach (var newSubFoo in newFoo.SubFoo)
    {
        var dbSubFoo = dbFoo.SubFoo.SingleOrDefault(s => s.Id == newSubFoo.Id);
        if (dbSubFoo != null)
            // Update subFoos that are in the newFoo.SubFoo collection
            context.Entry(dbSubFoo).CurrentValues.SetValues(newSubFoo);
        else
            // Insert subFoos into the database that are not
            // in the dbFoo.subFoo collection
            dbFoo.SubFoo.Add(newSubFoo);
    }

    // and the same for AnotherSubFoo...

    db.SaveChanges();

    return newFoo;
}
于 2012-11-05T17:29:28.990 に答える
3

関連するエンティティを更新する方法を理解するのに本当に役立ったので、以下のリンクを投稿すると思いました。

aspnetmvcアプリケーションのエンティティフレームワークで関連データを更新する

注:必要に応じて、UpdateInstructorCourses関数に表示されるロジックを少し変更しました。

于 2013-08-15T02:38:31.513 に答える
0

テーブルを個別に呼び出すこともできます

MyContext db = new MyContext
// I like using asynchronous calls in my API methods 
var OldFoo = await db.Foo.FindAsync(id);
var OldAssociateFoo = db.AssociatedFoo;  
var NewFoo = OldFoo;
var NewAssociatedFoo = OldAssociatedFoo;

NewFoo.SomeValue = "The Value";
NewAssociatedFoo.OtherValue = 20;

db.Entry(OldFoo).CurrentValues.SetValues(NewFoo);
db.Entry(OldAssociatedFoo).CurrentValues.SetValues(NewAssociatedFoo);

await db.SaveChangesAsync();
于 2015-02-23T04:17:10.390 に答える