在 linq-sql 中插入具有唯一索引的多个记录表时的 SqlException

本文关键字:记录表 SqlException 索引 唯一 linq-sql 插入 | 更新日期: 2024-11-06 13:40:31

我有一个 sql 表,其中包含 PK 列ItemID和列 Value 上的唯一索引。

CREATE TABLE [dbo].[Item](
    [ItemID] [int] IDENTITY(1,1) NOT NULL,
    [Value] [int] NULL
)
CREATE UNIQUE NONCLUSTERED INDEX [IDX_Item_Value_U_NC] ON [dbo].[Item] 
(
    [Value] ASC
)

我正在使用linq-sql在表中插入多个记录(批量插入/更新)。在插入记录之前,我进行了"存在"检查。

void MultipleInserts(List<int> values)
{
    using (var db = new DBDataContext())
    {
        foreach (var value in values)
        {
            var dbItem = db.Items
                    .Where(x => x.Value == value)
                    .SingleOrDefault();
            if (dbItem == null)
            {
                var Item = new Item()
                {
                    Value = value
                };
                db.Items.InsertOnSubmit(Item);
            }
        }
        db.SubmitChanges();
    }
}

如果列表具有相同的值,由于唯一索引,我得到以下异常。 Item表没有记录。

var values = new List<int>();
values.Add(1);
values.Add(1);
MultipleInserts(values);

异常消息:

无法在对象'dbo'中插入重复的键行。具有唯一索引"IDX_Item_Value_U_NC"的项目"。
该语句已终止。

如何避免这种情况?

编辑:我不能进行单次插入,因此无法在for循环内移动db.SubmitChanges()调用。

EDIT2:到目前为止发布的答案涉及修复数据,而不是linq-sql中的一种方法。我放了一个简单的例子,其中包含一个整数列表。但实际上,我有一个要插入的对象图列表,每个项目都命中多个表进行插入/更新。我找到了一种使用 linq DataContextChangeSetDataContext.GetChangeSet().DeletesDataContext.GetChangeSet().Inserts 等)的方法,我已经发布了作为答案。

在 linq-sql 中插入具有唯一索引的多个记录表时的 SqlException

这里的问题是您同时插入它们。 在你真正打电话之前,它们都不存在db.SubmitChanges()

尝试在循环内移动db.SubmitChanges(),这应该可以解决问题。

ETA:另一种可能性是将循环从:

foreach (var value in values)

foreach (var value in values.Distinct())

我不确定这是否只是您问题中的一个错字,但是您的 LINQ 语句中的 Where 子句.Where(x => x.Value = value) .我认为您想要这里的比较运算符,而不是赋值。 尝试将 LINQ 语句更改为以下内容:

var dbItem = db.Items 
    .Where(x => x.Value == value) 
    .SingleOrDefault();

此外,如果使用的是 .NET 3.5 或更高版本,则可以将List<T>替换为 HashSet<T>,以保证只有唯一值。

我没有修复数据,而是找到了一种使用 DataContext 的 ChangeSet 在 linq-sql 中工作的方法。示例中的数据很简单,但它可能是命中多个表的对象图,并且难以修复。

我首先查看db.Items,然后检查 DataContext 的变更集 ( db.GetChangeSet().Inserts ) 以找到该项,如果未找到,则插入。

void MultipleInserts(List<int> values)
{
    using (var db = new DBDataContext())
    {
        foreach (var value in values)
        {
            // look in db first
            var dbItem = db.Items
                    .Where(x => x.Value == value)
                    .SingleOrDefault();
            if (dbItem == null)
            {
                // look in ChangeSet second
                var cachedItem = db.GetChangeSet().Inserts
                    .OfType<Item>()
                    .Where(x => x.Value == value)
                    .SingleOrDefault();
                if (cachedItem == null)
                {
                    var Item = new Item()
                    {
                        Value = value
                    };
                    db.Items.InsertOnSubmit(Item);
                }
            }
        }
        db.SubmitChanges();
    }
}