Encja Framework, problemy z aktualizacją powiązanych obiektów
Obecnie pracuję nad projektem wykorzystującym najnowszą wersję Entity Framework i natknąłem się na problem, którego nie mogę rozwiązać.
Jeśli chodzi o aktualizację istniejących obiektów, mogę dość łatwo zaktualizować właściwości obiektu ok, dopóki nie dojdzie do właściwości, która jest odniesieniem do innej klasy.
W poniższym przykładzie mam klasę Foo, która przechowuje różne właściwości, z których 2 są instancjami innych klasy
public class Foo
{
public int Id {get; set;}
public string Name {get; set;}
public SubFoo SubFoo {get; set}
public AnotherSubFoo AnotherSubFoo {get; set}
}
Kiedy używam poniższej metody Edit()
, przechodzę do obiektu, który chcę zaktualizować i udaje mi się uzyskać poprawną aktualizację Name
, jednak nie udało mi się znaleźć sposobu na zmianę właściwości SubFoo. Na przykład, jeśli klasa SubFoo
ma właściwość Name
, która została zmieniona i różni się między my DB a newFoo
, nie jest aktualizowana.
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;
}
Każda pomoc lub wskazówki byłyby znacznie doceniam to.
UPDATE: Na podstawie komentarza Slauma zmodyfikowałem swoją metodę na
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;
}
Podczas uruchamiania tego teraz, dostaję błąd:
Zbiór typu entity ' 1 nie jest częścią modelu dla bieżącego kontekst.
Aby to obejść, dodałem kod, aby dołączyć newFoo
podklasy do kontekstu, ale to przez błąd mówiący, że ObjectManager
już miał encję taką samą:
Obiekt o tym samym key już istnieje w ObjectStateManager. Menedżer ObjectStateManager nie może śledzić wielu obiektów z tym samym klucz
3 answers
CurrentValues.SetValues
aktualizuje tylko właściwości skalarne, ale nie ma powiązanych podmiotów, więc musisz zrobić to samo dla każdego powiązanego podmiotu:
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;
}
Jeśli związek mógł zostać całkowicie usunięty lub został utworzony, musisz również zająć się tymi przypadkami jawnie:
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;
}
W końcu musisz również ustawić stan dołączonych encji na Modified
, jeśli relacja została zmieniona i również właściwości skalarne.
Edit
If-according to twój komentarz - {[5] } jest w rzeczywistości kolekcją i nie tylko referencją, będziesz potrzebował czegoś takiego, aby zaktualizować powiązane podmioty:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-21 22:15:45
Po prostu pomyślałem, że zamieszczę link poniżej, ponieważ naprawdę pomógł mi zrozumieć, jak aktualizować powiązane podmioty.
Aktualizacja powiązanych danych z ramą entity w aplikacji asp net mvc
Uwaga: zmieniłem nieco logikę, która jest pokazana w funkcji UpdateInstructorCourses, aby zaspokoić moje potrzeby.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-15 02:38:31
Można również wywoływać tabele niezależnie
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();
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-23 04:17:10