現在、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は、同じキーを持つ複数のオブジェクトを追跡できません
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;
}
関連するエンティティを更新する方法を理解するのに本当に役立ったので、以下のリンクを投稿すると思いました。
asp net mvcアプリケーションのエンティティフレームワークを使用して関連データを更新する
注:必要に応じて、UpdateInstructorCourses関数に表示されるロジックを少し変更しました。
テーブルを個別に呼び出すこともできます
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();