2008-09-19 8 views
11

Me gustaría saber si hay una manera más fácil de insertar un registro si aún no existe en una tabla. Todavía estoy tratando de desarrollar mis habilidades de LINQ to SQL.LINQ to SQL insert-if-inexistente

Esto es lo que tengo, pero parece que debería haber una manera más fácil.

public static TEntity InsertIfNotExists<TEntity> 
(
    DataContext db, 
    Table<TEntity> table, 
    Func<TEntity,bool> where, 
    TEntity record 
) 
    where TEntity : class 
{ 
    TEntity existing = table.SingleOrDefault<TEntity>(where); 

    if (existing != null) 
    { 
     return existing; 
    } 
    else 
    { 
     table.InsertOnSubmit(record); 

     // Can't use table.Context.SubmitChanges() 
     // 'cause it's read-only 

     db.SubmitChanges(); 
    } 

    return record; 
} 

Respuesta

14
public static void InsertIfNotExists<TEntity> 
        (this Table<TEntity> table, 
        TEntity entity, 
        Expression<Func<TEntity,bool>> predicate) 
    where TEntity : class 
{ 
    if (!table.Any(predicate)) 
    { 
     table.InsertOnSubmit(record); 
     table.Context.SubmitChanges(); 
    } 
} 


table.InsertIfNotExists(entity, e=>e.BooleanProperty); 
+0

¿Qué es e => e.BooleanProperty? Las entidades no tienen un miembro BooleanProperty. Nunca he visto esto antes ... – core

+0

Hmm, notarás que en mi código, dije que table.Context.SubmitChanges() no funcionó porque es solo de get. Aparentemente cometí un error. – core

+0

e => e.BooleanProperty es solo un ejemplo. Puede representar cualquier expresión que devuelva un valor booleano. –

4

De acuerdo con marxidad's answer, pero ver nota 1

Nota 1: En mi humilde opinión, no es prudente llamar db.SubmitChanges() en un método de ayuda, ya que puede romper la transacción contexto. Esto significa que si llama al InsertIfNotExists<TEntity> en medio de una actualización compleja de varias entidades, está guardando los cambios no de una vez sino en pasos.

Nota 2: El método InsertIfNotExists<TEntity> es un método muy genérico que funciona para cualquier situación. Si sólo quiere discriminar a las entidades que han cargado desde la base de datos de las entidades que se han creado a partir del código, puede utilizar el método parcial OnLoaded de la clase de entidad de esta manera:

public partial class MyEntity 
{ 
    public bool IsLoaded { get; private set; } 
    partial void OnLoaded() 
    { 
     IsLoaded = true; 
    } 
} 

Teniendo en cuenta que (y nota 1), entonces la funcionalidad InsertIfNotExists se reduce a lo siguiente:

if (!record.IsLoaded) 
    db.InsertOnSubmit(record); 
+2

Un mejor diseño podría ser para que el método se llame InsertOnSubmitIfNotExists() y para omitir table.Context.SubmitChanges() –

4

pequeña modificación de la respuesta de la marca:

Si sólo se preocupa por comprobar si la entidad existe por su clave primaria, la respuesta de Marke puede ser utilizado como esto:

public static void InsertIfNotExists<TEntity> 
        (this Table<TEntity> table 
        , TEntity entity 
        ) where TEntity : class 
    { 
     if (!table.Contains(entity)) 
     { 
      table.InsertOnSubmit(entity); 

     } 
    } 
11

Como otros han señalado, las soluciones if (!Any()) { InsertOnSubmit(); } todos tienen una condición de carrera. Si sigue esa ruta, cuando llame al SubmitChanges, debe tener en cuenta que a) se podría generar un SqlException para un inserto duplicado, ob) podría tener registros duplicados en la tabla.

Afortunadamente, podemos utilizar la base de datos para evitar la condición de carrera mediante la aplicación de la singularidad. El siguiente código supone que hay una clave principal o restricción única en la tabla para evitar la inserción de registros duplicados.

using (var db = new DataContext()) { 

    // Add the new (possibly duplicate) record to the data context here. 

    try { 
     db.SubmitChanges(); 
    } catch (SqlException ex) { 
     const int violationOfPrimaryKeyContraint = 2627; 
     const int violationOfUniqueConstraint = 2601; 
     var duplicateRecordExceptionNumbers = new [] { 
      violationOfPrimaryKeyContraint, violationOfUniqueConstraint 
     }; 
     if (!duplicateRecordExceptionNumbers.Contains(ex.Number)) { 
      throw; 
     } 
    } 
} 

Ahora ... las cosas se ponen un poco justo más complicado si se tiene que realizar la inserción en una operación por lotes con otras actualizaciones de la base.