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

3

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

我想把这个方法变成通用的... 有什么好的想法吗?

5个回答

2
我想到了一些扩展方法,对于我来说似乎效果很好。
    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 => { });
    }

它的使用方式如下:

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

这将始终设置实体上的“Number”属性,将其标记为脏数据。当执行SubmitChanges()时,这可能会导致不必要的更新。 - Adam Robinson

2

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

1

像这样使用扩展方法怎么样:

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

如果你希望它能接受一个谓词,你可以使用这个定义:

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

这样你就可以将它用作FirstOrDefault()的替代了:

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

或者使用 Predicate:

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

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

编辑:我今天一直在思考这个问题,我发现上述方法需要您检测实体是否是新的(不存在的),并将其附加到 DataContext 上,因此我想出了这个妥协方案,使用相同的方法:

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

缺点是您必须将DataContext作为参数传递,但它应该足够好用:
Customer selectedCustomer = (from c in db.Customers
                             where c.CustomerId == selectedCustomerId
                             select c).FirstOrCreate(db);

肯定有人会点个赞吧?:)


0

可以缩短为。

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

但是invoiceDb没有被分配 - TheSoftwareJedi
真的吗?因为我没有给你完整的可粘贴代码示例,所以你就给了我一个反对票?它旨在向您展示如何去做,而不是替你完成。 - Adam Robinson

0
你可以使用空值合并运算符(??)
var invoice = ctx.Invoices.SingleOrDefault(a => a.InvoicerId == InvoicerId &&
                                             a.Number == invoiceNumber) ??
                          new Invoice();

1
这不会向表中添加新的发票。 - Adam Robinson
参见:http://en.wikipedia.org/wiki/%3F%3F_Operator(我撰写了该文章的第一个版本,所以尽可能地提供链接)。 - Matt Grande
仍需要逻辑来确定是否需要插入。 - TheSoftwareJedi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接