Entity framework, проблемы обновления связанных объектов
в настоящее время я работаю над проектом, используя последнюю версию Entity Framework, и я столкнулся с проблемой, которую я не могу решить.
когда дело доходит до обновления существующих объектов, я могу довольно легко обновить свойства объекта ok, пока дело не дойдет до свойства, которое является ссылкой на другой класс.
в приведенном ниже примере у меня есть класс 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, и это было изменено и отличается между моей БД и 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 не может отслеживать несколько объектов с одним и тем же
ключ
3 ответов:
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если отношение было изменено и скалярные свойства как что ж.Edit
если - согласно вашему комментарию -
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; }
просто подумал, что я опубликую ссылку ниже, поскольку это действительно помогло мне понять, как обновить связанные объекты.
обновление связанных данных с помощью Entity framework в приложении 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();
Comments