I did something similar, but more complex (because it also manipulates expressions when it processes them). To do this, I created a shell class that implemented IQueryable and contained a link to what I really wanted to request. I passed all the interface elements through a reference object, with the exception of the Provider property, which returned a link to another class I created, inherited from IQueryProvider. IQueryProvider has methods that are called each time a query is built or executed. Thus, you can do something similar if you do not mind that you are always forced to request your wrapper object instead of the original object (s).
You should also know that if you use LINQ-to-SQL, there is a Log property in the DataContext that you can use to route a lot of debugging information wherever you want.
Code example:
Create your own IQueryable to control the returned QueryProvider.
Public Class MyQueryable(Of TableType) Implements IQueryable(Of TableType) Private innerQueryable As IQueryable(Of TableType) Private myProvider As MyQueryProvider = Nothing Public Sub New(ByVal innerQueryable As IQueryable(Of TableType)) Me.innerQueryable = innerQueryable End Sub Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of TableType) Implements System.Collections.Generic.IEnumerable(Of TableType).GetEnumerator Return innerQueryable.GetEnumerator() End Function Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator Return innerQueryable.GetEnumerator() End Function Public ReadOnly Property ElementType() As System.Type Implements System.Linq.IQueryable.ElementType Get Return innerQueryable.ElementType End Get End Property Public ReadOnly Property Expression() As System.Linq.Expressions.Expression Implements System.Linq.IQueryable.Expression Get Return innerQueryable.Expression End Get End Property Public ReadOnly Property Provider() As System.Linq.IQueryProvider Implements System.Linq.IQueryable.Provider Get If myProvider Is Nothing Then myProvider = New MyQueryProvider(innerQueryable.Provider) Return myProvider End Get End Property Friend ReadOnly Property innerTable() As System.Data.Linq.ITable Get If TypeOf innerQueryable Is System.Data.Linq.ITable Then Return DirectCast(innerQueryable, System.Data.Linq.ITable) End If Throw New InvalidOperationException("Attempted to treat a MyQueryable as a table that is not a table") End Get End Property End Class
Create your own query provider to manage the generated expression generator.
Public Class MyQueryProvider Implements IQueryProvider Private innerProvider As IQueryProvider Public Sub New(ByVal innerProvider As IQueryProvider) Me.innerProvider = innerProvider End Sub Public Function CreateQuery(ByVal expression As System.Linq.Expressions.Expression) As System.Linq.IQueryable Implements System.Linq.IQueryProvider.CreateQuery Return innerProvider.CreateQuery(expression) End Function Public Function CreateQuery1(Of TElement)(ByVal expression As System.Linq.Expressions.Expression) As System.Linq.IQueryable(Of TElement) Implements System.Linq.IQueryProvider.CreateQuery Dim newQuery = innerProvider.CreateQuery(Of TElement)(ConvertExpression(expression)) If TypeOf newQuery Is IOrderedQueryable(Of TElement) Then Return New MyOrderedQueryable(Of TElement)(DirectCast(newQuery, IOrderedQueryable(Of TElement))) Else Return New MyQueryable(Of TElement)(newQuery) End If End Function Public Shared Function ConvertExpression(ByVal expression As Expression) As Expression If TypeOf expression Is MethodCallExpression Then Dim mexp = DirectCast(expression, MethodCallExpression) Return Expressions.MethodCallExpression.Call(ConvertExpression(mexp.Object), _ mexp.Method, (From row In mexp.Arguments Select ConvertExpression(row)).ToArray()) ElseIf TypeOf expression Is BinaryExpression Then Dim bexp As BinaryExpression = DirectCast(expression, BinaryExpression) Dim memberInfo As NestedMember = Nothing Dim constExp As Expression = Nothing Dim memberOnLeft As Boolean Dim doConvert = True '' [etc... lots of code to generate a manipulated expression tree ElseIf TypeOf expression Is LambdaExpression Then Dim lexp = DirectCast(expression, LambdaExpression) Return LambdaExpression.Lambda( _ ConvertExpression(lexp.Body), (From row In lexp.Parameters Select _ DirectCast(ConvertExpression(row), ParameterExpression)).ToArray()) ElseIf TypeOf expression Is ConditionalExpression Then Dim cexp = DirectCast(expression, ConditionalExpression) Return ConditionalExpression.Condition(ConvertExpression(cexp.Test), _ ConvertExpression(cexp.IfTrue), _ ConvertExpression(cexp.IfFalse)) ElseIf TypeOf expression Is InvocationExpression Then Dim iexp = DirectCast(expression, InvocationExpression) Return InvocationExpression.Invoke( _ ConvertExpression(iexp.Expression), (From row In iexp.Arguments _ Select ConvertExpression(row)).ToArray()) ElseIf TypeOf expression Is MemberExpression Then '' [etc... lots of code to generate a manipulated expression tree ElseIf TypeOf expression Is UnaryExpression Then '' [etc... lots of code to generate a manipulated expression tree Else Return expression End If End Function Public Function Execute(ByVal expression As System.Linq.Expressions.Expression) As Object Implements System.Linq.IQueryProvider.Execute Return innerProvider.Execute(expression) End Function Public Function Execute1(Of TResult)(ByVal expression As System.Linq.Expressions.Expression) As TResult Implements System.Linq.IQueryProvider.Execute Return innerProvider.Execute(Of TResult)(ConvertExpression(expression)) End Function End Class
Then extend the resulting derived DataContext by providing wrapped queries:
Partial Public Class MyDataContext Private myQueries As Dictionary(Of System.Type, Object) = New Dictionary(Of System.Type, Object) Public ReadOnly Property My_AccountCategories() As MyQueryable(Of AccountCategory) Get Dim result As Object = Nothing If (Me.myQueries.TryGetValue(GetType(AccountCategory), result) = false) Then result = New MyQueryable(Of AccountCategory)(Me.AccountCategories) Me.myQueries(GetType(AccountCategory)) = result End If Return CType(result,MyQueryable(Of AccountCategory)) End Get End Property Public ReadOnly Property My_AccountSegmentations() As MyQueryable(Of AccountSegmentation) Get Dim result As Object = Nothing If (Me.myQueries.TryGetValue(GetType(AccountSegmentation), result) = false) Then result = New MyQueryable(Of AccountSegmentation)(Me.AccountSegmentations) Me.myQueries(GetType(AccountSegmentation)) = result End If Return CType(result,MyQueryable(Of AccountSegmentation)) End Get End Property End Class