DeleteOnSubmit LINQ异常“无法添加具有已在使用的密钥的实体”

时间:2009-03-25 18:33:54

标签: c# linq

编辑:如果有人想知道,动作处理程序会调用创建和处理相同类型的datacontext的代码,以防可能与此行为有关。代码没有触及MatchUpdateQueue表,但我想我应该提一下以防万一。

双重编辑:回答的每个人都是正确的!我给了受到大部分质疑的受访者答案。修复问题允许弹出另一个问题(隐藏在处理程序中),这恰好抛出完全相同的异常。哎呦!

我在删除LINQ中的项目时遇到了一些问题。下面的代码中的DeleteOnSubmit调用导致LINQ异常,并显示消息“无法添加具有已使用的密钥的实体”。我不确定我在这里做错了什么,它开始把我推到墙上。主键只是一个整数自动增量列,我没有其他问题,直到我尝试从数据库队列中删除一个项目。希望我在这里做一些痛苦的事情,这对于那些不是我的人来说很容易被发现!

static void Pacman()
{
    Queue<MatchUpdateQueue> waiting = new Queue<MatchUpdateQueue>();

    events.WriteEntry("matchqueue worker thread started");

    while (!stop)
    {
        if (waiting.Count == 0)
        {
            /* grab any new items available */
            aDataContext db = new aDataContext();
            List<MatchUpdateQueue> freshitems = db.MatchUpdateQueues.OrderBy(item => item.id).ToList();

            foreach (MatchUpdateQueue item in freshitems)
                waiting.Enqueue(item);
            db.Dispose();
        }
        else
        {
            /* grab & dispatch waiting item */
            MatchUpdateQueue item = waiting.Peek();
            try
            {
                int result = ActionHandler.Handle(item);
                if (result == -1)
                    events.WriteEntry("unknown command consumed : " + item.actiontype.ToString(), EventLogEntryType.Error);

                /* remove item from queue */
                waiting.Dequeue();

                /* remove item from database */
                aDataContext db = new aDataContext();
                db.MatchUpdateQueues.DeleteOnSubmit(db.MatchUpdateQueues.Single(i => i == item));
                db.SubmitChanges();
                db.Dispose();
            }
            catch (Exception ex)
            {
                events.WriteEntry("exception while handling item : " + ex.Message, EventLogEntryType.Error);
                stop = true;
            }
        }

        /* to avoid hammering database when there's nothing to do */
        if (waiting.Count == 0)
            Thread.Sleep(TimeSpan.FromSeconds(10));
    }

    events.WriteEntry("matchqueue worker thread halted");
}

5 个答案:

答案 0 :(得分:2)

你可以做些什么来实现

的效果
db.MatchUpdateQueues.DeleteOnSubmit(db.MatchUpdateQueues.Single(theItem => theItem == item));

只是一个注释,因为其他答案暗示附加..你将无法在除了实体已被序列化之前收到项目的原始上下文之外的上下文中使用附件。

答案 1 :(得分:0)

使用:

aDataContext db = new aDataContext();
item = new MatchUpdateQueue { id=item.id }; // <- updated
db.MatchUpdateQueues.Attach(item);
db.MatchUpdateQueues.DeleteOnSubmit(item);
db.SubmitChanges();

由于您使用的是新的datacontext,因此它不知道该对象已经在数据库中。

答案 2 :(得分:0)

尝试将while循环的整个内部包装在单个数据上下文的using语句中:

Queue<MatchUpdateQueue> waiting = new Queue<MatchUpdateQueue>();
events.WriteEntry("matchqueue worker thread started");
while (!stop)
{
    using (var db = new aDataContext())
    {
      if (waiting.Count == 0)
      {
        /* grab any new items available */
           List<MatchUpdateQueue> freshitems = db.MatchUpdateQueues
                                                 .OrderBy(item => item.id)
                                                 .ToList();
           foreach (MatchUpdateQueue item in freshitems)
                waiting.Enqueue(item);
      }
      ...
   }
}

答案 3 :(得分:0)

删除第一个db.Dispose()dispose。它可能是问题代码,因为实体保留对其数据上下文的引用,因此您不希望在仍在使用实例时处置它。这不会影响连接,因为它们仅在执行需要它们的操作时打开/关闭。

同样不要像这样处理第二个数据上下文,因为异常不会再调用那个dispose代码。使用using关键字,无论是否发生异常,都将确保调用dispose。同时从db中获取项目以删除它(以避免序列化/反序列化/附加)。

using (aDataContext db = new aDataContext())
{
   var dbItem = db.MatchUpdateQueues.Single(i => i.Id == item.Id);
   db.MatchUpdateQueues.DeleteOnSubmit(dbItem);
   db.SubmitChanges();
}

答案 4 :(得分:0)

如果您的TEntity(此处为Area)主键的类型为Identity列,请尝试此操作; 就是这样,你的SP或模型没有任何变化:

public void InitForm()
{
    'bnsEntity is a BindingSource and cachedAreas is a List<Area> created from dataContext.Areas.ToList()
    bnsEntity.DataSource = cachedAreas;
    'A nominal ID
    newID = cachedAreas.LastOrDefault().areaID + 1;
    'grdEntity is a GridView
    grdEntity.DataSource = bnsEntity;
}

private void tsbNew_Click(object sender, EventArgs e)
{
    var newArea = new Area();
    newArea.areaID = newID++;
    dataContext.GetTable<Area>().InsertOnSubmit(newArea);
    bnsEntity.Add(newArea);
    grdEntity.MoveToNewRecord();
}