简体   繁体   English

实体框架,更新相关对象的问题

[英]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. 我目前正在使用最新版本的Entity Framework进行项目,我遇到了一个我似乎无法解决的问题。

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

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. 当我使用下面的Edit()方法时,我传入了我想要更新的对象,我可以设法让Name正确更新,但是我还没有找到一种方法来获取SubFoo的属性更改。 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. 例如,如果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;
}

Any help or pointers would be greatly appreciated. 任何帮助或指针将不胜感激。

UPDATE: Based on the comment by Slauma I have modified my method to 更新:根据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;
}

When running this now, I get the error: 现在运行时,我收到错误:

The entity type Collection`1 is not part of the model for the current context. 实体类型Collection`1不是当前上下文的模型的一部分。

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: 为了尝试解决这个问题,我添加了代码以尝试将newFoo子类附加到上下文中,但这是通过一个错误说明ObjectManager已经有一个实体相同:

An object with the same key already exists in the ObjectStateManager. ObjectStateManager中已存在具有相同键的对象。 The ObjectStateManager cannot track multiple objects with the same key ObjectStateManager无法使用相同的键跟踪多个对象

CurrentValues.SetValues only updates scalar properties but no related entities, so you must do the same for each related entity: 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;
}

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

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: 如果 - 根据你的评论 - 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;
}

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 在asp net mvc应用程序中使用实体框架更新相关数据

NOTE: I did change the logic slightly that is shown in the UpdateInstructorCourses function to suit my needs. 注意:我确实稍微更改了UpdateInstructorCourses函数中显示的逻辑以满足我的需要。

You can also call the tables independently 您也可以单独调用表

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM