2009-04-24 5 views
3

Très souvent dans mon code LINQ to SQL, je dois « trouver ou de créer » une entité en tant que telle:Générique pour trouver ou créer un objet dans LINQ to SQL?

var invoiceDb = ctx.Invoices.FirstOrDefault(a => a.InvoicerId == InvoicerId && 
               a.Number == invoiceNumber); 
if (invoiceDb == null) 
{ 
    invoiceDb = new Invoice(); 
    invoiceDb.Number = invoiceNumber; 
    ctx.Invoices.InsertOnSubmit(invoiceDb); 
} 

Je cherche à en faire une méthode générique ... Toutes les bonnes idées?

Répondre

2

je suis venu avec ces méthodes d'extension qui semble bien fonctionner pour moi.

public static T FindOrCreate<T>(this Table<T> table, Func<T, bool> find, Action<T> create) where T : class, new() 
    { 
     T val = table.FirstOrDefault(find); 
     if (val == null) 
     { 
      val = new T(); 
      create(val); 
      table.InsertOnSubmit(val); 
     } 
     return val; 
    } 

    public static T FindOrCreate<T>(this Table<T> table, Func<T, bool> find) where T : class, new() 
    { 
     return FindOrCreate(table, find, a => { }); 
    } 

Et il est utilisé comme ceci:

var invoiceDb = ctx.Invoices.FindOrCreate(a => a.InvoicerId == InvoicerId && 
                a.Number == invoiceNumber); 
    invoiceDb.Number = invoiceNumber; 

Ou

var invoiceDb = ctx.Invoices.FindOrCreate(a => a.InvoicerId == InvoicerId && 
                a.Number == invoiceNumber, 
               a => a.Number = invoiceNumber); 
+0

Cela définira toujours la propriété "Number" sur l'entité, la marquant comme sale.Cela peut/va vous donner des mises à jour inutiles lors de l'exécution de SubmitChanges() –

0

Il pourrait être raccourci à.

if(invoiceDb == null) ctx.Invoices.InsertOnSubmit(invoiceDB = new Invoice() {Number = invoiceNumber}); 
+0

mais invoiceDb n'est pas affecté – TheSoftwareJedi

+0

Vraiment? Un downvote parce que je ne vous ai pas donné un exemple complet de code pasteable? Il est destiné à vous montrer comment le faire, ne pas le faire pour vous. –

+0

L'entité est maintenant affectée. –

0

Vous pouvez utiliser le Null-Coalescing Operator (??)

var invoice = ctx.Invoices.SingleOrDefault(a => a.InvoicerId == InvoicerId && 
              a.Number == invoiceNumber) ?? 
          new Invoice(); 
+1

Toutefois, cela n'ajoutera pas de nouvelle facture à la table. –

+0

Voir aussi: http://en.wikipedia.org/wiki/%3F%3F_Operator (j'ai écrit la première version de cet article, donc je l'associe chaque fois que je peux) –

+0

Encore besoin de la logique pour déterminer si un insert est nécessaire – TheSoftwareJedi

1

Que diriez-vous à l'aide d'une méthode d'extension comme ceci:

public static T FirstOrCreate<T>(this IEnumerable<T> source) where T : class, new() 
{ 
    var result = source.FirstOrDefault(); 
    return result != null ? result : new T(); 
} 

Si vous voulez qu'il soit capable d'accepter un prédicat, vous pouvez utiliser ce d éfinition:

public static T FirstOrCreate<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate) where T : class, new() 
{ 
    var result = source.FirstOrDefault(predicate); 
    return result != null ? result : new T(); 
} 

De cette façon, vous pouvez l'utiliser à la place de FirstOrDefault() comme ceci:

Invoice selectedInvoice = (from i in Invoices 
          where i.ID == invoiceID 
          select i).FirstOrCreate(); 

..ou avec l'utilisation d'un prédicats:

Invoice selectedInvoice = db.Invoices.FirstOrCreate(i => i.ID == invoiceID); 

Est-ce que ce soit renvoie une entité correspondante ou une nouvelle entité (non nulle) à la place. Edit: J'ai pensé à cela aujourd'hui, et je me rends compte que ce qui précède vous obligera à détecter que l'entité est nouvelle (non existante) et l'attacher au DataContext, donc je suis venu avec cette compromis, en utilisant la même approche:

public static T FirstOrCreate<T>(this IEnumerable<T> source, DataClassesDataContext db) where T : class, new() 
{ 
    var result = source.FirstOrDefault(); 
    if (result == null) 
    { 
     result = new T(); 
     db.GetTable<T>().InsertOnSubmit(result); 
    } 
    return result; 
} 

l'inconvénient est que vous devez passer le DataContext en tant que paramètre, mais il devrait fonctionner assez bien:

Customer selectedCustomer = (from c in db.Customers 
          where c.CustomerId == selectedCustomerId 
          select c).FirstOrCreate(db); 

Sûrement un upvote est là-bas? :)

2

version VB.NET:

Module dbi 
    <System.Runtime.CompilerServices.Extension()> _ 
    Public Function FindOrCreate(_ 
     Of T As {Class, New})(ByVal table As Data.Linq.Table(Of T), _ 
     ByVal find As Func(Of T, Boolean), _ 
     ByVal create As Action(Of T)) _ 
     As T 

     Dim val As T = table.FirstOrDefault(find) 
     If val Is Nothing Then 
      val = New T() 
      create(val) 
      table.InsertOnSubmit(val) 
     End If 
     Return val 
    End Function 

    <System.Runtime.CompilerServices.Extension()> _ 
    Public Function FindOrCreate(_ 
     Of T As {Class, New})(ByVal table As Data.Linq.Table(Of T), _ 
     ByVal find As Func(Of T, Boolean)) _ 
     As T 

     Return FindOrCreate(table, find, Function(a)) 
    End Function 

End Module