在LINQ to SQL中查找或创建对象的通用方法?

在我的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); } 

我想把它变成一个通用的方法……有什么好主意吗?

我想出了这些扩展方法似乎对我有用。

  public static T FindOrCreate(this Table table, Func find, Action 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(this Table table, Func find) where T : class, new() { return FindOrCreate(table, find, a => { }); } 

它的使用方式如下:

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

要么

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

VB.NET版本:

 Module dbi  _ 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  _ 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 

如何使用这样的扩展方法:

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

如果您希望它能够接受谓词,您可以使用以下定义:

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

这样你就可以用它代替FirstOrDefault(),如下所示:

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

..或使用谓词:

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

将返回匹配的实体或新的(非null)实体。

编辑:我今天一直在考虑这个问题,我发现以上内容将要求您检测到该实体是新的(不存在)并将其附加到DataContext,所以我想出了这个妥协,使用同样的方法:

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

缺点是您必须将DataContext作为参数传递,但它应该可以很好地工作:

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

肯定有一个upvote在那里? 🙂

你可以使用Null-Coalescing运算符(??)

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

它可以缩短为。

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