Подтвердить что ты не робот

Структура объекта, проблемы с обновлением связанных объектов

В настоящее время я работаю над проектом с использованием последней версии 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, и это было изменено и отличается от моего 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;
}

Любую помощь или указатели будут очень благодарны.

ОБНОВЛЕНИЕ: Основываясь на комментарии Слаумы, я изменил свой метод на

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

4b9b3361

Ответ 1

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;
}

Ответ 2

Просто подумал, что отправлю ссылку ниже, так как это действительно помогло мне понять, как обновлять связанные объекты.

Обновление связанных данных с инфраструктурой сущности в приложении asp net mvc

ПРИМЕЧАНИЕ. Я немного изменил логику, которая показана в функции UpdateInstructorCourses в соответствии с моими потребностями.

Ответ 3

Вы также можете самостоятельно вызывать таблицы

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();