かなり頻繁に私は自分がこのようなコードを書いていることに気づきます:
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 to Objectsの両方を操作し、LINQtoSQLのクエリ最適化を利用するのが難しいことです。私が思いつくことができる最高のものはこれです:
''' <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
また、LINQ to Objectsの場合をカバーするために、IEnumerable(Of T)の別の同等の拡張メソッドを作成しました。これらはうまく機能しますが、例外をキャッチして再スローする必要があるのは悪いことのようです。Take(1)とAsEnumerable()を使用して別のアプローチを試しましたが、プロファイルを作成すると、2つの別々のSELECTTOP1ステートメントが実行されていました。
<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をスローする可能性があるため、私はそれが好きではありません。これにより、実際にはまったく別の問題であったにもかかわらず、コードが結果がないと誤って判断する可能性があります。
..。
さて、あなたが詳細な質問をタイプするときによくあることですが、私はより良い解決策を見つけたと思います、そして私はそれを以下の答えに投稿します。しかし、誰かがもっと良いものを見つけた場合に備えて、質問は開いたままにしておきます:-)