很多時候我發現自己寫這樣的代碼:如何編寫與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