很多时候我发现自己写这样的代码:如何编写与LINQ to SQL(和高性能)兼容的FirstOrException()扩展方法?
Dim oRenewalOrder = (From c in dc.Orders _
Where c.CustomerID = iCustomerID _
And c.Type = "RENEWAL").FirstOrDefault()
If oRenewalOrder Is Nothing Then
Throw New Exception("Can't find renewal order for customer " & iCustomerID) ' or assert, if you prefer
End If
' Continue processing...
我宁愿使用First()方法来代替,因为它已经抛出异常时,有没有元素。但是这个异常告诉你什么导致了它,使它更难调试。所以我想写我可以使用这样的FirstOrException()扩展方法:
Dim oRenewalOrder = (From c in dc.Orders _
Where c.CustomerID = iCustomerID _
And c.Type = "RENEWAL").FirstOrException("Can't find renewal order for customer " & iCustomerID)
' Continue processing...
的问题是,它很难在一个通用的方式来写这样的方法,既LINQ到工作对象和利用目前LINQ to SQL的查询优化。我可以想出最好是这样的:
''' <summary>
''' Returns the first element of a sequence.
''' If the sequence is empty, an InvalidOperationException is thrown with the specified message.
<Extension()> _
Function FirstOrException(Of T)(ieThis As IEnumerable(Of T), sMessage As String) As T
Try
Return ieThis.First()
Catch ex As InvalidOperationException
Throw New InvalidOperationException(sMessage, ex)
End Try
End Function
我也写另一个等效扩展方法用于IEnumerable的(OF T),以覆盖LINQ到对象的情况下。这些工作很好,但似乎不好,必须赶上例外并重新抛出它。我尝试了不同的方法,通过采取(1)和AsEnumerable(),但是当我异形它,它运行两个单独的SELECT TOP 1语句:
<Extension()> _
Function FirstOrException(Of T)(iqThis As IQueryable(Of T), sMessage As String) As T
Dim aFirst = iqThis.Take(1).AsEnumerable()
If aFirst.Count() <= 0 Then
Throw New InvalidOperationException(sMessage)
Else
Return aFirst(0)
End If
End Function
所以我回到了异常处理方法。我不喜欢它,因为无论LINQ提供者是否属于这个集合,都有可能在出现不同的问题时抛出一个InvalidOperationException - 不是缺少结果,而是另一个问题。这会导致我的代码错误地认为没有结果,但实际上完全是另一个问题。
...
嗯,所以经常发生,当你键入了一个详细的问题,我想我找到了一个更好的解决方案,我将它张贴在下面的答案。但我会留下问题,以防万一有人发现更好的东西:-)
,因为我还没有收到超过devundef的,它们虽然快速,简单的引用类型的集合,将不会为值类型的集合的工作以外的任何建议,我会接受我自己的答案。 – 2012-08-14 17:29:52