Как вставить только новые записи, используя Linq-to-SQL? - PullRequest
5 голосов
/ 07 апреля 2009

Мне необходимо периодически вставлять некоторые данные в базу данных SQL Server. Но каналы, где я читаю данные, повторяют некоторые данные, которые были вставлены ранее. Когда я использую Linq-to-SQL для вставки в БД, либо дублируются некоторые данные, либо возникает исключение нарушения первичного ключа в зависимости от первичного ключа.

Как вставить данные без дублирования и без исключений? Я не хочу избегать исключения с помощью try-catch, потому что после возникновения исключения остальные данные не вставляются.

обновление Я также нашел свое собственное решение: я написал хранимую процедуру удаления дублированных записей, которая запускается сразу после InsertAllOnSubmit + SubmitChanges

Ответы [ 2 ]

6 голосов
/ 07 апреля 2009

Все, что вам нужно сделать, это создать новый экземпляр вашего класса, а затем вызвать InsertOnSumbit () для таблицы:

var foo = new MyFoo { Name = "foo1" };
var dc = new MyDataContext();
dc.Foos.InsertOnSubmit(foo);
dc.SubmitChanges();

Другая вещь, в которой вы должны быть уверены, это то, как вы увеличиваете столбец идентификатора. В общем, я всегда использую настройку IDENTITY (1,1) в моих столбцах ID. Это объявлено в столбце идентификатора вашей сущности LINQ следующим образом:

[Column(AutoSync = AutoSync.OnInsert, IsPrimaryKey = true, IsDbGenerated = true)]
public Int32 Id { get; set; }

Чтобы избежать дубликатов, вам действительно нужно то, что мы называем в моем магазине функцией «добавления». ИМХО, это проще всего сделать с помощью хранимой процедуры - у нас даже есть шаблон, который мы используем для нее:

USE [<Database_Name, sysobject, Database_Name>]
GO

CREATE PROCEDURE [<Schema, sysobject, dbo>].[<Table_Name, sysobject, Table_Name>__append]
(
    @id INT OUTPUT,
    @<Key_Param, sysobject, Key_Param> <Key_Param_Type, sysobject, VARCHAR(50)>
)
AS
BEGIN

        SELECT @id = [id] FROM [<Schema, sysobject, dbo>].[<Table_Name, sysobject, Table_Name>s] (NOLOCK) WHERE [<Key_Param, sysobject, Key_Param>] = @<Key_Param, sysobject, Key_Param>

IF @id IS NULL  
BEGIN       
    INSERT INTO [<Schema, sysobject, dbo>].[<Table_Name, sysobject, Table_Name>s] ([<Key_Param, sysobject, Key_Param>]) 
    OUTPUT INSERTED.[id] INTO @inserted_ids
    VALUES (@<Key_Param, sysobject, Key_Param>)

    SELECT TOP 1 @id = [id] FROM @inserted_ids;
END
ELSE
BEGIN
    UPDATE [<Schema, sysobject, dbo>].[<Table_Name, sysobject, Table_Name>s]
    SET
        [<Key_Param, sysobject, Key_Param>] = @<Key_Param, sysobject, Key_Param>
    WHERE [id] = @id
END
END
GO

Впрочем, это можно сделать в linq, просто запросив список существующих идентификаторов (или любого столбца, из которого вы вводите ключ):

var dc = new MyDataContext();
var existingFoos = dc.Foos.ToList();
var newFoos = new List<Foo>();
foreach(var bar in whateverYoureIterating) {
// logic to add to newFoos 
}
var foosToInsert = newFoos.Where(newFoo => !existingFoos.Any(existingFoo => newFoo.Id == existingFoo.Id));

dc.Foos.InsertAllOnSubmit(foosToInsert);
dc.SubmitChanges();
// use the next line if you plan on re-using existingFoos. If that's the case I'd wrap  dc.SubmitChanges() in a try-catch as well.
existingFoos.AddRange(foosToInsert);
2 голосов
/ 07 апреля 2009

К сожалению, обойти это невозможно, поскольку Linq to SQL не проверяет базу данных перед выполнением вставки. Единственный способ сделать это - запросить базу данных сначала , чтобы определить, существует ли дублирующаяся запись, а затем добавить запись, если ее нет.

В идеале Linq to SQL должен поддерживать свойство Ignore Duplicate Keys для столбца SQL. Но, к сожалению, пока нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...