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 не может отслеживать несколько объектов с одним и тем же
ключ


1062   3  

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

    Ничего не найдено.