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