2008-09-19 8 views
11

Je voudrais savoir s'il existe un moyen plus simple d'insérer un enregistrement s'il n'existe pas déjà dans une table. J'essaie toujours de développer mes compétences LINQ to SQL.LINQ to SQL insertion-si-inexistante

Voici ce que j'ai, mais il semble qu'il devrait y avoir un moyen plus facile.

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

Répondre

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'est-ce que e => e.BooleanProperty? Les entités n'ont pas de membre BooleanProperty. Je n'ai jamais vu ça avant ... – core

+0

Hmm, vous remarquerez que dans mon code, j'ai dit que table.Context.SubmitChanges() ne fonctionnait pas parce que c'est get-only. Apparemment j'ai fait une erreur. – core

+0

e => e.BooleanProperty est juste un exemple. Cela peut représenter n'importe quelle expression qui retourne une valeur booléenne. –

4

d'accord avec marxidad's answer, mais note 1.

Note 1: à mon humble avis, il est sage d'appeler db.SubmitChanges() dans une méthode d'aide, parce que vous pouvez casser la transaction de contexte. Cela signifie que si vous appelez le InsertIfNotExists<TEntity> au milieu d'une mise à jour complexe de plusieurs entités, vous n'enregistrez pas les modifications en même temps, mais en étapes.

Remarque 2: La méthode InsertIfNotExists<TEntity> est une méthode très générique qui fonctionne pour n'importe quel scénario. Si vous voulez distinguer simplement les entités qui ont chargé à partir de la base de données des entités qui ont été créées à partir du code, vous pouvez utiliser la méthode partielle OnLoaded de la classe entité comme ceci:

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

Étant donné que (et note 1), puis la fonctionnalité InsertIfNotExists est réduite à ce qui suit:

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

Une meilleure conception pourrait être que la méthode s'appelle InsertOnSubmitIfNotExists() et omette table.Context.SubmitChanges() –

4

Petite modification pour la réponse de Mark:

Si vous ne vous préoccupez vérifier si l'entité existe par sa clé primaire, la réponse de Marke peut être utilisé comme ceci:

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

     } 
    } 
11

Comme d'autres l'ont souligné, les if (!Any()) { InsertOnSubmit(); } solutions ont toutes une condition de course. Si vous suivez cette route, lorsque vous appelez SubmitChanges, vous devez prendre en compte que a) un SqlException pourrait être déclenché pour une insertion dupliquée, ou b) vous pourriez avoir des enregistrements en double dans la table.

Heureusement, nous pouvons utiliser la base de données pour éviter la condition de concurrence en renforçant l'unicité. Le code suivant suppose qu'il existe une clé primaire ou une contrainte unique sur la table pour empêcher l'insertion d'enregistrements en double.

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

maintenant ... les choses deviennent un peu plus compliqué juste si vous devez effectuer l'insertion dans une transaction par lots avec d'autres mises à jour de base de données.