Entity framework, problems updating related objects

I am currently working on a project using the latest version of Entity Framework and I have come across an issue which I can not seem to solve.

When it comes to updating existing objects, I can fairly easily update the object properties ok, until it comes to a property which is a reference to another class.

In the below example I have a class called Foo, which stores various properties, with 2 of these being instances of other classes

public class Foo
{
     public int Id {get; set;}
     public string Name {get; set;}
     public SubFoo SubFoo {get; set}
     public AnotherSubFoo AnotherSubFoo {get; set}
}

When I use the below Edit method, I pass in the object I wish to update and I can manage to get the Name to properly update, however I have not managed to find a way in which to get the properties of the SubFoo to change. For example, if the SubFoo class has a property of Name, and this has been changed and is different between my DB and the newFoo, it does not get updated.

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

Any help or pointers would be greatly appreciated.

UPDATE: Based on the comment by Slauma I have modified my method to

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

When running this now, I get the error

The entity type Collection`1 is not part of the model for the current context.

To try and get around this, I added code to try to attach the newFoo subclasses to the context, but this through an error saying that the ObjectManager already had an entity the same

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key


CurrentValues.SetValues only updates scalar properties but no related entities, so you must do the same for each related entity:

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

If the relationship could have been removed altogether or have been created you also need to handle those cases explicitly:

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

You eventually also need to set the state of the attached entities to Modified if the relationship has been changed and the scalar properties as well.

Edit

If - according to your comment - Foo.SubFoo is actually a collection and not only a reference you will need something like this to update the related entities:

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

Just thought I would post the link below as it really helped me understand how to update related entities.

Updating related data with the entity framework in an asp net mvc application

NOTE: I did change the logic slightly that is shown in the UpdateInstructorCourses function to suit my needs.


您也可以独立调用表格

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();
链接地址: http://www.djcxy.com/p/33592.html

上一篇: 实体框架多对多清除不分离?

下一篇: 实体框架,更新相关对象的问题