【问题标题】:DbSet.Attach() only updates single table but not referencing onesDbSet.Attach() 只更新单个表而不是引用表
【发布时间】:2014-04-02 22:15:57
【问题描述】:

我有两个表:Word 和 Adjective,都有一些属性。两个表的主键是ID,Adjective.ID 还引用Word.ID 作为外键,所以是1-1 关系。

我还有一个存储库,用于存储任何类型的具有更新功能的表。

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 和 Translation 的值,将其转换并写回。转换后我有一个这样的对象:

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 发生了什么,这让我在这里有点无助。 遗憾的是我没有链接(很久以前我创建了数据库项目)

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

【问题讨论】:

    标签: database entity-framework repository


    【解决方案1】:

    你的实体词包含一个实体形容词,它是对象图的根。现在通常在以下情况下您应该牢记以下几点:


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

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


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

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


    根被修改,一些节点被修改(词和形容词都存在于数据库中并且都被修改了)

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


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

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

    希望对你有帮助


    编辑

    1. 调用DbSet.Attach(...) 只是将对象添加到EF 跟踪的对象跟踪。如果在调用DbSet.Attach(...)之前修改了对象,调用SaveChages()时修改不会持久化到DB,所以在修改前原样附加对象,调用DbSet.Attach(...)再修改该对象是让 EF 了解修改的方式。

    2. 根据您定义的更新方法的方式,我会假设您的存储库可能看起来像这样?

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

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

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

    public void Update(T entity)
    {
        context.Entry(entity).State = EntityState.Modified;
    }
    

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

    【讨论】:

    • 非常感谢您的解释。在形容词上将 EntityState 设置为 Modify 有效。但是,我认为 DbSet.Attach(...) 可以解决这个问题。如果不是,它实际上是为了什么?另外,是否有任何一种自动过程可以更新图中的所有元素?遍历每个不同表中的相关表有点杀死我的数据库访问层的通用方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    相关资源
    最近更新 更多