Question

I have a sql table with a PK column ItemID and unique index on column 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
)

I'm inserting multiple records (batch insert/update) in the table using linq-sql. I do an "exists" check before inserting the record.

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

I get the below exception due to the unique index if the list has the same values. Item table has no records.

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

Exception message:

Cannot insert duplicate key row in object 'dbo.Item' with unique index 'IDX_Item_Value_U_NC'.
The statement has been terminated.

How do I avoid this?

EDIT: I cannot do single inserts so I cannot move the db.SubmitChanges() call inside the for loop.

EDIT2: The answers posted so far deal with fixing the data rather than a way within linq-sql. I have put a simple example with a list of ints. But in reality I have a list of object graph to inserts and every item hits multiple tables for insert/update. I found a way using linq DataContext's ChangeSet (DataContext.GetChangeSet().Deletes, DataContext.GetChangeSet().Inserts, etc.) which I have posted as answer.

Was it helpful?

Solution

The problem here is that you are inserting them both at the same time. Neither of them exist until you actually call db.SubmitChanges()

Try moving the db.SubmitChanges() inside the loop and that should solve the problem.

ETA: Another possibility would be changing your loop from:

foreach (var value in values)

to

foreach (var value in values.Distinct())

OTHER TIPS

I'm not sure if this was just a typo in your question, but the Where clause in your LINQ statement is .Where(x => x.Value = value). I think you want the comparison operator here, not assignment. Try changing your LINQ statement to the following:

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

In addition, if you're using .NET 3.5 or later, you could replace your List<T> with HashSet<T> to guarantee that you only have unique values.

Rather than fixing the data, I found a way to work within linq-sql using the DataContext's ChangeSet. The data in the example is simple but it could be an object graph which hits multiple tables and would be difficult to fix.

I first look in the db.Items and then check the DataContext's ChangeSet (db.GetChangeSet().Inserts) to find the item and insert if it not found.

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();
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top