如何使用 Linq-to-SQL 插入仅包含新记录的数据?

5

我需要定期向我的SQL Server数据库中插入一些数据。但是,我读取数据的源重复了之前插入过的一些数据。当我使用Linq-to-SQL插入到数据库中时,会出现数据重复或主键冲突异常,这取决于主键。

如何在不重复且不出现异常的情况下插入数据?我不想用try-catch避免异常,因为一旦异常被引发,其余的数据就无法插入。

更新 我还找到了自己的解决方案:我编写了一个重复条目删除存储过程,在InsertAllOnSubmit + SubmitChanges之后运行。

2个回答

6

您只需创建一个新的类实例,然后在表上调用InsertOnSubmit()即可:

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

您需要确保的另一件事是如何增加ID列。通常,我总是确保在我的ID列上使用IDENTITY(1,1)设置。在LINQ实体的ID列上声明如下:

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

为了避免重复,你需要的是我们店里称之为"追加"功能。在我看来,这最容易实现的方法是使用存储过程——我们甚至有一个模板用于此: ```HTML

要避免重复项,您真正需要的是我们店里称之为“追加”功能。在我看来,这最容易通过使用存储过程来实现——我们甚至有一个模板用于此:

```
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,但是只需查询现有ID列表(或您的关键列)即可:

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

1
LINQ解决方案似乎相当缓慢。查询一个巨大的表并将内容加载到内存中对我来说似乎不可行。也许我应该坚持使用SP方法。 - Jader Dias
2
这正是我想到的...不过,你可以通过仅选择你真正需要的属性来加快LINQ解决方案的速度...例如,只选择ID列。此外,如果你可以重复使用“现有”的查询进行多次运行,那也会有所帮助。 - Daniel Schaffer

2

很遗憾,无法避免这个问题,因为Linq to SQL在执行插入之前不会检查数据库。唯一的方法是先查询数据库以确定是否存在重复记录,如果不存在,则添加该记录。

理想情况下,Linq to SQL应支持SQL列上的忽略重复键属性。但不幸的是目前它还不支持。


1
2018年怎么样?你现在有办法知道吗? - johnny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接