Quite often in my LINQ to SQL code, I need to "find or create" an entity as such:
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);
}
I'm looking to make this a generic method... Any good ideas?
VB.NET version:
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
I came up with these extension methods that seems to work well for me.
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 => { });
}
And it's used like so:
var invoiceDb = ctx.Invoices.FindOrCreate(a => a.InvoicerId == InvoicerId &&
a.Number == invoiceNumber);
invoiceDb.Number = invoiceNumber;
Or
var invoiceDb = ctx.Invoices.FindOrCreate(a => a.InvoicerId == InvoicerId &&
a.Number == invoiceNumber,
a => a.Number = invoiceNumber);
How about using an extension method like so:
public static T FirstOrCreate<T>(this IEnumerable<T> source) where T : class, new()
{
var result = source.FirstOrDefault();
return result != null ? result : new T();
}
If you want it to be able to accept a predicate, you can use this definition:
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();
}
That way you can use it in place of FirstOrDefault() like so:
Invoice selectedInvoice = (from i in Invoices
where i.ID == invoiceID
select i).FirstOrCreate();
..or with the use of a Predicate:
Invoice selectedInvoice = db.Invoices.FirstOrCreate(i => i.ID == invoiceID);
Will either return a matching entity or a new (non-null) entity instead.
Edit: I've been thinking about this today, and I occurs to me that the above will require you to detect that the entity is new (not existing) and attach it to the DataContext, so I came up with this compromise, using the same approach:
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;
}
The drawback is you have to pass the DataContext in as a parameter, but it should work nicely enough:
Customer selectedCustomer = (from c in db.Customers
where c.CustomerId == selectedCustomerId
select c).FirstOrCreate(db);
Surely one upvote is out there? :)
它可以缩短为。
if(invoiceDb == null) ctx.Invoices.InsertOnSubmit(invoiceDB = new Invoice() {Number = invoiceNumber});
You could use the Null-Coalescing Operator (??)
var invoice = ctx.Invoices.SingleOrDefault(a => a.InvoicerId == InvoicerId &&
a.Number == invoiceNumber) ??
new Invoice();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.