在 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 DataContext
的ChangeSet
(DataContext.GetChangeSet().Deletes
、DataContext.GetChangeSet().Inserts
等)的方法,我已经发布了作为答案。
这里的问题是您同时插入它们。 在你真正打电话之前,它们都不存在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();
}
}