将对象添加到实体集会创建不需要的新数据库行(多对多)。与现有对象建立关系?

时间:2011-08-03 21:15:10

标签: c# entity-framework many-to-many

我很长时间读者 - 第一次发布海报 - 经过无数个小时的研究实体框架问题,我觉得我需要一些帮助。我是C#的新手,也是编程新手,所以请耐心等待。

我的数据模型中有一个相当简单的多对多关系,使用Web 2.0样式的“标签”。班次有标签,用户有标签。

我正在从缓存转移。当我尝试复制一个班次,复制缓存副本中的班次细节和标签时,我会做以下(简化)。

Data.Shift s = new Data.Shift();

/* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */    

foreach (var t in shift.Tags) {  //shift object is a cached object

                Data.Tag dt = new Data.Tag
                {
                    TagID = t.TagID,
                    Name = t.Name,
                    OrgID = t.OrgID,
                };
                s.Tags.Add(dt);
            }

即使我已经在新的Data.Tag对象中显式设置了TagID,但在SaveChanges()上会将新标记插入到DB中,而不是仅仅在DB中已存在的标记上创建关系。 TagID是PK标识列

当我尝试以下代码时:

foreach (var t in shift.Tags){
   s.Tags.Add(t)
}

它显然失败了,因为转移是从与当前请求的上下文不同的对象上下文缓存的。

有什么想法?据我所知:

  • 由于性能问题,清除缓存对我来说不是一个选项 - 我认识到如果我在同一个对象上下文中完成这项工作,整个问题就会消失。
  • 此外,由于需要关注,因此无法在当前上下文中从数据库中提取Data.Tag。

这似乎是我想要做的一件非常简单的事情......

修改 好的 - 我尝试用两种解决方案更新我的代码但是我遇到了两个问题。让我们试试第一个:

// ctx is grabbed up here from HttpContext.Current.Items

 Data.Shift s = new Data.Shift();

 /* copy over a bunch of stuff from the cached shift object. I'll spare the boring details */    

foreach (var t in shift.Tags) {  //shift object is a cached object

            Data.Tag dt = new Data.Tag
            {
                TagID = t.TagID,
                Name = t.Name,
                OrgID = t.OrgID,
            };
            ObjectStateEntry entry;
            ctx.ObjectStateManager.TryGetObjectStateEntry(t.EntityKey, out entry);
            if (entry == null || entry.State == EntityState.Detached) { 
                    ctx.Tags.Attach(t);
            }
            s.Tags.Add(dt);
        }

这会引发以下错误:

  An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

我猜我得到的原因是这个错误,是因为移位对象及其标签被拉出缓存(显然来自不同的上下文)。想法?

1 个答案:

答案 0 :(得分:1)

TAgId设置为现有值无关紧要。 EF是愚蠢的 - 它不会为你做任何Upsert / Merge。它不检查是否存在相同的实体,并且如果在数据库中自动生成TagId,它甚至会在您调用SaveChanges后丢弃您的值。

你要做什么?您必须手动告知EF Tag不是新实体。

尝试:

Data.Shift s = new Data.Shift();
context.Shifts.AddObject(s);  // Add a new shift

foreach (var t in shift.Tags) 
{  //shift object is a cached object
     Data.Tag dt = new Data.Tag
     {
         TagID = t.TagID,
         Name = t.Name,
         OrgID = t.OrgID,
     };
     context.Tags.Attach(Tag); // Attach an existing tag
     s.Tags.Add(dt);
 }
 context.SaveChanges();

Data.Shift s = new Data.Shift();
foreach (var t in shift.Tags) 
{  //shift object is a cached object
     Data.Tag dt = new Data.Tag
     {
         TagID = t.TagID,
         Name = t.Name,
         OrgID = t.OrgID,
     };
     s.Tags.Add(dt);
 }

 context.Shifts.AddObject(s);
 // Now shift and all tags are added
 // Change the state of each tag to unchanged
 foreach (var tag in s.Tags)
 {
     context.ObjectStateManager.ChangeEntityState(tag, EntityState.Unchanged);
 }
 context.SaveChanges();