DbSet.Attach()仅更新单个表但不引用单个表

时间:2014-04-02 22:15:58

标签: database entity-framework repository

我有两个表:WordAdjective,两者都有一些属性。这两个表的主键是IDAdjective.ID也引用Word.ID作为外键,因此存在1-1关系。

我还有一个包含Update功能的表的存储库。

public void Update(T entity) {
    var entry = DatabaseContext.Entry(entity);
    DatabaseSet.Attach(entity);
    entry.State = EntityState.Modified;
}

我从数据库中获取一个值,将其转换为一个看起来像这样的ViewModel(当然它实际上有点复杂):

public class WordModel {
    public int ID { get; set; }
    public string OriginalWord { get; set; }
}
public class AdjectiveModel : WordModel {
    public string Translation { get; set; }
}

然后我改变属性Word和翻译的值,转换并写回。转换后我有一个像这样的对象:

Word = {
    ID = 1
    OriginalWord = y
    Adjective = {
        ID = 1
        Translation = z
    }
}

然而,在更新时,只有一个表得到更新。

Database.Words.Update(Word)仅更新Word表格中的OriginalWord值, Database.Adjectives.Update(Word.Adjective)仅更新“形容词”表中的“翻译”值。 当顺序运行两个表的更新时,我得到一个InvalidOperationException: ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。 创建新的数据库条目非常有效。

我无法相信我必须自己更新两个表,然后为每个表保存上下文。我通过教程创建了数据库存储库,显然没有很好地解释DbSet和DbContext的情况,这让我有点无奈。 可悲的是,我没有链接(很久以前我创建了数据库项目)

那么,我在这里做错了什么?

1 个答案:

答案 0 :(得分:2)

你实体Word包含一个实体形容词,它就是对象图的根。现在,在下列情况下,您应该记住以下内容:


图表中的所有对象都是新的(新词和新形容词)

使用myDbContext.Words.Add(myNewWordObjectGraph);来获得所需的正确状态。


只有root是新的(新词和预先存在的非修饰形容词)

使用myDbContext.Entry(myNewWord).state = EntityState.Added;来获得所需的正确状态。


Root被修改,一些节点被修改(单词和形容词都存在于DB中,两者都被修改过)

使用myDbContext.Entry(myWord).State = EntityState.Modified; myDbContext.Entry(myAdjective).State = EntityState.Modified;来获得您想要的正确状态。即为图中的每个修改对象调用myDbContext.Entry(myObject).State = EntityState.Modified;,无论它是根还是其他节点。


Root未更改和/或已修改,并且添加了一些节点,其他节点也未更改和/或已修改

使用myDbContext.MyRootObjectDbSet.Add(myRootObject);;这会将图表中的所有对象标记为EntityState.Added ,包括未更改和/或修改的对象 。所以下一个调用应该是每个未更改和/或修改的对象,以便更正其状态:myDbContext.Entry(myObject).State = ThisObjectSCorrectState;

我希望有所帮助


修改

  1. 调用DbSet.Attach(...)只是将对象添加到EF的跟踪对象中。如果在调用DbSet.Attach(...)之前修改对象,则在调用SaveChages()时修改将不会持久保存到DB,因此在修改之前附加对象 ,调用{{ 1}}然后修改对象是让EF意识到修改的方法。

  2. 根据你的更新方法定义的方式,我认为你的存储库看起来像这样吗?

  3. //不是线程安全的,因为它包含一个瞬态对象'DbContext'。

    DbSet.Attach(...)

    我建议将更新方法更改为以下内容:

    public class Repository<T> : IRespository<T> where T : class
    {
        private readonly MyDbContext context;
        public Repository(MtDbContext context)
        {
            this.context = context
        }
    
        //...
        public void Update(T entity) {... }
        public void Commit() { context.SaveChanges(); }
    }
    

    使用包含DbContext的存储库的相同实例,将为您在图中更新的每个对象调用此更新方法。