【问题标题】:SqlException when inserting multiple records table with unique index in linq-sql在 linq-sql 中插入具有唯一索引的多个记录表时出现 SqlException
【发布时间】:2012-08-06 18:31:42
【问题描述】:

我有一个带有 PK 列 ItemID 和列 Value 上的唯一索引的 sql 表。

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

异常信息:

无法在具有唯一索引“IDX_Item_Value_U_NC”的对象“dbo.Item”中插入重复的键行。
声明已终止。

如何避免这种情况?

编辑:我不能做单次插入,所以我不能在 for 循环内移动 db.SubmitChanges() 调用。

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

【问题讨论】:

    标签: c# linq-to-sql unique-index


    【解决方案1】:

    这里的问题是您同时插入它们。在您实际致电db.SubmitChanges()之前,它们都不存在

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

    ETA:另一种可能性是从以下位置更改您的循环:

    foreach (var value in values)
    

    foreach (var value in values.Distinct())
    

    【讨论】:

    • 埃里克,我明白这一点。我必须做多次插入。我不能做单次插入。
    • @hlpPy - 刚刚更新了我的答案。或者,您也许可以在您的 dbml 中将该属性标记为唯一(虽然不记得具体是如何做到的,但已经有一段时间了)
    • 我实际上在 2 列上有一个唯一索引(我简化了问题)。在这种情况下,dbml 技巧中的独特属性将不起作用。
    【解决方案2】:

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

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

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

    【讨论】:

    • 乔恩,这很简单 - 但在 3.5 中引入了 HashSet&lt;T&gt;
    【解决方案3】:

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

    我首先查看db.Items,然后检查DataContext 的ChangeSet (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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-09
      • 1970-01-01
      • 2011-12-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多