62

我目前正在使用最新版本的 Entity Framework 开发一个项目,但遇到了一个似乎无法解决的问题。

在更新现有对象时,我可以相当轻松地更新对象属性,直到涉及到一个引用另一个类的属性。

在下面的示例中,我有一个名为 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;
}

任何帮助或指示将不胜感激。

更新:根据 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 无法跟踪具有相同键的多个对象

4

3 回答 3

100

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如果关系已更改,您最终还需要设置附加实体的状态以及标量属性。

编辑

如果 - 根据您的评论 -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;
}
于 2012-11-05T17:29:28.990 回答
3

只是想我会发布下面的链接,因为它真的帮助我了解如何更新相关实体。

在asp net mvc应用程序中使用实体框架更新相关数据

注意:我确实稍微更改了 UpdateInstructorCourses 函数中显示的逻辑以满足我的需要。

于 2013-08-15T02:38:31.513 回答
0

您也可以独立调用表格

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();
于 2015-02-23T04:17:10.390 回答