更新实体框架 6 中的多对多导航属性,未保存更改

本文关键字:属性 导航 保存更改 更新 框架 实体 | 更新日期: 2023-09-27 17:56:56

我已经拔头发大约 2 天了,因为每当我将多对多实体添加到现有实体时,我根本无法让 EF 保存更改。

我的结构很简单:

  • 我有一个名为 Person 的表,它有一个 ID(主、身份)和其他一些字符串字段

  • 名为 Keyword 的表,具有 ID(主、标识)和名为 Value 的字符串字段

  • 和一个PersonKeywordRelation,带有PersonIdKeywordId

当我生成我的实体(首先是数据库)时,我得到了一个类Person,带有ICollection<Keyword> - 一切都很好,按预期工作。

当我尝试保存现有Person时出现问题,并修改关键字列表。只保存标量属性(字符串),而不是我的关键字!

  • 我试过禁用延迟加载,没有效果。
  • 我尝试再次从数据库中加载每个单独的关键字,但没有效果。
  • 我尝试将所有关键字加载到上下文中,看看这是否有助于 EF 检测更改,但事实并非如此。

我很确定我不是唯一一个遇到这个问题的人,(事实上我完全确定,因为我已经在这里看到了几个关于同一主题的问题,但我无法找到有效的答案......),主要是针对旧版本的 EF,这是为什么我开始另一个问题的另一个很好的理由: 没有任何改变可以解决这个问题吗?

这是我的代码,用于更新(和创建)人员。您会注意到我尝试相应地进行 EF 保存更改。

    public void SavePersons(IList<Person> persons)
    {
        // Create a EF Context
        using (var ctx = new MyDbEntities())
        {
            foreach (var person in persons)
            {
                // Attach
                ctx.Persons.Attach(person);
                // Insert or update?
                ctx.Entry(person).State = person.Id == 0 ? EntityState.Added : EntityState.Modified;
                // Get current keywords before clearing from entity
                var keywords = new List<Keyword>(person.Keywords);
                // Clear keywords from entity, so we can add fresh ones, hopefully
                // EF will have an easier time handling this..
                person.Keywords.Clear();
                // Add keywords
                keywords.ForEach(kw =>
                {
                    ctx.Keywords.Attach(kw);
                    ctx.Entry(kw).State = EntityState.Modified;
                    person.Keywords.Add(kw);
                });            
            }
            // Save
            ctx.SaveChanges();
        }
    }

更新实体框架 6 中的多对多导航属性,未保存更改

最后..我终于可以休息了!我找到了解决方案!这不是一个漂亮的,但它有效!

这是代码 - 分享就是关怀。

这绝对是我最后一次使用实体框架。导致更多的痛苦和痛苦而不是好处。

    public void SavePersons(IList<Person> persons)
    {
        // Create a EF Context
        using (var ctx = new MyDbEntities())
        {
            // Iterate
            foreach (var person in persons)
            {
                // Get current keywords
                var keywords = new List<Keyword>(person.Keywords).ToList();
                // Fetch Person from DB (if its not a NEW entry). Must use Include, else it's not working.
                var newPerson = ctx.Persons
                                       .Include("Keywords")
                                       .FirstOrDefault(s => s.Id == person.Id) ?? person;
                // Clear keywords of the object, else EF will INSERT them.. Silly.
                newPerson.Keywords.Clear();
                // Insert or update?
                ctx.Entry(newPerson).State = newPerson.Id == 0 ? EntityState.Added : EntityState.Modified;
                // Apply new scalar values
                if(newPerson.Id != 0)
                {
                    person.Id = newPerson.Id;
                    ctx.Entry(newPerson).CurrentValues.SetValues(person);
                }
                // Iterate through all keywords
                foreach (var kw in ctx.Keywords)
                {
                    // If the current kw exists in OUR list, add it
                    // - if not, remove the relation from the DB.
                    if (keywords.Any(k => k.Id == kw.Id))
                    {
                        //ctx.Entry(kw).State = EntityState.Unchanged;
                        ctx.Keywords.Attach(kw);
                        newPerson.Keywords.Add(kw);
                    }
                    else
                        newPerson.Keywords.Remove(kw);
                }
            }
            // Save
            ctx.SaveChanges();
        }
    }

尝试添加 .ToList()

var keywords = new List<Keyword>(person.Keywords).ToList();//generate list sepereate from .Keywords

我怀疑您的关键字列表从未填充过,因为您在补水之前将其清除。

所以以下内容未经测试,但是,在您修复我的错误后;)它应该有希望做到这一点。 我不知道您的其余代码,因此我选择创建输入数据的克隆并以特定顺序将对象附加到上下文中。

编辑:重命名的方法

    // get unique list of Keywords from all Persons
    private List<Keyword> getUniqueKeywords(IEnumerable<Person> oxygenThiefs) {
        var result = new List<Keyword>();
        foreach (var thief in oxygenThiefs) {
            foreach (var keyword in thief.Keywords) {
                if (!result.Contains(keyword)) {
                    result.Add(keyword);
                }
            }
        }
        return result;
    }
    // shallow clone of Person
    private Person clonePerson(Person target) {
        return new Person {
            Id = target.Id,
            Name = target.Name,
            ..
            ..
        };
    }
    public void SavePersons(IList<Person> persons) {
        // Create a EF Context
        using (var ctx = new MyDbEntities()) {
            // add all Keywords to the Context so that they are tracked
            foreach (var keyword in getUniqueKeywords(persons)) {
                ctx.Keywords.Attach(keyword);
                // if value of Keyword has actually changed then uncomment line
                // ctx.Entry(keyword).State = EntityState.Modified
            }
            foreach (var person in persons) {
                // hehe
                var shallowPerson = clonePerson(person);
                // Attach Person
                ctx.Persons.Attach(shallowPerson);
                // Establish relationship (however shallow and meaningless)
                foreach (var keyword in person.Keywords) {
                    shallowPerson.Keywords.Add(keyword);
                }
                // Insert or update?
                ctx.Entry(shallowPerson).State = person.Id == 0 ? EntityState.Added : EntityState.Modified;
            }
            // Save
            ctx.SaveChanges();
        }
    }