2009-04-07 21 views
5

Tengo que insertar algunos datos periódicamente en mi base de datos de SQL Server. Pero las fuentes donde leo los datos repiten algunos datos que se insertaron antes. Cuando uso Linq-to-SQL para insertar en el DB, algunos datos se duplican o se produce una excepción de violación de clave principal, dependiendo de la clave principal.¿Cómo insertar solo nuevos registros usando Linq-to-SQL?

¿Cómo insertar los datos sin duplicaciones y sin excepciones? No quiero evitar la excepción con un try-catch, porque una vez que se produce la excepción, el resto de los datos no se insertan.

actualización También encontré mi propia solución: Escribí un archivo de entradas duplicadas eliminación almacenados procedimiento, que se ejecuta justo después de las SubmitChanges InsertAllOnSubmit +

Respuesta

6

Todo lo que tiene que hacer es crear una nueva instancia de la clase y a continuación, llamar InsertOnSumbit() en la tabla:

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

la otra cosa que hay que estar seguro es de cómo se está incrementando la columna ID. En general, siempre me aseguro de usar la configuración de IDENTIDAD (1,1) en mis columnas de ID. Esto se declara en la columna de identificación de su entidad LINQ, así:

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

Para evitar duplicados, lo que realmente necesita es lo que llamamos en mi tienda de "añadir" funcionalidad. En mi humilde opinión, esto se logra más fácilmente con un procedimiento almacenado - incluso tenemos una plantilla que utilizamos para ello:

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 

Es posible hacerlo en LINQ sin embargo, sólo una consulta de una lista de identificadores existentes (o lo que sea la columna que está tecleando fuera de):

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

esperando su edición ... –

+0

La solución LINQ parece bastante lenta. Preguntarle a una mesa gigante que cargar los contenidos en la memoria parece inviable para mí. Tal vez debería seguir con el enfoque SP. –

+1

Eso es lo que pensé ... Sin embargo, podría acelerar la solución LINQ solo seleccionando las propiedades que realmente necesita ... por ejemplo, solo la columna ID. Además, si puede volver a utilizar la consulta "existente" para varias ejecuciones, también será de ayuda. –

1

Desafortunadamente, no hay manera de evitarlo como LINQ a SQL no comprueba la base de datos antes de que se realiza la inserción. La única forma de hacerlo es consultar la base de datos primero para determinar si existe el registro duplicado y luego agregar el registro si no lo hace.

Idealmente, Linq to SQL admitiría la propiedad Ignore Duplicate Keys en una columna SQL. Pero lamentablemente no lo hace en este momento.

Cuestiones relacionadas