2009-04-07 5 views
5

Je dois insérer des données périodiquement dans ma base de données SQL Server. Mais les flux où je lis les données répètent certaines données qui ont été insérées auparavant. Lorsque j'utilise Linq-to-SQL pour insérer dans la base de données, certaines données sont dupliquées ou une exception de violation de clé primaire est déclenchée, en fonction de la clé primaire.Comment insérer uniquement de nouveaux enregistrements en utilisant Linq-to-SQL?

Comment insérer les données sans duplications et sans exceptions? Je ne veux pas éviter l'exception avec un try-catch, car une fois l'exception levée, le reste des données n'est pas inséré.

mise à jour Je trouve aussi ma propre solution: j'ai écrit des entrées dupliquées procédure de suppression stockées, qui est exécuté juste après les InsertAllOnSubmit + SubmitChanges

Répondre

6

Tout ce que vous devez faire est de créer une nouvelle instance de votre classe et puis appelez InsertOnSumbit() sur la table:

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

l'autre chose que vous devez être sûr de comment vous incrémenter votre colonne ID. En général, je m'assure toujours d'utiliser le paramètre IDENTITY (1,1) sur mes colonnes d'ID. Ceci est déclaré sur votre colonne id de l'entité LINQ comme ceci:

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

Pour éviter les doublons, ce que vous avez vraiment besoin est ce que nous appelons dans ma boutique une fonctionnalité « ajouter ». À mon humble avis, cela est plus facile à réaliser avec une procédure stockée - nous avons même un modèle représenterons-nous:

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 

Il est possible de le faire dans LINQ si, juste une requête pour une liste d'identifiants existants (ou tout autre colonne vous hors de la saisie):

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

en attente de votre modification ... –

+0

La solution LINQ semble assez lente. Interroger une table géante et charger le contenu dans la mémoire me semble irréalisable. Peut-être que je devrais rester avec l'approche SP. –

+1

C'est ce que j'ai pensé ... Cependant, vous pouvez accélérer la solution LINQ en sélectionnant uniquement les propriétés dont vous avez vraiment besoin ... par exemple, juste la colonne ID. En outre, si vous pouvez réutiliser la requête "existante" pour plusieurs exécutions, cela vous aidera également. –

1

Malheureusement, il n'y a pas moyen de contourner comme LINQ to SQL ne vérifie pas la base de données avant d'effectuer l'insertion. La seule façon de procéder consiste à interroger la base de données d'abord pour déterminer si l'enregistrement en double existe et ensuite ajouter l'enregistrement si ce n'est pas le cas.

Idéalement Linq to SQL prend en charge la propriété Ignore Duplicate Keys sur une colonne SQL. Mais malheureusement, ce n'est pas le cas pour le moment.

Questions connexes